diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/junipernetworks/junos/plugins | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/junipernetworks/junos/plugins')
227 files changed, 55871 insertions, 0 deletions
diff --git a/ansible_collections/junipernetworks/junos/plugins/action/__init__.py b/ansible_collections/junipernetworks/junos/plugins/action/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/action/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/action/junos.py b/ansible_collections/junipernetworks/junos/plugins/action/junos.py new file mode 100644 index 000000000..7c0389a8b --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/action/junos.py @@ -0,0 +1,58 @@ +# +# (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 + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + +CLI_SUPPORTED_MODULES = ["junos_netconf", "junos_ping", "junos_command"] + + +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 ["junos_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection not in ("netconf", "network_cli"): + return { + "failed": True, + "msg": "Connection type '%s' is not valid for '%s' module. " + "Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html" + % (self._play_context.connection, module_name), + } + + 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/ansible_collections/junipernetworks/junos/plugins/cliconf/__init__.py b/ansible_collections/junipernetworks/junos/plugins/cliconf/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/cliconf/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/cliconf/junos.py b/ansible_collections/junipernetworks/junos/plugins/cliconf/junos.py new file mode 100644 index 000000000..72ffec99e --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/cliconf/junos.py @@ -0,0 +1,346 @@ +# +# (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 (@ansible-network) +name: junos +short_description: Use junos cliconf to run command on Juniper Junos OS platform +description: +- This junos plugin provides low level abstraction apis for sending and receiving + CLI commands from Juniper Junos OS network devices. +version_added: 1.0.0 +options: + config_commands: + description: + - Specifies a list of commands that can make configuration changes + to the target device. + - When `ansible_network_single_user_mode` is enabled, if a command sent + to the device is present in this list, the existing cache is invalidated. + version_added: 2.0.0 + type: list + elements: str + default: [] + vars: + - name: ansible_junos_config_commands +""" + +import json +import re + +from functools import wraps +from itertools import chain + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_text +from ansible.module_utils.common._collections_compat import Mapping +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list +from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import CliconfBase + + +def configure(func): + @wraps(func) + def wrapped(self, *args, **kwargs): + prompt = self._connection.get_prompt() + if not to_text(prompt, errors="surrogate_or_strict").strip().endswith("#"): + self.send_command("configure") + return func(self, *args, **kwargs) + + return wrapped + + +class Cliconf(CliconfBase): + def __init__(self, *args, **kwargs): + self._device_info = {} + super(Cliconf, self).__init__(*args, **kwargs) + + def get_text(self, ele, tag): + try: + return to_text( + ele.find(tag).text, + errors="surrogate_then_replace", + ).strip() + except AttributeError: + pass + + def get_device_info(self): + if not self._device_info: + device_info = {} + device_info["network_os"] = "junos" + + reply = self.get(command="show version") + data = to_text(reply, errors="surrogate_or_strict").strip() + + match = re.search(r"Junos: (\S+)", data) + if match: + device_info["network_os_version"] = match.group(1) + + match = re.search(r"Model: (\S+)", data, re.M) + if match: + device_info["network_os_model"] = match.group(1) + + match = re.search(r"Hostname: (\S+)", data, re.M) + if match: + device_info["network_os_hostname"] = match.group(1) + + self._device_info = device_info + + return self._device_info + + def get_config(self, source="running", format="text", flags=None): + if source != "running": + raise ValueError( + "fetching configuration from %s is not supported" % source, + ) + + options_values = self.get_option_values() + if format not in options_values["format"]: + raise ValueError( + "'format' value %s is invalid. Valid values are %s" + % (format, ",".join(options_values["format"])), + ) + + if format == "text": + cmd = "show configuration" + else: + cmd = "show configuration | display %s" % format + + cmd += " ".join(to_list(flags)) + cmd = cmd.strip() + return self.send_command(cmd) + + @configure + def edit_config( + self, + candidate=None, + commit=True, + replace=None, + comment=None, + ): + + operations = self.get_device_operations() + self.check_edit_config_capability( + operations, + candidate, + commit, + replace, + comment, + ) + + resp = {} + results = [] + requests = [] + + if replace: + candidate = "load override {0}".format(replace) + + for line in to_list(candidate): + if not isinstance(line, Mapping): + line = {"command": line} + cmd = line["command"] + try: + results.append(self.send_command(**line)) + except AnsibleConnectionFailure as exc: + if "error: commit failed" in exc.message: + self.discard_changes() + raise + requests.append(cmd) + + diff = self.compare_configuration() + if diff: + resp["diff"] = diff + + if commit: + self.commit(comment=comment) + else: + self.discard_changes() + + else: + self.send_command("top") + self.discard_changes() + + resp["request"] = requests + resp["response"] = results + return resp + + def get( + self, + command, + prompt=None, + answer=None, + sendonly=False, + output=None, + newline=True, + check_all=False, + ): + if output: + command = self._get_command_with_output(command, output) + return self.send_command( + command=command, + prompt=prompt, + answer=answer, + sendonly=sendonly, + newline=newline, + check_all=check_all, + ) + + @configure + def commit( + self, + comment=None, + confirmed=False, + at_time=None, + synchronize=False, + ): + """ + Execute commit command on remote device. + :param comment: Comment to be associated with commit + :param confirmed: Boolean flag to indicate if the previous commit should confirmed + :param at_time: Time at which to activate configuration changes + :param synchronize: Boolean flag to indicate if commit should synchronize on remote peers + :return: Command response received from device + """ + command = "commit" + if comment: + command += " comment {0}".format(comment) + if confirmed: + command += " confirmed" + if at_time: + command += " {0}".format(at_time) + if synchronize: + command += " peers-synchronize" + + command += " and-quit" + + try: + response = self.send_command(command) + except AnsibleConnectionFailure: + self.discard_changes() + raise + + return response + + @configure + def discard_changes(self): + command = "rollback 0" + for cmd in chain(to_list(command), ["exit"]): + self.send_command(cmd) + + @configure + def validate(self): + return self.send_command("commit check") + + @configure + def compare_configuration(self, rollback_id=None): + command = "show | compare" + if rollback_id is not None: + command += " rollback %s" % int(rollback_id) + resp = self.send_command(command) + + r = resp.splitlines() + if len(r) == 1 and "[edit]" in r[0] or len(r) == 4 and r[1].startswith("- version"): + resp = "" + + return resp + + @configure + def rollback(self, rollback_id, commit=True): + resp = {} + self.send_command("rollback %s" % int(rollback_id)) + resp["diff"] = self.compare_configuration() + if commit: + self.commit() + else: + self.discard_changes() + return resp + + def get_diff(self, rollback_id=None): + diff = {"config_diff": None} + response = self.compare_configuration(rollback_id=rollback_id) + if response: + diff["config_diff"] = response + return diff + + def get_device_operations(self): + return { + "supports_diff_replace": False, + "supports_commit": True, + "supports_rollback": True, + "supports_defaults": False, + "supports_onbox_diff": True, + "supports_commit_comment": True, + "supports_multiline_delimiter": False, + "supports_diff_match": False, + "supports_diff_ignore_lines": False, + "supports_generate_diff": False, + "supports_replace": True, + } + + def get_option_values(self): + return { + "format": ["text", "set", "xml", "json"], + "diff_match": [], + "diff_replace": [], + "output": ["text", "set", "xml", "json"], + } + + def get_capabilities(self): + result = super(Cliconf, self).get_capabilities() + result["rpc"] += [ + "commit", + "discard_changes", + "run_commands", + "compare_configuration", + "validate", + "get_diff", + ] + 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="#") + + def _get_command_with_output(self, command, output): + options_values = self.get_option_values() + if output not in options_values["output"]: + raise ValueError( + "'output' value %s is invalid. Valid values are %s" + % (output, ",".join(options_values["output"])), + ) + + if output == "json" and not command.endswith("| display json"): + cmd = "%s | display json" % command + elif output == "xml" and not command.endswith("| display xml"): + cmd = "%s | display xml" % command + elif output == "text" and ( + command.endswith("| display json") or command.endswith("| display xml") + ): + cmd = command.rsplit("|", 1)[0] + else: + cmd = command + return cmd diff --git a/ansible_collections/junipernetworks/junos/plugins/doc_fragments/__init__.py b/ansible_collections/junipernetworks/junos/plugins/doc_fragments/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/doc_fragments/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/doc_fragments/junos.py b/ansible_collections/junipernetworks/junos/plugins/doc_fragments/junos.py new file mode 100644 index 000000000..23bf60bb2 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/doc_fragments/junos.py @@ -0,0 +1,21 @@ +# -*- 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: {} +notes: +- For information on using CLI and netconf see the :ref:`Junos OS Platform Options + guide <junos_platform_options>` +- 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 Juniper network devices see U(https://www.ansible.com/ansible-juniper). +""" diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/acl_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..ae2303c00 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acl_interfaces/acl_interfaces.py @@ -0,0 +1,80 @@ +# +# -*- 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 junos_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 junos_acl_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "access_groups": { + "elements": "dict", + "options": { + "acls": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "type": "str", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "afi": {"choices": ["ipv4", "ipv6"], "type": "str"}, + }, + "type": "list", + }, + "name": {"type": "str"}, + }, + "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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/acls.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/acls.py new file mode 100644 index 000000000..bf8633268 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/acls/acls.py @@ -0,0 +1,211 @@ +# +# _*_ 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 junos_acls module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class AclsArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_acls module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "type": "list", + "options": { + "afi": { + "required": True, + "choices": ["ipv4", "ipv6"], + "type": "str", + }, + "acls": { + "elements": "dict", + "type": "list", + "options": { + "name": {"required": True, "type": "str"}, + "aces": { + "elements": "dict", + "type": "list", + "options": { + "name": {"required": True, "type": "str"}, + "source": { + "type": "dict", + "options": { + "address": {"type": "raw"}, + "prefix_list": { + "elements": "dict", + "type": "list", + "options": { + "name": {"type": "str"}, + }, + }, + "port_protocol": { + "type": "dict", + "options": { + "eq": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "start": { + "type": "int", + }, + "end": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "destination": { + "type": "dict", + "options": { + "address": {"type": "raw"}, + "prefix_list": { + "elements": "dict", + "type": "list", + "options": { + "name": {"type": "str"}, + }, + }, + "port_protocol": { + "type": "dict", + "options": { + "eq": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "start": { + "type": "int", + }, + "end": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "protocol": {"type": "str"}, + "protocol_options": { + "type": "dict", + "options": { + "icmp": { + "type": "dict", + "options": { + "dod_host_prohibited": { + "type": "bool", + }, + "dod_net_prohibited": { + "type": "bool", + }, + "echo": {"type": "bool"}, + "echo_reply": {"type": "bool"}, + "host_tos_unreachable": { + "type": "bool", + }, + "host_redirect": { + "type": "bool", + }, + "host_tos_redirect": { + "type": "bool", + }, + "host_unknown": { + "type": "bool", + }, + "host_unreachable": { + "type": "bool", + }, + "net_redirect": { + "type": "bool", + }, + "net_tos_redirect": { + "type": "bool", + }, + "network_unknown": { + "type": "bool", + }, + "port_unreachable": { + "type": "bool", + }, + "protocol_unreachable": { + "type": "bool", + }, + "reassembly_timeout": { + "type": "bool", + }, + "redirect": {"type": "bool"}, + "router_advertisement": { + "type": "bool", + }, + "router_solicitation": { + "type": "bool", + }, + "source_route_failed": { + "type": "bool", + }, + "time_exceeded": { + "type": "bool", + }, + "ttl_exceeded": { + "type": "bool", + }, + }, + }, + }, + }, + "grant": { + "type": "str", + "choices": ["permit", "deny"], + }, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } + + +# pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/bgp_address_family.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..950c68629 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_address_family/bgp_address_family.py @@ -0,0 +1,894 @@ +# +# -*- 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 junos_bgp_address_family module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Bgp_address_familyArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_bgp_address_family module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "address_family": { + "elements": "dict", + "options": { + "af_type": { + "elements": "dict", + "options": { + "accepted_prefix_limit": { + "options": { + "forever": {"type": "bool"}, + "idle_timeout": {"type": "bool"}, + "idle_timeout_value": {"type": "int"}, + "limit_threshold": {"type": "int"}, + "maximum": {"type": "int"}, + "teardown": {"type": "bool"}, + }, + "type": "dict", + }, + "add_path": { + "options": { + "receive": {"type": "bool"}, + "send": { + "options": { + "include_backup_path": { + "type": "int", + }, + "multipath": {"type": "bool"}, + "path_count": { + "required": True, + "type": "int", + }, + "path_selection_mode": { + "options": { + "all_paths": { + "type": "bool", + }, + "equal_cost_paths": { + "type": "bool", + }, + }, + "type": "dict", + }, + "prefix_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "aggregate_label": { + "options": { + "community": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "aigp": { + "options": { + "disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "damping": {"type": "bool"}, + "defer_initial_multipath_build": { + "options": { + "maximum_delay": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "delay_route_advertisements": { + "options": { + "max_delay_route_age": {"type": "int"}, + "max_delay_routing_uptime": { + "type": "int", + }, + "min_delay_inbound_convergence": { + "type": "int", + }, + "min_delay_routing_uptime": { + "type": "int", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "entropy_label": { + "options": { + "import": {"type": "str"}, + "no_next_hop_validation": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "explicit_null": { + "options": { + "connected_only": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "extended_nexthop": {"type": "bool"}, + "extended_nexthop_color": {"type": "bool"}, + "graceful_restart_forwarding_state_bit": { + "choices": ["from-fib", "set"], + "type": "str", + }, + "legacy_redirect_ip_action": { + "options": { + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "local_ipv4_address": {"type": "str"}, + "loops": {"type": "int"}, + "no_install": {"type": "bool"}, + "no_validate": {"type": "str"}, + "output_queue_priority_expedited": { + "type": "bool", + }, + "output_queue_priority_priority": { + "type": "int", + }, + "per_group_label": {"type": "bool"}, + "per_prefix_label": {"type": "bool"}, + "prefix_limit": { + "options": { + "forever": {"type": "bool"}, + "idle_timeout": {"type": "bool"}, + "idle_timeout_value": {"type": "int"}, + "limit_threshold": {"type": "int"}, + "maximum": {"type": "int"}, + "teardown": {"type": "bool"}, + }, + "type": "dict", + }, + "resolve_vpn": {"type": "bool"}, + "rib": {"choices": ["inet.3"], "type": "str"}, + "ribgroup_name": {"type": "str"}, + "route_refresh_priority_expedited": { + "type": "bool", + }, + "route_refresh_priority_priority": { + "type": "int", + }, + "secondary_independent_resolution": { + "type": "bool", + }, + "set": {"type": "bool"}, + "strip_nexthop": {"type": "bool"}, + "topology": { + "elements": "dict", + "options": { + "community": { + "elements": "str", + "type": "list", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "traffic_statistics": { + "options": { + "file": { + "options": { + "filename": {"type": "str"}, + "files": {"type": "int"}, + "no_world_readable": { + "type": "bool", + }, + "size": {"type": "int"}, + "world_readable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "interval": {"type": "int"}, + "labeled_path": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "type": { + "choices": [ + "any", + "flow", + "labeled-unicast", + "multicast", + "segment-routing-te", + "unicast", + "signaling", + "auto-discovery-mspw", + "auto-discovery-only", + ], + "type": "str", + }, + "withdraw_priority_expedited": { + "type": "bool", + }, + "withdraw_priority_priority": {"type": "int"}, + }, + "type": "list", + }, + "afi": { + "choices": [ + "evpn", + "inet", + "inet-mdt", + "inet-mvpn", + "inet-vpn", + "inet6", + "inet6-mvpn", + "inet6-vpn", + "iso-vpn", + "l2vpn", + "route-target", + "traffic-engineering", + ], + "type": "str", + }, + }, + "type": "list", + }, + "groups": { + "elements": "dict", + "options": { + "address_family": { + "elements": "dict", + "options": { + "af_type": { + "elements": "dict", + "options": { + "accepted_prefix_limit": { + "options": { + "forever": {"type": "bool"}, + "idle_timeout": { + "type": "bool", + }, + "idle_timeout_value": { + "type": "int", + }, + "limit_threshold": { + "type": "int", + }, + "maximum": {"type": "int"}, + "teardown": {"type": "bool"}, + }, + "type": "dict", + }, + "add_path": { + "options": { + "receive": {"type": "bool"}, + "send": { + "options": { + "include_backup_path": { + "type": "int", + }, + "multipath": { + "type": "bool", + }, + "path_count": { + "required": True, + "type": "int", + }, + "path_selection_mode": { + "options": { + "all_paths": { + "type": "bool", + }, + "equal_cost_paths": { + "type": "bool", + }, + }, + "type": "dict", + }, + "prefix_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "aggregate_label": { + "options": { + "community": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "aigp": { + "options": { + "disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "damping": {"type": "bool"}, + "defer_initial_multipath_build": { + "options": { + "maximum_delay": { + "type": "int", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "delay_route_advertisements": { + "options": { + "max_delay_route_age": { + "type": "int", + }, + "max_delay_routing_uptime": { + "type": "int", + }, + "min_delay_inbound_convergence": { + "type": "int", + }, + "min_delay_routing_uptime": { + "type": "int", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "entropy_label": { + "options": { + "import": {"type": "str"}, + "no_next_hop_validation": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "explicit_null": { + "options": { + "connected_only": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "extended_nexthop": {"type": "bool"}, + "extended_nexthop_color": { + "type": "bool", + }, + "graceful_restart_forwarding_state_bit": { + "choices": ["from-fib", "set"], + "type": "str", + }, + "legacy_redirect_ip_action": { + "options": { + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "local_ipv4_address": {"type": "str"}, + "loops": {"type": "int"}, + "no_install": {"type": "bool"}, + "no_validate": {"type": "str"}, + "output_queue_priority_expedited": { + "type": "bool", + }, + "output_queue_priority_priority": { + "type": "int", + }, + "per_group_label": {"type": "bool"}, + "per_prefix_label": {"type": "bool"}, + "prefix_limit": { + "options": { + "forever": {"type": "bool"}, + "idle_timeout": { + "type": "bool", + }, + "idle_timeout_value": { + "type": "int", + }, + "limit_threshold": { + "type": "int", + }, + "maximum": {"type": "int"}, + "teardown": {"type": "bool"}, + }, + "type": "dict", + }, + "resolve_vpn": {"type": "bool"}, + "rib": { + "choices": ["inet.3"], + "type": "str", + }, + "ribgroup_name": {"type": "str"}, + "route_refresh_priority_expedited": { + "type": "bool", + }, + "route_refresh_priority_priority": { + "type": "int", + }, + "secondary_independent_resolution": { + "type": "bool", + }, + "set": {"type": "bool"}, + "strip_nexthop": {"type": "bool"}, + "topology": { + "elements": "dict", + "options": { + "community": { + "elements": "str", + "type": "list", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "traffic_statistics": { + "options": { + "file": { + "options": { + "filename": { + "type": "str", + }, + "files": { + "type": "int", + }, + "no_world_readable": { + "type": "bool", + }, + "size": { + "type": "int", + }, + "world_readable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "interval": {"type": "int"}, + "labeled_path": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "type": { + "choices": [ + "any", + "flow", + "labeled-unicast", + "multicast", + "segment-routing-te", + "unicast", + "signaling", + "auto-discovery-mspw", + "auto-discovery-only", + ], + "type": "str", + }, + "withdraw_priority_expedited": { + "type": "bool", + }, + "withdraw_priority_priority": { + "type": "int", + }, + }, + "type": "list", + }, + "afi": { + "choices": [ + "evpn", + "inet", + "inet-mdt", + "inet-mvpn", + "inet-vpn", + "inet6", + "inet6-mvpn", + "inet6-vpn", + "iso-vpn", + "l2vpn", + "route-target", + "traffic-engineering", + ], + "type": "str", + }, + }, + "type": "list", + }, + "name": {"type": "str"}, + "neighbors": { + "elements": "dict", + "options": { + "address_family": { + "elements": "dict", + "options": { + "af_type": { + "elements": "dict", + "options": { + "accepted_prefix_limit": { + "options": { + "forever": { + "type": "bool", + }, + "idle_timeout": { + "type": "bool", + }, + "idle_timeout_value": { + "type": "int", + }, + "limit_threshold": { + "type": "int", + }, + "maximum": { + "type": "int", + }, + "teardown": { + "type": "bool", + }, + }, + "type": "dict", + }, + "add_path": { + "options": { + "receive": { + "type": "bool", + }, + "send": { + "options": { + "include_backup_path": { + "type": "int", + }, + "multipath": { + "type": "bool", + }, + "path_count": { + "required": True, + "type": "int", + }, + "path_selection_mode": { + "options": { + "all_paths": { + "type": "bool", + }, + "equal_cost_paths": { + "type": "bool", + }, + }, + "type": "dict", + }, + "prefix_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "aggregate_label": { + "options": { + "community": { + "type": "str", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "aigp": { + "options": { + "disable": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "damping": {"type": "bool"}, + "defer_initial_multipath_build": { + "options": { + "maximum_delay": { + "type": "int", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "delay_route_advertisements": { + "options": { + "max_delay_route_age": { + "type": "int", + }, + "max_delay_routing_uptime": { + "type": "int", + }, + "min_delay_inbound_convergence": { + "type": "int", + }, + "min_delay_routing_uptime": { + "type": "int", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "entropy_label": { + "options": { + "import": { + "type": "str", + }, + "no_next_hop_validation": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "explicit_null": { + "options": { + "connected_only": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "extended_nexthop": { + "type": "bool", + }, + "extended_nexthop_color": { + "type": "bool", + }, + "graceful_restart_forwarding_state_bit": { + "choices": [ + "from-fib", + "set", + ], + "type": "str", + }, + "legacy_redirect_ip_action": { + "options": { + "receive": { + "type": "bool", + }, + "send": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "local_ipv4_address": { + "type": "str", + }, + "loops": {"type": "int"}, + "no_install": {"type": "bool"}, + "no_validate": {"type": "str"}, + "output_queue_priority_expedited": { + "type": "bool", + }, + "output_queue_priority_priority": { + "type": "int", + }, + "per_group_label": { + "type": "bool", + }, + "per_prefix_label": { + "type": "bool", + }, + "prefix_limit": { + "options": { + "forever": { + "type": "bool", + }, + "idle_timeout": { + "type": "bool", + }, + "idle_timeout_value": { + "type": "int", + }, + "limit_threshold": { + "type": "int", + }, + "maximum": { + "type": "int", + }, + "teardown": { + "type": "bool", + }, + }, + "type": "dict", + }, + "resolve_vpn": { + "type": "bool", + }, + "rib": { + "choices": ["inet.3"], + "type": "str", + }, + "ribgroup_name": { + "type": "str", + }, + "route_refresh_priority_expedited": { + "type": "bool", + }, + "route_refresh_priority_priority": { + "type": "int", + }, + "secondary_independent_resolution": { + "type": "bool", + }, + "set": {"type": "bool"}, + "strip_nexthop": { + "type": "bool", + }, + "topology": { + "elements": "dict", + "options": { + "community": { + "elements": "str", + "type": "list", + }, + "name": { + "type": "str", + }, + }, + "type": "list", + }, + "traffic_statistics": { + "options": { + "file": { + "options": { + "filename": { + "type": "str", + }, + "files": { + "type": "int", + }, + "no_world_readable": { + "type": "bool", + }, + "size": { + "type": "int", + }, + "world_readable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "interval": { + "type": "int", + }, + "labeled_path": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "type": { + "choices": [ + "any", + "flow", + "labeled-unicast", + "multicast", + "segment-routing-te", + "unicast", + "signaling", + "auto-discovery-mspw", + "auto-discovery-only", + ], + "type": "str", + }, + "withdraw_priority_expedited": { + "type": "bool", + }, + "withdraw_priority_priority": { + "type": "int", + }, + }, + "type": "list", + }, + "afi": { + "choices": [ + "evpn", + "inet", + "inet-mdt", + "inet-mvpn", + "inet-vpn", + "inet6", + "inet6-mvpn", + "inet6-vpn", + "iso-vpn", + "l2vpn", + "route-target", + "traffic-engineering", + ], + "type": "str", + }, + }, + "type": "list", + }, + "neighbor_address": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/bgp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/bgp_global.py new file mode 100644 index 000000000..2cbbdf5e2 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/bgp_global/bgp_global.py @@ -0,0 +1,1271 @@ +# +# -*- 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 junos_bgp_global module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Bgp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_bgp_global module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "accept_remote_nexthop": {"type": "bool"}, + "add_path_display_ipv4_address": {"type": "bool"}, + "advertise_bgp_static": { + "options": { + "policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_external": { + "options": { + "conditional": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_from_main_vpn_tables": {"type": "bool"}, + "advertise_inactive": {"type": "bool"}, + "advertise_peer_as": {"type": "bool"}, + "as_number": {"type": "str"}, + "asdot_notation": {"type": "bool"}, + "authentication_algorithm": { + "choices": ["aes-128-cmac-96", "hmac-sha-1-96", "md5"], + "type": "str", + }, + "authentication_key": {"type": "str", "no_log": True}, + "authentication_key_chain": {"type": "str", "no_log": False}, + "bfd_liveness_detection": { + "options": { + "authentication": { + "options": { + "algorithm": { + "choices": [ + "keyed-md5", + "keyed-sha-1", + "meticulous-keyed-md5", + "meticulous-keyed-sha-1", + "simple-password", + ], + "type": "str", + }, + "key_chain": {"type": "str", "no_log": True}, + "loose_check": {"type": "bool"}, + }, + "type": "dict", + }, + "detection_time": { + "options": {"threshold": {"type": "int"}}, + "type": "dict", + }, + "holddown_interval": {"type": "int"}, + "minimum_interval": {"type": "int"}, + "minimum_receive_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + "no_adaptation": {"type": "bool"}, + "session_mode": { + "choices": ["automatic", "multihop", "single-hop"], + "type": "str", + }, + "transmit_interval": { + "options": { + "minimum_interval": {"type": "int"}, + "threshold": {"type": "int"}, + }, + "type": "dict", + }, + "version": { + "choices": ["0", "1", "automatic"], + "type": "str", + }, + }, + "type": "dict", + }, + "bgp_error_tolerance": { + "options": { + "malformed_route_limit": {"type": "int"}, + "malformed_update_log_interval": {"type": "int"}, + "no_malformed_route_limit": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "bmp": { + "options": { + "monitor": {"type": "bool"}, + "route_monitoring": { + "options": { + "none": {"type": "bool"}, + "post_policy": {"type": "bool"}, + "post_policy_exclude_non_eligible": { + "type": "bool", + }, + "post_policy_exclude_non_feasible": { + "type": "bool", + }, + "pre_policy": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "cluster_id": {"type": "str"}, + "damping": {"type": "bool"}, + "description": {"type": "str"}, + "disable": {"type": "bool"}, + "egress_te": { + "options": { + "backup_path": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "egress_te_backup_paths": { + "options": { + "templates": { + "elements": "dict", + "options": { + "ip_forward": { + "options": { + "rti_name": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "path_name": {"required": True, "type": "str"}, + "peers": {"elements": "str", "type": "list"}, + "remote_nexthop": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "egress_te_set_segment": { + "elements": "dict", + "options": { + "egress_te_backup_segment_label": {"type": "int"}, + "label": {"type": "int"}, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "egress_te_sid_stats": {"type": "bool"}, + "enforce_first_as": {"type": "bool"}, + "export": {"type": "str"}, + "forwarding_context": {"type": "str"}, + "graceful_restart": { + "options": { + "disable": {"type": "bool"}, + "dont_help_shared_fate_bfd_down": {"type": "bool"}, + "forwarding_state_bit": { + "options": { + "as_rr_client": {"type": "bool"}, + "from_fib": {"type": "bool"}, + }, + "type": "dict", + }, + "long_lived": { + "options": { + "advertise_to_non_llgr_neighbor": { + "options": { + "omit_no_export": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "receiver_disable": {"type": "bool"}, + }, + "type": "dict", + }, + "restart_time": {"type": "int"}, + "set": {"type": "bool"}, + "stale_routes_time": {"type": "int"}, + }, + "type": "dict", + }, + "groups": { + "elements": "dict", + "options": { + "accept_remote_nexthop": {"type": "bool"}, + "add_path_display_ipv4_address": {"type": "bool"}, + "advertise_bgp_static": { + "options": { + "policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_external": { + "options": { + "conditional": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_inactive": {"type": "bool"}, + "advertise_peer_as": {"type": "bool"}, + "allow": {"elements": "str", "type": "list"}, + "as_override": {"type": "bool"}, + "authentication_algorithm": { + "choices": [ + "aes-128-cmac-96", + "hmac-sha-1-96", + "md5", + ], + "type": "str", + }, + "authentication_key": {"type": "str", "no_log": True}, + "authentication_key_chain": { + "type": "str", + "no_log": False, + }, + "bfd_liveness_detection": { + "options": { + "authentication": { + "options": { + "algorithm": { + "choices": [ + "keyed-md5", + "keyed-sha-1", + "meticulous-keyed-md5", + "meticulous-keyed-sha-1", + "simple-password", + ], + "type": "str", + }, + "key_chain": { + "type": "str", + "no_log": True, + }, + "loose_check": {"type": "bool"}, + }, + "type": "dict", + }, + "detection_time": { + "options": {"threshold": {"type": "int"}}, + "type": "dict", + }, + "holddown_interval": {"type": "int"}, + "minimum_interval": {"type": "int"}, + "minimum_receive_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + "no_adaptation": {"type": "bool"}, + "session_mode": { + "choices": [ + "automatic", + "multihop", + "single-hop", + ], + "type": "str", + }, + "transmit_interval": { + "options": { + "minimum_interval": {"type": "int"}, + "threshold": {"type": "int"}, + }, + "type": "dict", + }, + "version": { + "choices": ["0", "1", "automatic"], + "type": "str", + }, + }, + "type": "dict", + }, + "bgp_error_tolerance": { + "options": { + "malformed_route_limit": {"type": "int"}, + "malformed_update_log_interval": { + "type": "int", + }, + "no_malformed_route_limit": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "bmp": { + "options": { + "monitor": {"type": "bool"}, + "route_monitoring": { + "options": { + "none": {"type": "bool"}, + "post_policy": {"type": "bool"}, + "post_policy_exclude_non_eligible": { + "type": "bool", + }, + "post_policy_exclude_non_feasible": { + "type": "bool", + }, + "pre_policy": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "cluster_id": {"type": "str"}, + "damping": {"type": "bool"}, + "description": {"type": "str"}, + "egress_te": { + "options": { + "backup_path": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "enforce_first_as": {"type": "bool"}, + "export": {"type": "str"}, + "forwarding_context": {"type": "str"}, + "graceful_restart": { + "options": { + "disable": {"type": "bool"}, + "dont_help_shared_fate_bfd_down": { + "type": "bool", + }, + "forwarding_state_bit": { + "options": { + "as_rr_client": {"type": "bool"}, + "from_fib": {"type": "bool"}, + }, + "type": "dict", + }, + "long_lived": { + "options": { + "advertise_to_non_llgr_neighbor": { + "options": { + "omit_no_export": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "receiver_disable": {"type": "bool"}, + }, + "type": "dict", + }, + "restart_time": {"type": "int"}, + "set": {"type": "bool"}, + "stale_routes_time": {"type": "int"}, + }, + "type": "dict", + }, + "hold_time": {"type": "int"}, + "idle_after_switch_over": { + "options": { + "forever": {"type": "bool"}, + "timeout": {"type": "int"}, + }, + "type": "dict", + }, + "import": {"elements": "str", "type": "list"}, + "include_mp_next_hop": {"type": "bool"}, + "ipsec_sa": {"type": "str"}, + "keep": {"choices": ["all", "none"], "type": "str"}, + "local_address": {"type": "str"}, + "local_as": { + "options": { + "alias": {"type": "bool"}, + "as_num": {"required": True, "type": "str"}, + "loops": {"type": "int"}, + "no_prepend_global_as": {"type": "bool"}, + "private": {"type": "bool"}, + }, + "type": "dict", + }, + "local_interface": {"type": "str"}, + "local_preference": {"type": "str"}, + "log_updown": {"type": "bool"}, + "metric_out": { + "options": { + "igp": { + "options": { + "delay_med_update": {"type": "bool"}, + "metric_offset": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "metric_value": {"type": "int"}, + "minimum_igp": { + "options": { + "metric_offset": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "mtu_discovery": {"type": "bool"}, + "multihop": { + "options": { + "no_nexthop_change": {"type": "bool"}, + "set": {"type": "bool"}, + "ttl": {"type": "int"}, + }, + "type": "dict", + }, + "multipath": { + "options": { + "disable": {"type": "bool"}, + "multiple_as": {"type": "bool"}, + "multiple_as_disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "name": {"type": "str"}, + "neighbors": { + "elements": "dict", + "options": { + "accept_remote_nexthop": {"type": "bool"}, + "add_path_display_ipv4_address": { + "type": "bool", + }, + "advertise_bgp_static": { + "options": { + "policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_external": { + "options": { + "conditional": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "advertise_inactive": {"type": "bool"}, + "advertise_peer_as": {"type": "bool"}, + "as_override": {"type": "bool"}, + "authentication_algorithm": { + "choices": [ + "aes-128-cmac-96", + "hmac-sha-1-96", + "md5", + ], + "type": "str", + }, + "authentication_key": { + "type": "str", + "no_log": True, + }, + "authentication_key_chain": { + "type": "str", + "no_log": False, + }, + "bfd_liveness_detection": { + "options": { + "authentication": { + "options": { + "algorithm": { + "choices": [ + "keyed-md5", + "keyed-sha-1", + "meticulous-keyed-md5", + "meticulous-keyed-sha-1", + "simple-password", + ], + "type": "str", + }, + "key_chain": { + "type": "str", + "no_log": False, + }, + "loose_check": { + "type": "bool", + }, + }, + "type": "dict", + }, + "detection_time": { + "options": { + "threshold": {"type": "int"}, + }, + "type": "dict", + }, + "holddown_interval": {"type": "int"}, + "minimum_interval": {"type": "int"}, + "minimum_receive_interval": { + "type": "int", + }, + "multiplier": {"type": "int"}, + "no_adaptation": {"type": "bool"}, + "session_mode": { + "choices": [ + "automatic", + "multihop", + "single-hop", + ], + "type": "str", + }, + "transmit_interval": { + "options": { + "minimum_interval": { + "type": "int", + }, + "threshold": {"type": "int"}, + }, + "type": "dict", + }, + "version": { + "choices": ["0", "1", "automatic"], + "type": "str", + }, + }, + "type": "dict", + }, + "bgp_error_tolerance": { + "options": { + "malformed_route_limit": { + "type": "int", + }, + "malformed_update_log_interval": { + "type": "int", + }, + "no_malformed_route_limit": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "bmp": { + "options": { + "monitor": {"type": "bool"}, + "route_monitoring": { + "options": { + "none": {"type": "bool"}, + "post_policy": { + "type": "bool", + }, + "post_policy_exclude_non_eligible": { + "type": "bool", + }, + "post_policy_exclude_non_feasible": { + "type": "bool", + }, + "pre_policy": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "cluster_id": {"type": "str"}, + "damping": {"type": "bool"}, + "description": {"type": "str"}, + "egress_te": { + "options": { + "backup_path": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "enforce_first_as": {"type": "bool"}, + "export": {"type": "str"}, + "forwarding_context": {"type": "str"}, + "graceful_restart": { + "options": { + "disable": {"type": "bool"}, + "dont_help_shared_fate_bfd_down": { + "type": "bool", + }, + "forwarding_state_bit": { + "options": { + "as_rr_client": { + "type": "bool", + }, + "from_fib": {"type": "bool"}, + }, + "type": "dict", + }, + "long_lived": { + "options": { + "advertise_to_non_llgr_neighbor": { + "options": { + "omit_no_export": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "receiver_disable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "restart_time": {"type": "int"}, + "set": {"type": "bool"}, + "stale_routes_time": {"type": "int"}, + }, + "type": "dict", + }, + "hold_time": {"type": "int"}, + "idle_after_switch_over": { + "options": { + "forever": {"type": "bool"}, + "timeout": {"type": "int"}, + }, + "type": "dict", + }, + "import": {"elements": "str", "type": "list"}, + "include_mp_next_hop": {"type": "bool"}, + "ipsec_sa": {"type": "str"}, + "keep": { + "choices": ["all", "none"], + "type": "str", + }, + "local_address": {"type": "str"}, + "local_as": { + "options": { + "alias": {"type": "bool"}, + "as_num": { + "required": True, + "type": "str", + }, + "loops": {"type": "int"}, + "no_prepend_global_as": { + "type": "bool", + }, + "private": {"type": "bool"}, + }, + "type": "dict", + }, + "local_interface": {"type": "str"}, + "local_preference": {"type": "str"}, + "log_updown": {"type": "bool"}, + "metric_out": { + "options": { + "igp": { + "options": { + "delay_med_update": { + "type": "bool", + }, + "metric_offset": { + "type": "int", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "metric_value": {"type": "int"}, + "minimum_igp": { + "options": { + "metric_offset": { + "type": "int", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "mtu_discovery": {"type": "bool"}, + "multihop": { + "options": { + "no_nexthop_change": {"type": "bool"}, + "set": {"type": "bool"}, + "ttl": {"type": "int"}, + }, + "type": "dict", + }, + "multipath": { + "options": { + "disable": {"type": "bool"}, + "multiple_as": {"type": "bool"}, + "multiple_as_disable": { + "type": "bool", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "neighbor_address": {"type": "str"}, + "no_advertise_peer_as": {"type": "bool"}, + "no_aggregator_id": {"type": "bool"}, + "no_client_reflect": {"type": "bool"}, + "out_delay": {"type": "int"}, + "outbound_route_filter": { + "options": { + "bgp_orf_cisco_mode": {"type": "bool"}, + "prefix_based": { + "options": { + "accept": { + "options": { + "inet": { + "type": "bool", + }, + "inet6": { + "type": "bool", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "passive": {"type": "bool"}, + "peer_as": {"type": "str"}, + "preference": {"type": "str"}, + "remove_private": { + "options": { + "all": {"type": "bool"}, + "all_replace": {"type": "bool"}, + "all_replace_nearest": { + "type": "bool", + }, + "no_peer_loop_check": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "rfc6514_compliant_safi129": {"type": "bool"}, + "route_server_client": {"type": "bool"}, + "tcp_aggressive_transmission": { + "type": "bool", + }, + "tcp_mss": {"type": "int"}, + "traceoptions": { + "options": { + "file": { + "options": { + "filename": { + "required": True, + "type": "str", + }, + "files": {"type": "int"}, + "no_world_readable": { + "type": "bool", + }, + "size": {"type": "int"}, + "world_readable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "flag": { + "elements": "dict", + "options": { + "detail": {"type": "bool"}, + "disable": {"type": "bool"}, + "filter": { + "options": { + "match_on_prefix": { + "type": "bool", + }, + "policy": { + "type": "str", + }, + "set": { + "type": "bool", + }, + }, + "type": "dict", + }, + "name": { + "choices": [ + "4byte-as", + "add-path", + "all", + "bfd", + "damping", + "egress-te", + "general", + "graceful-restart", + "keepalive", + "normal", + "nsr-synchronization", + "open", + "packets", + "policy", + "refresh", + "route", + "state", + "task", + "thread-io", + "thread-update-io", + "timer", + "update", + ], + "required": True, + "type": "str", + }, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "ttl": {"type": "int"}, + "unconfigured_peer_graceful_restart": { + "type": "bool", + }, + "vpn_apply_export": {"type": "bool"}, + }, + "type": "list", + }, + "no_advertise_peer_as": {"type": "bool"}, + "no_aggregator_id": {"type": "bool"}, + "no_client_reflect": {"type": "bool"}, + "optimal_route_reflection": { + "options": { + "igp_backup": {"type": "str"}, + "igp_primary": {"type": "str"}, + }, + "type": "dict", + }, + "out_delay": {"type": "int"}, + "outbound_route_filter": { + "options": { + "bgp_orf_cisco_mode": {"type": "bool"}, + "prefix_based": { + "options": { + "accept": { + "options": { + "inet": {"type": "bool"}, + "inet6": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "passive": {"type": "bool"}, + "peer_as": {"type": "str"}, + "preference": {"type": "str"}, + "remove_private": { + "options": { + "all": {"type": "bool"}, + "all_replace": {"type": "bool"}, + "all_replace_nearest": {"type": "bool"}, + "no_peer_loop_check": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "rfc6514_compliant_safi129": {"type": "bool"}, + "route_server_client": {"type": "bool"}, + "tcp_aggressive_transmission": {"type": "bool"}, + "tcp_mss": {"type": "int"}, + "traceoptions": { + "options": { + "file": { + "options": { + "filename": { + "required": True, + "type": "str", + }, + "files": {"type": "int"}, + "no_world_readable": {"type": "bool"}, + "size": {"type": "int"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "flag": { + "elements": "dict", + "options": { + "detail": {"type": "bool"}, + "disable": {"type": "bool"}, + "filter": { + "options": { + "match_on_prefix": { + "type": "bool", + }, + "policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "name": { + "choices": [ + "4byte-as", + "add-path", + "all", + "bfd", + "damping", + "egress-te", + "general", + "graceful-restart", + "keepalive", + "normal", + "nsr-synchronization", + "open", + "packets", + "policy", + "refresh", + "route", + "state", + "task", + "thread-io", + "thread-update-io", + "timer", + "update", + ], + "required": True, + "type": "str", + }, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "ttl": {"type": "int"}, + "type": { + "choices": ["external", "internal"], + "type": "str", + }, + "unconfigured_peer_graceful_restart": {"type": "bool"}, + "vpn_apply_export": {"type": "bool"}, + }, + "type": "list", + }, + "hold_time": {"type": "int"}, + "holddown_all_stale_labels": {"type": "bool"}, + "idle_after_switch_over": { + "options": { + "forever": {"type": "bool"}, + "timeout": {"type": "int"}, + }, + "type": "dict", + }, + "import": {"elements": "str", "type": "list"}, + "include_mp_next_hop": {"type": "bool"}, + "ipsec_sa": {"type": "str"}, + "keep": {"choices": ["all", "none"], "type": "str"}, + "local_address": {"type": "str"}, + "local_as": { + "options": { + "alias": {"type": "bool"}, + "as_num": {"required": True, "type": "str"}, + "loops": {"type": "int"}, + "no_prepend_global_as": {"type": "bool"}, + "private": {"type": "bool"}, + }, + "type": "dict", + }, + "local_interface": {"type": "str"}, + "local_preference": {"type": "str"}, + "log_updown": {"type": "bool"}, + "loops": {"type": "int"}, + "metric_out": { + "options": { + "igp": { + "options": { + "delay_med_update": {"type": "bool"}, + "metric_offset": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "metric_value": {"type": "int"}, + "minimum_igp": { + "options": { + "metric_offset": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "mtu_discovery": {"type": "bool"}, + "multihop": { + "options": { + "no_nexthop_change": {"type": "bool"}, + "set": {"type": "bool"}, + "ttl": {"type": "int"}, + }, + "type": "dict", + }, + "multipath": { + "options": { + "disable": {"type": "bool"}, + "multiple_as": {"type": "bool"}, + "multiple_as_disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "multipath_build_priority": { + "choices": ["low", "medium"], + "type": "str", + }, + "no_advertise_peer_as": {"type": "bool"}, + "no_aggregator_id": {"type": "bool"}, + "no_client_reflect": {"type": "bool"}, + "no_precision_timers": {"type": "bool"}, + "out_delay": {"type": "int"}, + "outbound_route_filter": { + "options": { + "bgp_orf_cisco_mode": {"type": "bool"}, + "prefix_based": { + "options": { + "accept": { + "options": { + "inet": {"type": "bool"}, + "inet6": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "output_queue_priority": { + "options": { + "defaults": { + "options": { + "high": { + "options": { + "expedited": {"type": "bool"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + "low": { + "options": { + "expedited": {"type": "bool"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + "medium": { + "options": { + "expedited": {"type": "bool"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "expedited_update_tokens": { + "type": "int", + "no_log": True, + }, + "priority_update_tokens": { + "elements": "dict", + "no_log": True, + "options": { + "priority": {"required": True, "type": "int"}, + "update_tokens": { + "required": True, + "type": "int", + "no_log": True, + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "passive": {"type": "bool"}, + "path_selection": { + "options": { + "always_compare_med": {"type": "bool"}, + "as_path_ignore": {"type": "bool"}, + "cisco_non_deterministic": {"type": "bool"}, + "external_router_id": {"type": "bool"}, + "l2vpn_use_bgp_rules": {"type": "bool"}, + "med_plus_igp": { + "options": { + "igp_multiplier": {"type": "int"}, + "med_multiplier": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "peer_as": {"type": "str"}, + "precision_timers": {"type": "bool"}, + "preference": {"type": "str"}, + "remove_private": { + "options": { + "all": {"type": "bool"}, + "all_replace": {"type": "bool"}, + "all_replace_nearest": {"type": "bool"}, + "no_peer_loop_check": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "rfc6514_compliant_safi129": {"type": "bool"}, + "route_server_client": {"type": "bool"}, + "send_addpath_optimization": {"type": "bool"}, + "snmp_options": { + "options": { + "backward_traps_only_from_established": { + "type": "bool", + }, + "emit_inet_address_length_in_oid": {"type": "bool"}, + }, + "type": "dict", + }, + "sr_preference_override": {"type": "str"}, + "stale_labels_holddown_period": {"type": "int"}, + "tcp_aggressive_transmission": {"type": "bool"}, + "tcp_mss": {"type": "int"}, + "traceoptions": { + "options": { + "file": { + "options": { + "filename": {"required": True, "type": "str"}, + "files": {"type": "int"}, + "no_world_readable": {"type": "bool"}, + "size": {"type": "int"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "flag": { + "elements": "dict", + "options": { + "detail": {"type": "bool"}, + "disable": {"type": "bool"}, + "filter": { + "options": { + "match_on_prefix": {"type": "bool"}, + "policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "name": { + "choices": [ + "4byte-as", + "add-path", + "all", + "bfd", + "damping", + "egress-te", + "general", + "graceful-restart", + "keepalive", + "normal", + "nsr-synchronization", + "open", + "packets", + "policy", + "refresh", + "route", + "state", + "task", + "thread-io", + "thread-update-io", + "timer", + "update", + ], + "required": True, + "type": "str", + }, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "traffic_statistics_labeled_path": { + "options": { + "file": { + "options": { + "filename": {"type": "str"}, + "files": {"type": "int"}, + "no_world_readable": {"type": "bool"}, + "size": {"type": "int"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "interval": {"type": "int"}, + }, + "type": "dict", + }, + "ttl": {"type": "int"}, + "unconfigured_peer_graceful_restart": {"type": "bool"}, + "vpn_apply_export": {"type": "bool"}, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "purged", + "merged", + "replaced", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/facts.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/facts.py new file mode 100644 index 000000000..2ee80bd7a --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/facts/facts.py @@ -0,0 +1,29 @@ +# +# -*- 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 junos facts module. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class FactsArgs(object): + """The arg spec for the junos facts module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "gather_subset": dict(default=["min"], type="list", elements="str"), + "config_format": dict( + default="text", + choices=["xml", "text", "set", "json"], + ), + "gather_network_resources": dict(type="list", elements="str"), + "available_network_resources": {"type": "bool", "default": False}, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/hostname.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/hostname.py new file mode 100644 index 000000000..84e70cfd0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/hostname/hostname.py @@ -0,0 +1,56 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_hostname module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class HostnameArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_hostname module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": {"options": {"hostname": {"type": "str"}}, "type": "dict"}, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/interfaces.py new file mode 100644 index 000000000..bc152bd38 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/interfaces/interfaces.py @@ -0,0 +1,86 @@ +# +# -*- 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 junos_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class InterfacesArgs(object): + """The arg spec for the junos_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "duplex": { + "choices": ["automatic", "full-duplex", "half-duplex"], + "type": "str", + }, + "enabled": {"default": True, "type": "bool"}, + "hold_time": { + "options": { + "down": {"type": "int"}, + "up": {"type": "int"}, + }, + "required_together": [["down", "up"]], + "type": "dict", + }, + "mtu": {"type": "int"}, + "name": {"required": True, "type": "str"}, + "speed": {"type": "str"}, + "units": { + "elements": "dict", + "options": { + "name": {"type": "int"}, + "description": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/l2_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..e2495ff24 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l2_interfaces/l2_interfaces.py @@ -0,0 +1,75 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the junos_l2_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class L2_interfacesArgs(object): + """The arg spec for the junos_l2_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "access": { + "type": "dict", + "options": {"vlan": {"type": "str"}}, + }, + "name": {"required": True, "type": "str"}, + "trunk": { + "type": "dict", + "options": { + "allowed_vlans": {"elements": "str", "type": "list"}, + "native_vlan": {"type": "str"}, + }, + }, + "unit": {"type": "int"}, + "enhanced_layer": {"type": "bool"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/l3_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..ec89c3233 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/l3_interfaces/l3_interfaces.py @@ -0,0 +1,54 @@ +# -*- 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 junos_l3_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class L3_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_l3_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "ipv4": { + "elements": "dict", + "options": {"address": {"type": "str"}}, + "type": "list", + }, + "ipv6": { + "elements": "dict", + "options": {"address": {"type": "str"}}, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + "unit": {"type": "int", "default": 0}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/lacp.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/lacp.py new file mode 100644 index 000000000..e666d5f94 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp/lacp.py @@ -0,0 +1,64 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the junos_lacp module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class LacpArgs(object): + """The arg spec for the junos_lacp module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "dict", + "options": { + "link_protection": { + "choices": ["revertive", "non-revertive"], + "type": "str", + }, + "system_priority": {"type": "int"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/lacp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..052a54b06 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,79 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the junos_lacp_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lacp_interfacesArgs(object): + """The arg spec for the junos_lacp_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "force_up": {"type": "bool"}, + "name": {"type": "str"}, + "period": {"choices": ["fast", "slow"]}, + "port_priority": {"type": "int"}, + "sync_reset": { + "choices": ["disable", "enable"], + "type": "str", + }, + "system": { + "options": { + "mac": { + "type": "dict", + "options": {"address": {"type": "str"}}, + }, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/lag_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..c0a56f0cd --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lag_interfaces/lag_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 junos_lag_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lag_interfacesArgs(object): + """The arg spec for the junos_lag_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "members": { + "elements": "dict", + "options": { + "link_type": {"choices": ["primary", "backup"]}, + "member": {"type": "str"}, + }, + "type": "list", + }, + "mode": {"choices": ["active", "passive"]}, + "name": {"required": True, "type": "str"}, + "link_protection": {"type": "bool"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/lldp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/lldp_global.py new file mode 100644 index 000000000..8f25d4e10 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_global/lldp_global.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 junos_lldp module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lldp_globalArgs(object): + """The arg spec for the junos_lldp module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "address": {"type": "str"}, + "enabled": {"type": "bool"}, + "hold_multiplier": {"type": "int"}, + "interval": {"type": "int"}, + "transmit_delay": {"type": "int"}, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/lldp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..272302bf0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,63 @@ +# +# -*- 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 junos_lldp_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lldp_interfacesArgs(object): + """The arg spec for the junos_lldp_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "enabled": {"type": "bool"}, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/logging_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/logging_global.py new file mode 100644 index 000000000..a00263a50 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/logging_global/logging_global.py @@ -0,0 +1,1380 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_logging_global module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Logging_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_logging_global module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "allow_duplicates": {"type": "bool"}, + "archive": { + "options": { + "binary_data": {"type": "bool"}, + "file_size": {"type": "str"}, + "files": {"type": "int"}, + "no_binary_data": {"type": "bool"}, + "no_world_readable": {"type": "bool"}, + "set": {"type": "bool"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "console": { + "options": { + "any": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "authorization": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "change_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "conflict_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "daemon": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "dfc": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "external": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "firewall": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "ftp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "interactive_commands": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "kernel": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "ntp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "pfe": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "security": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "user": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "files": { + "elements": "dict", + "options": { + "allow_duplicates": {"type": "bool"}, + "any": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "archive": { + "options": { + "archive_sites": { + "elements": "str", + "type": "list", + }, + "binary_data": {"type": "bool"}, + "file_size": {"type": "str"}, + "files": {"type": "int"}, + "no_binary_data": {"type": "bool"}, + "no_world_readable": {"type": "bool"}, + "set": {"type": "bool"}, + "start_time": {"type": "str"}, + "transfer_interval": {"type": "int"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "authorization": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "change_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "conflict_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "daemon": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "dfc": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "explicit_priority": {"type": "bool"}, + "external": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "firewall": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "ftp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "interactive_commands": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "kernel": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "match": {"type": "str"}, + "match_strings": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "ntp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "pfe": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "security": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "structured_data": { + "options": { + "brief": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "user": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "hosts": { + "elements": "dict", + "options": { + "allow_duplicates": {"type": "bool"}, + "any": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "authorization": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "change_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "conflict_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "daemon": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "dfc": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "exclude_hostname": {"type": "bool"}, + "explicit_priority": {"type": "bool"}, + "external": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "facility_override": {"type": "str"}, + "firewall": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "ftp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "interactive_commands": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "kernel": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "log_prefix": {"type": "str"}, + "match": {"type": "str"}, + "match_strings": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "ntp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "pfe": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "port": {"type": "int"}, + "routing_instance": {"type": "str"}, + "security": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "source_address": {"type": "str"}, + "structured_data": { + "options": { + "brief": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "user": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "log_rotate_frequency": {"type": "int"}, + "routing_instance": {"type": "str"}, + "server": { + "options": { + "routing_instance": { + "options": { + "all": {"type": "bool"}, + "default": {"type": "bool"}, + "routing_instances": { + "elements": "dict", + "options": { + "disable": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "source_address": {"type": "str"}, + "time_format": { + "options": { + "set": {"type": "bool"}, + "millisecond": {"type": "bool"}, + "year": {"type": "bool"}, + }, + "type": "dict", + }, + "users": { + "elements": "dict", + "options": { + "allow_duplicates": {"type": "bool"}, + "any": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "authorization": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "change_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "conflict_log": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "daemon": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "dfc": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "external": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "firewall": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "ftp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "interactive_commands": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "kernel": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "match": {"type": "str"}, + "match_strings": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "ntp": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "pfe": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "security": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + "user": { + "options": { + "level": { + "choices": [ + "alert", + "any", + "critical", + "emergency", + "error", + "info", + "none", + "notice", + "warning", + ], + "required": True, + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/ntp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/ntp_global.py new file mode 100644 index 000000000..d8fdf2678 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ntp_global/ntp_global.py @@ -0,0 +1,133 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_ntp_global module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Ntp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_ntp_global module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "authentication_keys": { + "elements": "dict", + "no_log": False, + "options": { + "algorithm": { + "choices": ["md5", "sha1", "sha256"], + "type": "str", + }, + "id": {"type": "int"}, + "key": {"type": "str", "no_log": True}, + }, + "type": "list", + }, + "boot_server": {"type": "str"}, + "broadcast_client": {"type": "bool"}, + "broadcasts": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "key": {"type": "str", "no_log": False}, + "routing_instance_name": {"type": "str"}, + "ttl": {"type": "int"}, + "version": {"type": "int"}, + }, + "type": "list", + }, + "interval_range": {"type": "int"}, + "multicast_client": {"type": "str"}, + "peers": { + "elements": "dict", + "options": { + "key_id": {"type": "int", "no_log": False}, + "peer": {"type": "str"}, + "prefer": {"type": "bool"}, + "version": {"type": "int"}, + }, + "type": "list", + }, + "servers": { + "elements": "dict", + "options": { + "key_id": {"type": "int"}, + "prefer": {"type": "bool"}, + "routing_instance": {"type": "str"}, + "server": {"type": "str"}, + "version": {"type": "int"}, + }, + "type": "list", + }, + "source_addresses": { + "elements": "dict", + "options": { + "routing_instance": {"type": "str"}, + "source_address": {"type": "str"}, + }, + "type": "list", + }, + "threshold": { + "options": { + "action": { + "choices": ["accept", "reject"], + "type": "str", + }, + "value": {"type": "int"}, + }, + "type": "dict", + }, + "trusted_keys": { + "type": "list", + "elements": "dict", + "no_log": False, + "options": {"key_id": {"type": "int"}}, + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/ospf_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..5bf718991 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospf_interfaces/ospf_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) + +############################################# +# 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 junos_ospf_interfaces module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Ospf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_ospf_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "router_id": {"type": "str"}, + "address_family": { + "elements": "dict", + "options": { + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + "processes": { + "options": { + "area": { + "options": {"area_id": {"type": "str"}}, + "type": "dict", + }, + "authentication": { + "options": { + "md5": { + "options": { + "key_id": {"type": "str"}, + "key_value": { + "type": "str", + "no_log": True, + }, + "start_time": {"type": "str"}, + }, + "type": "dict", + }, + "simple_password": { + "type": "str", + "no_log": True, + }, + }, + "type": "dict", + }, + "bandwidth_based_metrics": { + "elements": "dict", + "options": { + "bandwidth": { + "choices": ["1g", "10g"], + "type": "str", + }, + "metric": {"type": "int"}, + }, + "type": "list", + }, + "dead_interval": {"type": "int"}, + "demand_circuit": {"type": "bool"}, + "flood_reduction": {"type": "bool"}, + "hello_interval": {"type": "int"}, + "interface_type": { + "choices": ["nbma", "p2mp", "p2p"], + "type": "str", + }, + "ipsec_sa": {"type": "str"}, + "metric": {"type": "int"}, + "mtu": {"type": "int"}, + "no_advertise_adjacency_segment": { + "type": "bool", + }, + "no_eligible_backup": {"type": "bool"}, + "no_eligible_remote_backup": {"type": "bool"}, + "no_interface_state_traps": {"type": "bool"}, + "no_neighbor_down_notification": { + "type": "bool", + }, + "node_link_protection": {"type": "str"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + "passive": {"type": "bool"}, + "retransmit_interval": {"type": "int"}, + "secondary": {"type": "bool"}, + "te_metric": {"type": "int"}, + "transit_delay": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/ospfv2.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/ospfv2.py new file mode 100644 index 000000000..a160512a2 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv2/ospfv2.py @@ -0,0 +1,125 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +The arg spec for the junos_ospfv2 module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Ospfv2Args(object): # pylint: disable=R0903 + """The arg spec for the junos_ospfv2 module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "router_id": {"type": "str"}, + "areas": { + "elements": "dict", + "options": { + "area_id": {"required": True, "type": "str"}, + "area_range": {"type": "str"}, + "stub": { + "type": "dict", + "options": { + "default_metric": {"type": "int"}, + "set": {"type": "bool"}, + }, + }, + "interfaces": { + "elements": "dict", + "options": { + "authentication": { + "type": "dict", + "options": {"type": {"type": "dict"}}, + }, + "bandwidth_based_metrics": { + "elements": "dict", + "options": { + "bandwidth": { + "choices": ["1g", "10g"], + "type": "str", + }, + "metric": {"type": "int"}, + }, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + "priority": {"type": "int"}, + "metric": {"type": "int"}, + "flood_reduction": {"type": "bool"}, + "passive": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "dead_interval": {"type": "int"}, + "hello_interval": {"type": "int"}, + "poll_interval": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "transit_delay": {"type": "int"}, + }, + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "external_preference": {"type": "int"}, + "overload": { + "type": "dict", + "options": {"timeout": {"type": "int"}}, + }, + "preference": {"type": "int"}, + "prefix_export_limit": {"type": "int"}, + "reference_bandwidth": { + "choices": ["1g", "10g"], + "type": "str", + }, + "rfc1583compatibility": {"type": "bool"}, + "spf_options": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "holddown": {"type": "int"}, + "rapid_runs": {"type": "int"}, + }, + }, + }, + "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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/ospfv3.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/ospfv3.py new file mode 100644 index 000000000..f08691e8f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/ospfv3/ospfv3.py @@ -0,0 +1,125 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +The arg spec for the junos_ospfv3 module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Ospfv3Args(object): # pylint: disable=R0903 + """The arg spec for the junos_ospfv3 module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "router_id": {"type": "str"}, + "areas": { + "elements": "dict", + "options": { + "area_id": {"required": True, "type": "str"}, + "area_range": {"type": "str"}, + "stub": { + "type": "dict", + "options": { + "default_metric": {"type": "int"}, + "set": {"type": "bool"}, + }, + }, + "interfaces": { + "elements": "dict", + "options": { + "authentication": { + "type": "dict", + "options": {"type": {"type": "dict"}}, + }, + "bandwidth_based_metrics": { + "elements": "dict", + "options": { + "bandwidth": { + "choices": ["1g", "10g"], + "type": "str", + }, + "metric": {"type": "int"}, + }, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + "priority": {"type": "int"}, + "metric": {"type": "int"}, + "flood_reduction": {"type": "bool"}, + "passive": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "dead_interval": {"type": "int"}, + "hello_interval": {"type": "int"}, + "poll_interval": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "transit_delay": {"type": "int"}, + }, + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "external_preference": {"type": "int"}, + "overload": { + "type": "dict", + "options": {"timeout": {"type": "int"}}, + }, + "preference": {"type": "int"}, + "prefix_export_limit": {"type": "int"}, + "reference_bandwidth": { + "choices": ["1g", "10g"], + "type": "str", + }, + "rfc1583compatibility": {"type": "bool"}, + "spf_options": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "holddown": {"type": "int"}, + "rapid_runs": {"type": "int"}, + }, + }, + }, + "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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/prefix_lists.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..a85741c4a --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/prefix_lists/prefix_lists.py @@ -0,0 +1,63 @@ +# +# -*- 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 junos_prefix_lists module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Prefix_listsArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_prefix_lists module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "address_prefixes": {"elements": "str", "type": "list"}, + "dynamic_db": {"type": "bool"}, + "name": {"type": "str", "required": True}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/routing_instances.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/routing_instances.py new file mode 100644 index 000000000..93e69943b --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_instances/routing_instances.py @@ -0,0 +1,108 @@ +# +# -*- 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 junos_routing_instances module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Routing_instancesArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_routing_instances module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "connector_id_advertise": {"type": "bool"}, + "description": {"type": "str"}, + "egress_protection": { + "options": { + "context_identifier": {"type": "str"}, + "protector": {"type": "bool"}, + }, + "type": "dict", + }, + "instance_role": {"choices": ["access", "nni"], "type": "str"}, + "interfaces": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "protect_interface": {"type": "str"}, + }, + "type": "list", + }, + "l2vpn_id": {"type": "str"}, + "name": {"type": "str"}, + "no_irb_layer_2_copy": {"type": "bool"}, + "no_local_switching": {"type": "bool"}, + "no_normalization": {"type": "bool"}, + "no_vrf_advertise": {"type": "bool"}, + "no_vrf_propagate_ttl": {"type": "bool"}, + "qualified_bum_pruning_mode": {"type": "bool"}, + "route_distinguisher": {"type": "str"}, + "routing_interface": {"elements": "str", "type": "list"}, + "type": { + "choices": [ + "evpn", + "evpn-vpws", + "forwarding", + "l2backhaul-vpn", + "l2vpn", + "layer2-control", + "mac-vrf", + "mpls-forwarding", + "mpls-internet-multicast", + "no-forwarding", + "virtual-router", + "vpls", + "vrf", + ], + "type": "str", + }, + "vrf_exports": {"elements": "str", "type": "list"}, + "vrf_imports": {"elements": "str", "type": "list"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/routing_options.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/routing_options.py new file mode 100644 index 000000000..270d5a815 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/routing_options/routing_options.py @@ -0,0 +1,69 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_routing_options module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Routing_optionsArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_routing_options module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "autonomous_system": { + "options": { + "as_number": {"type": "str", "required": True}, + "asdot_notation": {"type": "bool"}, + "loops": {"type": "int"}, + }, + "type": "dict", + }, + "router_id": {"type": "str"}, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/security_policies.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/security_policies.py new file mode 100644 index 000000000..3c28c6418 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies/security_policies.py @@ -0,0 +1,822 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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. +# +############################################# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The arg spec for the junos_security_policies module +""" + + +class Security_policiesArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_security_policies module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "from_zones": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "to_zones": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "policies": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "match": { + "options": { + "application": { + "options": { + "any": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "destination_address": { + "options": { + "addresses": { + "elements": "str", + "type": "list", + }, + "any": { + "type": "bool", + }, + "any_ipv4": { + "type": "bool", + }, + "any_ipv6": { + "type": "bool", + }, + }, + "type": "dict", + }, + "destination_address_excluded": { + "type": "bool", + }, + "dynamic_application": { + "options": { + "any": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + "none": { + "type": "bool", + }, + }, + "type": "dict", + }, + "from_zone": { + "options": { + "any": { + "type": "bool", + }, + "junos_host": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "source_address": { + "options": { + "addresses": { + "elements": "str", + "type": "list", + }, + "any": { + "type": "bool", + }, + "any_ipv4": { + "type": "bool", + }, + "any_ipv6": { + "type": "bool", + }, + }, + "type": "dict", + }, + "source_address_excluded": { + "type": "bool", + }, + "source_end_user_profile": { + "type": "str", + }, + "source_identity": { + "options": { + "any": { + "type": "bool", + }, + "authenticated_user": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + "unauthenticated_user": { + "type": "bool", + }, + "unknown_user": { + "type": "bool", + }, + }, + "type": "dict", + }, + "to_zone": { + "options": { + "any": { + "type": "bool", + }, + "junos_host": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "url_category": { + "options": { + "any": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + "none": { + "type": "bool", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "name": {"type": "str"}, + "scheduler_name": {"type": "str"}, + "then": { + "options": { + "count": {"type": "bool"}, + "deny": {"type": "bool"}, + "log": { + "options": { + "session_close": { + "type": "bool", + }, + "session_init": { + "type": "bool", + }, + }, + "type": "dict", + }, + "permit": { + "options": { + "application_services": { + "options": { + "advanced_anti_malware_policy": { + "type": "str", + }, + "application_firewalls": { + "elements": "dict", + "options": { + "rule_set": { + "type": "str", + }, + }, + "type": "list", + }, + "application_traffic_control_rule_set": { + "type": "str", + }, + "gprs_gtp_profile": { + "type": "str", + }, + "gprs_sctp_profile": { + "type": "str", + }, + "icap_redirect": { + "type": "str", + }, + "idp": { + "type": "bool", + }, + "idp_policy": { + "type": "str", + }, + "packet_capture": { + "type": "bool", + }, + "redirect_wx": { + "type": "bool", + }, + "reverse_redirect_wx": { + "type": "bool", + }, + "security_intelligence": { + "options": { + "add_destination_identity_to_feed": { + "type": "str", + }, + "add_destination_ip_to_feed": { + "type": "str", + }, + "add_source_identity_to_feed": { + "type": "str", + }, + "add_source_ip_to_feed": { + "type": "str", + }, + }, + "type": "dict", + }, + "security_intelligence_policy": { + "type": "str", + }, + "ssl_proxy": { + "options": { + "enable": { + "type": "bool", + }, + "profile_name": { + "type": "str", + }, + }, + "type": "dict", + }, + "uac_policy": { + "options": { + "captive_portal": { + "type": "str", + }, + "enable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "utm_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + "destination_address": { + "choices": [ + "drop-translated", + "drop-untranslated", + ], + "type": "str", + }, + "firewall_authentication": { + "options": { + "pass_through": { + "no_log": True, + "options": { + "access_profile": { + "type": "str", + }, + "auth_only_browser": { + "type": "bool", + }, + "auth_user_agent": { + "type": "str", + }, + "client_match": { + "type": "str", + }, + "ssl_termination_profile": { + "type": "str", + }, + "web_redirect": { + "type": "bool", + }, + "web_redirect_to_https": { + "type": "bool", + }, + }, + "type": "dict", + }, + "push_to_identity_management": { + "type": "bool", + }, + "user_firewall": { + "options": { + "access_profile": { + "type": "str", + }, + "auth_only_browser": { + "type": "bool", + }, + "auth_user_agent": { + "type": "str", + }, + "domain": { + "type": "str", + }, + "ssl_termination_profile": { + "type": "str", + }, + "web_redirect": { + "type": "bool", + }, + "web_redirect_to_https": { + "type": "bool", + }, + }, + "type": "dict", + }, + "web_authentication": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "tcp_options": { + "options": { + "initial_tcp_mss": { + "type": "int", + }, + "reverse_tcp_mss": { + "type": "int", + }, + "sequence_check_required": { + "type": "bool", + }, + "syn_check_required": { + "type": "bool", + }, + "window_scale": { + "type": "bool", + }, + }, + "type": "dict", + }, + "tunnel": { + "options": { + "ipsec_vpn": { + "type": "str", + }, + "pair_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "reject": { + "options": { + "enable": { + "type": "bool", + }, + "profile": { + "type": "str", + }, + "ssl_proxy": { + "options": { + "enable": { + "type": "bool", + }, + "profile_name": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "global": { + "options": { + "policies": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "match": { + "options": { + "application": { + "options": { + "any": {"type": "bool"}, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "destination_address": { + "options": { + "addresses": { + "elements": "str", + "type": "list", + }, + "any": {"type": "bool"}, + "any_ipv4": {"type": "bool"}, + "any_ipv6": {"type": "bool"}, + }, + "type": "dict", + }, + "destination_address_excluded": { + "type": "bool", + }, + "dynamic_application": { + "options": { + "any": {"type": "bool"}, + "names": { + "elements": "str", + "type": "list", + }, + "none": {"type": "bool"}, + }, + "type": "dict", + }, + "from_zone": { + "options": { + "any": {"type": "bool"}, + "junos_host": {"type": "bool"}, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "source_address": { + "options": { + "addresses": { + "elements": "str", + "type": "list", + }, + "any": {"type": "bool"}, + "any_ipv4": {"type": "bool"}, + "any_ipv6": {"type": "bool"}, + }, + "type": "dict", + }, + "source_address_excluded": { + "type": "bool", + }, + "source_end_user_profile": { + "type": "str", + }, + "source_identity": { + "options": { + "any": {"type": "bool"}, + "authenticated_user": { + "type": "bool", + }, + "names": { + "elements": "str", + "type": "list", + }, + "unauthenticated_user": { + "type": "bool", + }, + "unknown_user": { + "type": "bool", + }, + }, + "type": "dict", + }, + "to_zone": { + "options": { + "any": {"type": "bool"}, + "junos_host": {"type": "bool"}, + "names": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "url_category": { + "options": { + "any": {"type": "bool"}, + "names": { + "elements": "str", + "type": "list", + }, + "none": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "name": {"type": "str"}, + "scheduler_name": {"type": "str"}, + "then": { + "options": { + "count": {"type": "bool"}, + "deny": {"type": "bool"}, + "log": { + "options": { + "session_close": { + "type": "bool", + }, + "session_init": { + "type": "bool", + }, + }, + "type": "dict", + }, + "permit": { + "options": { + "application_services": { + "options": { + "advanced_anti_malware_policy": { + "type": "str", + }, + "application_firewalls": { + "elements": "dict", + "options": { + "rule_set": { + "type": "str", + }, + }, + "type": "list", + }, + "application_traffic_control_rule_set": { + "type": "str", + }, + "gprs_gtp_profile": { + "type": "str", + }, + "gprs_sctp_profile": { + "type": "str", + }, + "icap_redirect": { + "type": "str", + }, + "idp": { + "type": "bool", + }, + "idp_policy": { + "type": "str", + }, + "packet_capture": { + "type": "bool", + }, + "redirect_wx": { + "type": "bool", + }, + "reverse_redirect_wx": { + "type": "bool", + }, + "security_intelligence": { + "options": { + "add_destination_identity_to_feed": { + "type": "str", + }, + "add_destination_ip_to_feed": { + "type": "str", + }, + "add_source_identity_to_feed": { + "type": "str", + }, + "add_source_ip_to_feed": { + "type": "str", + }, + }, + "type": "dict", + }, + "security_intelligence_policy": { + "type": "str", + }, + "ssl_proxy": { + "options": { + "enable": { + "type": "bool", + }, + "profile_name": { + "type": "str", + }, + }, + "type": "dict", + }, + "uac_policy": { + "options": { + "captive_portal": { + "type": "str", + }, + "enable": { + "type": "bool", + }, + }, + "type": "dict", + }, + "utm_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + "destination_address": { + "choices": [ + "drop-translated", + "drop-untranslated", + ], + "type": "str", + }, + "firewall_authentication": { + "options": { + "pass_through": { + "no_log": True, + "options": { + "access_profile": { + "type": "str", + }, + "auth_only_browser": { + "type": "bool", + }, + "auth_user_agent": { + "type": "str", + }, + "client_match": { + "type": "str", + }, + "ssl_termination_profile": { + "type": "str", + }, + "web_redirect": { + "type": "bool", + }, + "web_redirect_to_https": { + "type": "bool", + }, + }, + "type": "dict", + }, + "push_to_identity_management": { + "type": "bool", + }, + "user_firewall": { + "options": { + "access_profile": { + "type": "str", + }, + "auth_only_browser": { + "type": "bool", + }, + "auth_user_agent": { + "type": "str", + }, + "domain": { + "type": "str", + }, + "ssl_termination_profile": { + "type": "str", + }, + "web_redirect": { + "type": "bool", + }, + "web_redirect_to_https": { + "type": "bool", + }, + }, + "type": "dict", + }, + "web_authentication": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "tcp_options": { + "options": { + "initial_tcp_mss": { + "type": "int", + }, + "reverse_tcp_mss": { + "type": "int", + }, + "sequence_check_required": { + "type": "bool", + }, + "syn_check_required": { + "type": "bool", + }, + "window_scale": { + "type": "bool", + }, + }, + "type": "dict", + }, + "tunnel": { + "options": { + "ipsec_vpn": { + "type": "str", + }, + "pair_policy": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "reject": { + "options": { + "enable": {"type": "bool"}, + "profile": {"type": "str"}, + "ssl_proxy": { + "options": { + "enable": { + "type": "bool", + }, + "profile_name": { + "type": "str", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/security_policies_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/security_policies_global.py new file mode 100644 index 000000000..cac6c3466 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_policies_global/security_policies_global.py @@ -0,0 +1,129 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_security_policies_global module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Security_policies_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_security_policies_global module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "default_policy": { + "choices": ["deny-all", "permit-all"], + "type": "str", + }, + "policy_rematch": { + "options": { + "enable": {"type": "bool"}, + "extensive": {"type": "bool"}, + }, + "type": "dict", + }, + "policy_stats": { + "options": { + "enable": {"type": "bool"}, + "system_wide": {"type": "bool"}, + }, + "type": "dict", + }, + "pre_id_default_policy_action": { + "options": { + "log": { + "options": { + "session_close": {"type": "bool"}, + "session_init": {"type": "bool"}, + }, + "type": "dict", + }, + "session_timeout": { + "options": { + "icmp": {"type": "int"}, + "icmp6": {"type": "int"}, + "ospf": {"type": "int"}, + "others": {"type": "int"}, + "tcp": {"type": "int"}, + "udp": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "traceoptions": { + "options": { + "file": { + "options": { + "files": {"type": "int"}, + "match": {"type": "str"}, + "no_world_readable": {"type": "bool"}, + "size": {"type": "str"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "flag": { + "choices": [ + "all", + "configuration", + "compilation", + "ipc", + "lookup", + "routing-socket", + "rules", + ], + "type": "str", + }, + "no_remote_trace": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/security_zones.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/security_zones.py new file mode 100644 index 000000000..9d9bc4830 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/security_zones/security_zones.py @@ -0,0 +1,192 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_security_zones module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Security_zonesArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_security_zones module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "functional_zone_management": { + "options": { + "description": {"type": "str"}, + "host_inbound_traffic": { + "options": { + "protocols": { + "elements": "dict", + "options": { + "except": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + "system_services": { + "elements": "dict", + "options": { + "except": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "interfaces": {"elements": "str", "type": "list"}, + "screen": {"type": "str"}, + }, + "type": "dict", + }, + "zones": { + "elements": "dict", + "options": { + "address_book": { + "options": { + "address_sets": { + "elements": "dict", + "options": { + "address_sets": { + "elements": "str", + "type": "list", + }, + "addresses": { + "elements": "str", + "type": "list", + }, + "description": {"type": "str"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + "addresses": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "dns_name": { + "options": { + "ipv4_only": {"type": "bool"}, + "ipv6_only": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "dict", + }, + "ip_prefix": {"type": "str"}, + "name": {"type": "str"}, + "range_address": { + "options": { + "from": {"type": "str"}, + "to": {"type": "str"}, + }, + "type": "dict", + }, + "wildcard_address": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "advance_policy_based_routing_profile": { + "type": "str", + }, + "advanced_connection_tracking": { + "options": { + "mode": { + "choices": [ + "allow-any-host", + "allow-target-host", + "allow-target-host-port", + ], + "type": "str", + }, + "timeout": {"type": "int"}, + "track_all_policies_to_this_zone": { + "type": "bool", + }, + }, + "type": "dict", + }, + "application_tracking": {"type": "bool"}, + "description": {"type": "str"}, + "enable_reverse_reroute": {"type": "bool"}, + "host_inbound_traffic": { + "options": { + "protocols": { + "elements": "dict", + "options": { + "except": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + "system_services": { + "elements": "dict", + "options": { + "except": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "interfaces": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "screen": {"type": "str"}, + "source_identity_log": {"type": "bool"}, + "tcp_rst": {"type": "bool"}, + "unidirectional_session_refreshing": {"type": "bool"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/snmp_server.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/snmp_server.py new file mode 100644 index 000000000..d334f4ecc --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/snmp_server/snmp_server.py @@ -0,0 +1,700 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_snmp_server module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Snmp_serverArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_snmp_server module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "arp": { + "options": { + "host_name_resolution": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "client_lists": { + "elements": "dict", + "options": { + "addresses": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "restrict": {"type": "bool"}, + }, + "type": "list", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "communities": { + "elements": "dict", + "options": { + "authorization": { + "choices": ["read-only", "read-write"], + "type": "str", + }, + "client_list_name": {"type": "str"}, + "clients": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "restrict": {"type": "bool"}, + }, + "type": "list", + }, + "logical_system": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "routing_instances": { + "elements": "dict", + "options": { + "client_list_name": {"type": "str"}, + "clients": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "restrict": {"type": "bool"}, + }, + "type": "list", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "view": {"type": "str"}, + }, + "type": "list", + }, + "contact": {"type": "str"}, + "customization": { + "options": {"ether_stats_ifd_only": {"type": "bool"}}, + "type": "dict", + }, + "description": {"type": "str"}, + "engine_id": { + "options": { + "local": {"type": "str"}, + "use_default_ip_address": {"type": "bool"}, + "use_mac_address": {"type": "bool"}, + }, + "type": "dict", + }, + "filter_duplicates": {"type": "bool"}, + "filter_interfaces": { + "options": { + "all_internal_interfaces": {"type": "bool"}, + "interfaces": {"elements": "str", "type": "list"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "health_monitor": { + "options": { + "falling_threshold": {"type": "int"}, + "idp": {"type": "bool"}, + "interval": {"type": "int"}, + "rising_threshold": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "if_count_with_filter_interfaces": {"type": "bool"}, + "interfaces": {"elements": "str", "type": "list"}, + "location": {"type": "str"}, + "logical_system_trap_filter": {"type": "bool"}, + "name": {"type": "str"}, + "nonvolatile": { + "options": {"commit_delay": {"type": "int"}}, + "type": "dict", + }, + "proxies": { + "elements": "dict", + "options": { + "device_name": {"type": "str"}, + "logical_system": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "routing_instances": { + "elements": "dict", + "options": { + "client_list_name": {"type": "str"}, + "clients": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "restrict": {"type": "bool"}, + }, + "type": "list", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "version_v1": { + "options": { + "no_default_comm_to_v3_config": { + "type": "bool", + }, + "snmp_community": {"type": "str"}, + }, + "type": "dict", + }, + "version_v2c": { + "options": { + "no_default_comm_to_v3_config": { + "type": "bool", + }, + "snmp_community": {"type": "str"}, + }, + "type": "dict", + }, + "version_v3": { + "options": { + "context": {"type": "bool"}, + "security_name": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "rmon": { + "options": { + "alarms": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "falling_event_index": {"type": "int"}, + "falling_threshold": {"type": "int"}, + "falling_threshold_interval": {"type": "int"}, + "id": {"type": "str"}, + "interval": {"type": "int"}, + "request_type": { + "choices": [ + "get-next-request", + "get-request", + "walk-request", + ], + "type": "str", + }, + "rising_event_index": {"type": "int"}, + "rising_threshold": {"type": "int"}, + "sample_type": { + "choices": [ + "absolute-value", + "delta-value", + ], + "type": "str", + }, + "startup_alarm": { + "choices": [ + "falling-alarm", + "rising-alarm", + "rising-or-falling-alarm", + ], + "type": "str", + }, + "syslog_subtag": {"type": "str"}, + "variable": {"type": "str"}, + }, + "type": "list", + }, + "events": { + "elements": "dict", + "options": { + "community": {"type": "str"}, + "description": {"type": "str"}, + "id": {"type": "int"}, + "type": { + "choices": [ + "log", + "log-and-trap", + "none", + "snmptrap", + ], + "type": "str", + }, + }, + "type": "list", + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "routing_instance_access": { + "options": { + "access_lists": {"elements": "str", "type": "list"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "snmp_v3": { + "options": { + "notify": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "tag": {"type": "str"}, + "type": {"type": "str"}, + }, + "type": "list", + }, + "notify_filter": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "oids": { + "elements": "dict", + "options": { + "exclude": {"type": "bool"}, + "include": {"type": "bool"}, + "oid": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + "snmp_community": { + "elements": "dict", + "options": { + "community_index": {"type": "str"}, + "community_name": {"type": "str"}, + "context": {"type": "str"}, + "security_name": {"type": "str"}, + "tag": {"type": "str"}, + }, + "type": "list", + }, + "target_addresses": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "address_mask": {"type": "str"}, + "logical_system": {"type": "str"}, + "name": {"type": "str"}, + "port": {"type": "int"}, + "retry_count": {"type": "int"}, + "routing_instance": {"type": "str"}, + "tag_list": {"type": "str"}, + "target_parameters": {"type": "str"}, + "timeout": {"type": "int"}, + }, + "type": "list", + }, + "target_parameters": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "notify_filter": {"type": "str"}, + "parameters": { + "options": { + "message_processing_model": { + "choices": ["v1", "v2c", "v3"], + "type": "str", + }, + "security_level": { + "choices": [ + "authentication", + "none", + "privacy", + ], + "type": "str", + }, + "security_model": { + "choices": ["usm", "v1", "v2c"], + "type": "str", + }, + "security_name": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "usm": { + "options": { + "local_engine": { + "options": { + "users": { + "elements": "dict", + "options": { + "authentication_md5": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "authentication_none": { + "type": "bool", + }, + "authentication_sha": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "name": {"type": "str"}, + "privacy_3des": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_aes128": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_des": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_none": { + "type": "bool", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "remote_engine": { + "elements": "dict", + "options": { + "id": {"type": "str"}, + "users": { + "elements": "dict", + "options": { + "authentication_md5": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "authentication_none": { + "type": "bool", + }, + "authentication_sha": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "name": {"type": "str"}, + "privacy_3des": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_aes128": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_des": { + "options": { + "key": { + "type": "str", + "no_log": False, + }, + "password": { + "type": "str", + "no_log": False, + }, + }, + "type": "dict", + }, + "privacy_none": { + "type": "bool", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "subagent": { + "options": { + "tcp": { + "options": { + "routing_instances_default": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "traceoptions": { + "options": { + "file": { + "options": { + "files": {"type": "int"}, + "match": {"type": "str"}, + "no_world_readable": {"type": "bool"}, + "size": {"type": "int"}, + "world_readable": {"type": "bool"}, + }, + "type": "dict", + }, + "flag": { + "options": { + "all": {"type": "bool"}, + "general": {"type": "bool"}, + "interface_stats": {"type": "bool"}, + "nonvolatile_sets": {"type": "bool"}, + "pdu": {"type": "bool"}, + "protocol_timeouts": {"type": "bool"}, + "routing_socket": {"type": "bool"}, + "subagent": {"type": "bool"}, + "timer": {"type": "bool"}, + "varbind_error": {"type": "bool"}, + }, + "type": "dict", + }, + "memory_trace": { + "options": { + "set": {"type": "bool"}, + "size": {"type": "int"}, + }, + "type": "dict", + }, + "no_remote_trace": {"type": "bool"}, + }, + "type": "dict", + }, + "trap_groups": { + "elements": "dict", + "options": { + "categories": { + "options": { + "authentication": {"type": "bool"}, + "chassis": {"type": "bool"}, + "chassis_cluster": {"type": "bool"}, + "configuration": {"type": "bool"}, + "dot3oam_events": {"type": "bool"}, + "link": {"type": "bool"}, + "otn_alarms": { + "options": { + "oc_lof": {"type": "bool"}, + "oc_lom": {"type": "bool"}, + "oc_los": {"type": "bool"}, + "odu_ais": {"type": "bool"}, + "odu_bbe_threshold": {"type": "bool"}, + "odu_bdi": {"type": "bool"}, + "odu_bdodu_es_threshold": { + "type": "bool", + }, + "odu_lck": {"type": "bool"}, + "odu_oci": {"type": "bool"}, + "odu_rx_aps_change": {"type": "bool"}, + "odu_sd": {"type": "bool"}, + "odu_ses_threshold": {"type": "bool"}, + "odu_sf": {"type": "bool"}, + "odu_ttim": {"type": "bool"}, + "odu_uas_threshold": {"type": "bool"}, + "opu_ptm": {"type": "bool"}, + "otu_ais": {"type": "bool"}, + "otu_bbe_threshold": {"type": "bool"}, + "otu_bdi": {"type": "bool"}, + "otu_es_threshold": {"type": "bool"}, + "otu_fec_deg": {"type": "bool"}, + "otu_fec_exe": {"type": "bool"}, + "otu_iae": {"type": "bool"}, + "otu_sd": {"type": "bool"}, + "otu_ses_threshold": {"type": "bool"}, + "otu_sf": {"type": "bool"}, + "otu_ttim": {"type": "bool"}, + "otu_uas_threshold": {"type": "bool"}, + "set": {"type": "bool"}, + "wavelength_lock": {"type": "bool"}, + }, + "type": "dict", + }, + "remote_operations": {"type": "bool"}, + "rmon_alarm": {"type": "bool"}, + "routing": {"type": "bool"}, + "services": {"type": "bool"}, + "startup": {"type": "bool"}, + "vrrp_events": {"type": "bool"}, + }, + "type": "dict", + }, + "destination_port": {"type": "int"}, + "logical_system": {"elements": "str", "type": "list"}, + "name": {"type": "str"}, + "routing_instance": {"type": "str"}, + "targets": {"elements": "str", "type": "list"}, + "version": { + "choices": ["all", "v1", "v2"], + "type": "str", + }, + }, + "type": "list", + }, + "trap_options": { + "options": { + "agent_address": { + "options": { + "outgoing_interface": {"type": "bool"}, + }, + "type": "dict", + }, + "context_oid": {"type": "bool"}, + "enterprise_oid": {"type": "bool"}, + "logical_system": {"elements": "str", "type": "list"}, + "routing_instance": {"type": "str"}, + "set": {"type": "bool"}, + "source_address": { + "options": { + "address": {"type": "str"}, + "lowest_loopback": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "views": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "oids": { + "elements": "dict", + "options": { + "exclude": {"type": "bool"}, + "include": {"type": "bool"}, + "oid": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/static_routes.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/static_routes.py new file mode 100644 index 000000000..34c01e8a5 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/static_routes/static_routes.py @@ -0,0 +1,90 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the junos_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 junos_static_routes module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "address_families": { + "elements": "dict", + "options": { + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + "routes": { + "elements": "dict", + "options": { + "dest": {"type": "str"}, + "metric": {"type": "int"}, + "next_hop": { + "elements": "dict", + "options": { + "forward_router_address": { + "type": "str", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "vrf": {"type": "str"}, + }, + "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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/vlans.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/vlans.py new file mode 100644 index 000000000..41eaa8d5c --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/argspec/vlans/vlans.py @@ -0,0 +1,65 @@ +# +# -*- 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 junos_vlans module +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class VlansArgs(object): # pylint: disable=R0903 + """The arg spec for the junos_vlans module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "description": {}, + "name": {"required": True, "type": "str"}, + "vlan_id": {"type": "int"}, + "l3_interface": {"type": "str"}, + }, + "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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/acl_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..67f5fe309 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acl_interfaces/acl_interfaces.py @@ -0,0 +1,260 @@ +# +# -*- 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 junos_acl_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Acl_interfaces(ConfigBase): + """ + The junos_acl_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["acl_interfaces"] + + def __init__(self, module): + super(Acl_interfaces, self).__init__(module) + + def get_acl_interfaces_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + acl_interfaces_facts = facts["ansible_network_resources"].get( + "junos_acl_interfaces", + ) + if not acl_interfaces_facts: + return [] + return acl_interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_acl_interfaces_facts = self.get_acl_interfaces_facts() + else: + existing_acl_interfaces_facts = [] + if state == "gathered": + existing_acl_interfaces_facts = self.get_acl_interfaces_facts() + result["gathered"] = existing_acl_interfaces_facts + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_acl_interfaces_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_acl_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_acl_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_acl_interfaces_facts = self.get_acl_interfaces_facts() + + result["before"] = existing_acl_interfaces_facts + if result["changed"]: + result["after"] = changed_acl_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_acl_interfaces_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_acl_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _get_common_xml_node(self, name): + root_node = build_root_xml_node("interface") + build_child_xml_node(root_node, "name", name) + intf_unit_node = build_child_xml_node(root_node, "unit") + return root_node, intf_unit_node + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acl_intf_xml = [] + acl_intf_xml.extend(self._state_deleted(want, have)) + acl_intf_xml.extend(self._state_merged(want, have)) + return acl_intf_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acl_intf_xml = [] + acl_intf_xml.extend(self._state_deleted(have, have)) + acl_intf_xml.extend(self._state_merged(want, have)) + return acl_intf_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acl_intf_xml = [] + delete = {"delete": "delete"} + + if not want: + want = have + + acl_intf_xml = self._state_merged(want, have, delete=delete) + return acl_intf_xml + + def _state_merged(self, want, have, delete=None): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + + acl_intf_xml = [] + + for config in want: + root_node, unit_node = self._get_common_xml_node(config["name"]) + build_child_xml_node(unit_node, "name", "0") + family_node = build_child_xml_node(unit_node, "family") + for acl_filter in config["access_groups"]: + inet_family = "inet" + if acl_filter["afi"] == "ipv6": + inet_family = "inet6" + inet_node = build_child_xml_node(family_node, inet_family) + if acl_filter.get("acls"): + filter_node = build_child_xml_node(inet_node, "filter") + for acl in acl_filter["acls"]: + acl_node = None + if acl["direction"] == "in": + acl_node = build_child_xml_node( + filter_node, + "input-list", + acl["name"], + ) + else: + acl_node = build_child_xml_node( + filter_node, + "output-list", + acl["name"], + ) + if delete: + acl_node.attrib.update(delete) + elif delete: + build_child_xml_node(inet_node, "filter", None, delete) + acl_intf_xml.append(root_node) + return acl_intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/acls.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/acls.py new file mode 100644 index 000000000..540ffe271 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/acls/acls.py @@ -0,0 +1,441 @@ +# +# -*- 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 junos_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.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Acls(ConfigBase): + """ + The junos_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("junos_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} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES: + existing_acls_facts = self.get_acls_facts() + else: + existing_acls_facts = {} + if state == "gathered": + existing_acls_facts = self.get_acls_facts() + result["gathered"] = existing_acls_facts + 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) + elif self.state == "rendered": + config_xmls = self.set_config(existing_acls_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + diff = None + config_xmls = self.set_config(existing_acls_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_acls_facts = self.get_acls_facts() + + result["before"] = existing_acls_facts + if result["changed"]: + result["after"] = 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 + """ + root = build_root_xml_node("firewall") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acls_xml = [] + acls_xml.extend(self._state_deleted(want, have)) + acls_xml.extend(self._state_merged(want, have)) + return acls_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acls_xml = [] + acls_xml.extend(self._state_deleted(have, have)) + acls_xml.extend(self._state_merged(want, have)) + return acls_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acls_xml = [] + family_node = build_root_xml_node("family") + delete = dict(delete="delete") + + if not want: + want = have + + for config in want: + try: + family = "inet6" if config.get("afi") == "ipv6" else "inet" + except KeyError: + family = "inet" + inet_node = build_child_xml_node(family_node, family) + + # Look deeply into have to match replace correctly + existing_acls = [] + for conf in have: + if conf.get("afi") == config.get("afi"): + existing_acls.extend(conf["acls"] or []) + acl_names = [acl["name"] for acl in existing_acls] + + if not config["acls"]: + inet_node.attrib.update(delete) + continue + + for acl in config["acls"]: + if acl["name"] not in acl_names: + continue + + filter_node = build_child_xml_node(inet_node, "filter") + build_child_xml_node(filter_node, "name", acl["name"]) + if not acl.get("aces"): + filter_node.attrib.update(delete) + continue + + for ace in acl["aces"]: + # if ace["name"] not in ace_names: + term_node = build_child_xml_node(filter_node, "term") + build_child_xml_node(term_node, "name", ace["name"]) + term_node.attrib.update(delete) + + acls_xml.append(family_node) + return acls_xml + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + acls_xml = [] + family_node = build_root_xml_node("family") + for config in want: + try: + family = "inet6" if config.pop("afi") == "ipv6" else "inet" + except KeyError: + family = "inet" + inet_node = build_child_xml_node(family_node, family) + + for acl in config.get("acls") or []: + filter_node = build_child_xml_node(inet_node, "filter") + build_child_xml_node(filter_node, "name", acl["name"]) + for ace in acl.get("aces") or []: + term_node = build_child_xml_node(filter_node, "term") + build_child_xml_node(term_node, "name", ace["name"]) + + if ace.get("source") or ace.get("destination") or ace.get("protocol"): + from_node = build_child_xml_node(term_node, "from") + for direction in ("source", "destination"): + if ace.get(direction): + if ace[direction].get("address"): + addresses = ace[direction]["address"] + if not isinstance(addresses, list): + addresses = [addresses] + for address in addresses: + build_child_xml_node( + from_node, + "{0}-address".format(direction), + address, + ) + if ace[direction].get("prefix_list"): + for prefix in ace[direction].get( + "prefix_list", + ): + build_child_xml_node( + from_node, + "{0}-prefix-list".format( + direction, + ), + prefix["name"], + ) + if ace[direction].get("port_protocol"): + if "eq" in ace[direction]["port_protocol"]: + build_child_xml_node( + from_node, + "{0}-port".format(direction), + ace[direction]["port_protocol"]["eq"], + ) + elif "range" in ace[direction]["port_protocol"]: + ports = "{0}-{1}".format( + ace[direction]["port_protocol"]["start"], + ace[direction]["port_protocol"]["end"], + ) + build_child_xml_node( + from_node, + "{0}-port".format(direction), + ports, + ) + if ace.get("protocol"): + build_child_xml_node( + from_node, + "protocol", + ace["protocol"], + ) + if ace.get("protocol_options"): + if ace["protocol_options"].get("icmp"): + icmp_code = build_child_xml_node( + from_node, + "icmp-code", + ) + icmp_type = build_child_xml_node( + from_node, + "icmp-type", + ) + icmp = ace["protocol_options"]["icmp"] + if "dod_host_prohibited" in icmp: + build_child_xml_node( + icmp_code, + "destination-host-prohibited", + ) + if "dod_net_prohibited" in icmp: + build_child_xml_node( + icmp_code, + "destination-network-prohibited", + ) + if "echo" in icmp: + build_child_xml_node( + icmp_type, + "echo-request", + ) + if "echo_reply" in icmp: + build_child_xml_node( + icmp_type, + "echo-reply", + ) + if "host_tos_unreachable" in icmp: + build_child_xml_node( + icmp_code, + "host-unreachable-for-tos", + ) + if "host_redirect" in icmp: + build_child_xml_node( + icmp_code, + "redirect-for-host", + ) + if "host_tos_redirect" in icmp: + build_child_xml_node( + icmp_code, + "redirect-for-host-and-tos", + ) + if "host_unknown" in icmp: + build_child_xml_node( + icmp_code, + "destination-host-unknown", + ) + if "host_unreachable" in icmp: + build_child_xml_node( + icmp_code, + "host-unreachable", + ) + if "net_redirect" in icmp: + build_child_xml_node( + icmp_code, + "redirect-for-network", + ) + if "net_tos_redirect" in icmp: + build_child_xml_node( + icmp_code, + "redirect-for-tos-and-net", + ) + if "network_unknown" in icmp: + build_child_xml_node( + icmp_code, + "destination-network-unknown", + ) + if "port_unreachable" in icmp: + build_child_xml_node( + icmp_code, + "port-unreachable", + ) + if "protocol_unreachable" in icmp: + build_child_xml_node( + icmp_code, + "protocol-unreachable", + ) + if "reassembly_timeout" in icmp: + build_child_xml_node( + icmp_code, + "ttl-eq-zero-during-reassembly", + ) + if "redirect" in icmp: + build_child_xml_node(icmp_type, "redirect") + if "router_advertisement" in icmp: + build_child_xml_node( + icmp_type, + "router-advertisement", + ) + if "router_solicitation" in icmp: + build_child_xml_node( + icmp_type, + "router-solicit", + ) + if "source_route_failed" in icmp: + build_child_xml_node( + icmp_code, + "source-route-failed", + ) + if "time_exceeded" in icmp: + build_child_xml_node( + icmp_type, + "time-exceeded", + ) + if "ttl_exceeded" in icmp: + build_child_xml_node( + icmp_code, + "ttl-eq-zero-during-transit", + ) + if ace.get("grant"): + then_node = build_child_xml_node(term_node, "then") + if ace["grant"] == "permit": + build_child_xml_node(then_node, "accept") + if ace["grant"] == "deny": + build_child_xml_node(then_node, "discard") + + acls_xml.append(family_node) + return acls_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/bgp_address_family.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..064753ffb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_address_family/bgp_address_family.py @@ -0,0 +1,917 @@ +# +# -*- 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 junos_bgp_address_family 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Bgp_address_family(ConfigBase): + """ + The junos_bgp_address_family class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["bgp_address_family"] + + def __init__(self, module): + super(Bgp_address_family, self).__init__(module) + + def get_bgp_address_family_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, + ) + bgp_facts = facts["ansible_network_resources"].get( + "bgp_address_family", + ) + if not bgp_facts: + return {} + return bgp_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_bgp_address_family_facts = self.get_bgp_address_family_facts() + else: + existing_bgp_address_family_facts = {} + if state == "gathered": + existing_bgp_address_family_facts = self.get_bgp_address_family_facts() + result["gathered"] = existing_bgp_address_family_facts + 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_bgp_address_family_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_bgp_address_family_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + diff = None + config_xmls = self.set_config(existing_bgp_address_family_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_bgp_address_family_facts = self.get_bgp_address_family_facts() + + result["before"] = existing_bgp_address_family_facts + if result["changed"]: + result["after"] = changed_bgp_address_family_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_bgp_address_family_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_bgp_address_family_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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + self.autonomous_system = None + self.root = build_root_xml_node("configuration") + self.protocols = build_child_xml_node(self.root, "protocols") + self.bgp = build_child_xml_node(self.protocols, "bgp") + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state == "purged": + config_xmls = self._state_purged(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + elif state == "overridden": + config_xmls = self._state_overridden(want, have) + + for xml in config_xmls: + self.bgp.append(xml) + cfg_lst = [] + + if config_xmls: + for xml in self.root.getchildren(): + xml = tostring(xml) + cfg_lst.append(xml) + return cfg_lst + + def _state_replaced(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + bgp_xml = [] + bgp_xml.extend(self._state_deleted(want, have)) + bgp_xml.extend(self._state_merged(want, have)) + + return bgp_xml + + def _state_merged(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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + family_xml = [] + + family_root = build_root_xml_node("family") + + want = remove_empties(want) + + # Generate xml node for autonomous-system + if want.get("as_number"): + build_child_xml_node( + self.routing_options, + "autonomous-system", + want.get("as_number"), + ) + w_af_list = want.get("address_family") + # render global address family attribute commands + self.render_af(w_af_list, family_root) + + # render commands for group address family attribute commands + if "groups" in want.keys(): + groups = want.get("groups") + for group in groups: + groups_node = build_root_xml_node("group") + build_child_xml_node(groups_node, "name", group["name"]) + g_family_root = build_child_xml_node(groups_node, "family") + w_gaf_list = group.get("address_family") + self.render_af(w_gaf_list, g_family_root) + + # render neighbor address-family commands + if "neighbors" in group.keys(): + neighbors = group.get("neighbors") + for neighbor in neighbors: + neighbors_node = build_child_xml_node( + groups_node, + "neighbor", + ) + build_child_xml_node( + neighbors_node, + "name", + neighbor["neighbor_address"], + ) + n_family_root = build_child_xml_node( + neighbors_node, + "family", + ) + w_naf_list = neighbor.get("address_family") + self.render_af(w_naf_list, n_family_root) + + family_xml.append(groups_node) + + family_xml.append(family_root) + + return family_xml + + def render_af(self, w_af_list, family_root): + if w_af_list: + for waf in w_af_list: + # Add the nlri node + nlri_node = build_child_xml_node(family_root, waf["afi"]) + # Read nlri_types list + nlri_types = waf.get("af_type") + for type in nlri_types: + # Add the node for nlri type + type_node = build_child_xml_node(nlri_node, type["type"]) + # Add node for accepted-prefix-limit + if "accepted_prefix_limit" in type.keys(): + apl = type.get("accepted_prefix_limit") + # build node for accepted-prefix-limit + apl_node = build_child_xml_node( + type_node, + "accepted-prefix-limit", + ) + # Add node for maximum + if "maximum" in apl.keys(): + build_child_xml_node( + apl_node, + "maximum", + apl["maximum"], + ) + # Add node for teardown + td_node = None + if "limit_threshold" in apl.keys(): + td_node = build_child_xml_node( + apl_node, + "teardown", + ) + # add node for limit-threshold + build_child_xml_node( + td_node, + "limit-threshold", + apl.get("limit_threshold"), + ) + elif "teardown" in apl.keys(): + td_node = build_child_xml_node( + apl_node, + "teardown", + ) + it_node = None + # Add node for teardown idle_timeout + if "idle_timeout_value" in apl.keys(): + it_node = build_child_xml_node( + td_node, + "idle-timeout", + ) + # add node for timeout + build_child_xml_node( + it_node, + "timeout", + apl.get("idle_timeout_value"), + ) + + elif "forever" in apl.keys(): + if it_node is None: + it_node = build_child_xml_node( + td_node, + "idle-timeout", + ) + if it_node is not None: + it_node = build_child_xml_node( + td_node, + "idle-timeout", + ) + # add forever node + build_child_xml_node(it_node, "forever") + + # Add node for add-path + if "add_path" in type.keys(): + ap = type.get("add_path") + # build node for add_path + ap_node = build_child_xml_node(type_node, "add-path") + # add node for receive + if "receive" in ap.keys(): + build_child_xml_node(ap_node, "receive") + if "send" in ap.keys(): + # add node for send + send = ap.get("send") + send_node = build_child_xml_node(ap_node, "send") + # add node for path_count + if "path_count" in send.keys(): + build_child_xml_node( + send_node, + "path-count", + send.get("path_count"), + ) + # add node for include_backup_path + if "include_backup_path" in send.keys(): + build_child_xml_node( + send_node, + "include-backup-path", + send.get("include_backup_path"), + ) + # add node for path_selection_mode + if "path_selection_mode" in send.keys(): + psm = send.get("path_selection_mode") + psm_node = build_child_xml_node( + send_node, + "path-selection-mode", + ) + # add node for all_paths + if "all_paths" in psm.keys(): + build_child_xml_node(psm_node, "all-paths") + # add node for equal_cost_paths + if "equal_cost_paths" in psm.keys(): + build_child_xml_node( + psm_node, + "equal-cost-paths", + ) + # add node for prefix_policy + if "prefix_policy" in send.keys(): + build_child_xml_node( + send_node, + "prefix-policy", + send.get("prefix_policy"), + ) + # Add node for aggregate_label + if "aggregate_label" in type.keys(): + al = type.get("aggregate_label") + # build node for aggregate_label + al_node = build_child_xml_node( + type_node, + "aggregate_label", + ) + # add node community + if "community" in al.keys(): + build_child_xml_node( + al_node, + "community", + al.get("community"), + ) + + # Add node for aigp + if "aigp" in type.keys(): + aigp = type.get("aigp") + # build node for aigp + if "disable" in aigp.keys(): + aigp_node = build_child_xml_node(type_node, "aigp") + build_child_xml_node(aigp_node, "disable") + else: + build_child_xml_node(type_node, "aigp") + + # Add node for damping + if "damping" in type.keys(): + build_child_xml_node(type_node, "damping") + + # Add node for defer_initial_multipath_build + if "defer_initial_multipath_build" in type.keys(): + dimb = type.get("defer_initial_multipath_build") + # build node for defer_initial_multipath_build + dimb_node = build_child_xml_node( + type_node, + "defer-initial-multipath-build", + ) + # add node maximum_delay + if dimb and "maximum_delay" in dimb.keys(): + build_child_xml_node( + dimb_node, + "maximum-delay", + dimb.get("maximum_delay"), + ) + + # add node delay-route-advertisements + if "delay_route_advertisements" in type.keys(): + dra = type.get("delay_route_advertisements") + # build node for delay_route_advertisements + dra_node = build_child_xml_node( + type_node, + "delay-route-advertisements", + ) + # add maximum delay node + if ( + "max_delay_route_age" in dra.keys() + or "max_delay_routing_uptime" in dra.keys() + ): + maxd_node = build_child_xml_node( + dra_node, + "maximum-delay", + ) + # add node route-age + if "max_delay_route_age" in dra.keys(): + build_child_xml_node( + maxd_node, + "route-age", + dra.get("max_delay_route_age"), + ) + + # add node routing-uptime + if "max_delay_routing_uptime" in dra.keys(): + build_child_xml_node( + maxd_node, + "routing-uptime", + dra.get("max_delay_routing_uptime"), + ) + # add minimum delay node + if ( + "min_delay_inbound_convergence" in dra.keys() + or "min_delay_routing_uptime" in dra.keys() + ): + mind_node = build_child_xml_node( + dra_node, + "minimum-delay", + ) + # add node inbound-convergence + if "min_delay_inbound_convergence" in dra.keys(): + build_child_xml_node( + mind_node, + "inbound-convergence", + dra.get("min_delay_inbound_convergence"), + ) + + # add node routing-uptime + if "min_delay_routing_uptime" in dra.keys(): + build_child_xml_node( + mind_node, + "routing-uptime", + dra.get("min_delay_routing_uptime"), + ) + + # add node entropy-label + if "entropy_label" in type.keys(): + el = type.get("entropy_label") + # build node for entropy_label + el_node = build_child_xml_node( + type_node, + "entropy-label", + ) + # add node import + if "import" in el.keys(): + build_child_xml_node( + el_node, + "import", + el.get("import"), + ) + # add node no_next_hop_validation + if "no_next_hop_validation" in el.keys(): + build_child_xml_node( + el_node, + "no-next-hop-validation", + ) + + # add node explicit-null + if "explicit_null" in type.keys(): + en = type.get("explicit_null") + # add node connected-only + if "connected_only" in en.keys(): + en_node = build_child_xml_node( + type_node, + "explicit-null", + ) + build_child_xml_node(en_node, "connected-only") + else: + # build node for explicit_null + build_child_xml_node(type_node, "explicit-null") + + # add node extended-nexthop + if "extended_nexthop" in type.keys(): + enh = type.get("extended_nexthop") + # add node extended-nexthop + if enh: + build_child_xml_node(type_node, "extended-nexthop") + + # add node extended-nexthop-color + if "extended_nexthop_color" in type.keys(): + enhc = type.get("extended_nexthop_color") + # add node extended-nexthop-color + if enhc: + build_child_xml_node( + type_node, + "extended-nexthop-color", + ) + + # add node forwarding-state-bit + if "graceful_restart_forwarding_state_bit" in type.keys(): + grfs = type.get( + "graceful_restart_forwarding_state_bit", + ) + + # add node forwarding-state-bit + gr_node = build_child_xml_node( + type_node, + "graceful-restart", + ) + build_child_xml_node( + gr_node, + "forwarding-state-bit", + grfs, + ) + + # add node local-ipv4-address + if "local_ipv4_address" in type.keys(): + # add node local-ipv4-address + build_child_xml_node( + type_node, + "local-ipv4-address", + type.get("local_ipv4_address"), + ) + + # add node legacy-redirect-ip-action + if "legacy_redirect_ip_action" in type.keys(): + lria = type.get("legacy_redirect_ip_action") + # add node legacy_redirect_ip_action + lria_node = build_child_xml_node( + type_node, + "legacy-redirect-ip-action", + ) + if "send" in lria.keys(): + build_child_xml_node(lria_node, "send") + if "receive" in lria.keys(): + build_child_xml_node(lria_node, "receive") + + # add node loops + if "loops" in type.keys(): + build_child_xml_node( + type_node, + "loops", + type.get("loops"), + ) + + # add no-install + if "no_install" in type.keys(): + if type.get("no_install"): + build_child_xml_node(type_node, "no-install") + + # add node no-validate + if "no_validate" in type.keys(): + build_child_xml_node( + type_node, + "no-validate", + type.get("no_validate"), + ) + + # add node output-queue-priority + if ( + "output_queue_priority_expedited" in type.keys() + or "output_queue_priority_priority" in type.keys() + ): + # node for output-queue-priority + oqp_node = build_child_xml_node( + type_node, + "output-queue-priority", + ) + # add node expedited + if "output_queue_priority_expedited" in type.keys() and type.get( + "output_queue_priority_expedited", + ): + build_child_xml_node(oqp_node, "expedited") + # add node priority + if "output_queue_priority_priority" in type.keys(): + build_child_xml_node( + oqp_node, + "priority", + type.get("output_queue_priority_priority"), + ) + + # add per-prefix-label + if "per_prefix_label" in type.keys(): + if type.get("per_prefix_label"): + build_child_xml_node(type_node, "per-prefix-label") + + # add per-group-label + if "per_group_label" in type.keys(): + if type.get("per_group_label"): + build_child_xml_node(type_node, "per-group-label") + + # Add node for prefix-limit + if "prefix_limit" in type.keys(): + pl = type.get("prefix_limit") + # build node for prefix-limit + pl_node = build_child_xml_node( + type_node, + "prefix-limit", + ) + # Add node for maximum + if "maximum" in pl.keys(): + build_child_xml_node( + pl_node, + "maximum", + pl["maximum"], + ) + # Add node for teardown + td_node = None + if "limit_threshold" in pl.keys(): + td_node = build_child_xml_node( + pl_node, + "teardown", + pl.get("limit_threshold"), + ) + elif "teardown" in pl.keys(): + td_node = build_child_xml_node(pl_node, "teardown") + it_node = None + # Add node for teardown idle_timeout + if "idle_timeout_value" in pl.keys(): + it_node = build_child_xml_node( + td_node, + "idle-timeout", + pl.get("idle_timeout_value"), + ) + elif "idle_timeout" in pl.keys(): + it_node = build_child_xml_node( + td_node, + "idle-timeout", + ) + if "forever" in pl.keys(): + if it_node is None: + it_node = build_child_xml_node( + td_node, + "idle-timeout", + ) + # add forever node + build_child_xml_node(it_node, "forever") + + # add resolve-vpn + if "resolve_vpn" in type.keys(): + if type.get("resolve_vpn"): + build_child_xml_node(type_node, "resolve-vpn") + + # add rib + if "rib" in type.keys(): + rib_node = build_child_xml_node(type_node, "rib") + # add node inet.3 + build_child_xml_node(rib_node, "inet.3") + + # add rib-group + if "ribgroup_name" in type.keys(): + build_child_xml_node( + type_node, + "rib-group", + type.get("ribgroup_name"), + ) + + # add node route-refresh-priority + if ( + "route_refresh_priority_expedited" in type.keys() + or "route_refresh_priority_priority" in type.keys() + ): + # node for route-refresh-priority + rrp_node = build_child_xml_node( + type_node, + "route-refresh-priority", + ) + # add node expedited + if "route_refresh_priority_expedited" in type.keys() and type.get( + "route_refresh_priority_expedited", + ): + build_child_xml_node(rrp_node, "expedited") + # add node priority + if "route_refresh_priority_priority" in type.keys(): + build_child_xml_node( + rrp_node, + "priority", + type.get("route_refresh_priority_priority"), + ) + + # add secondary-independent-resolution + if "secondary_independent_resolution" in type.keys(): + if type.get("secondary_independent_resolution"): + build_child_xml_node( + type_node, + "secondary-independent-resolution", + ) + + # add node withdraw-priority + if ( + "withdraw_priority_expedited" in type.keys() + or "withdraw_priority_priority" in type.keys() + ): + # node for withdraw-priority + wp_node = build_child_xml_node( + type_node, + "withdraw-priority", + ) + # add node expedited + if "withdraw_priority_expedited" in type.keys() and type.get( + "withdraw_priority_expedited", + ): + build_child_xml_node(wp_node, "expedited") + # add node priority + if "withdraw_priority_priority" in type.keys(): + build_child_xml_node( + wp_node, + "priority", + type.get("withdraw_priority_priority"), + ) + + # add strip-nexthop + if "strip_nexthop" in type.keys(): + if type.get("strip_nexthop"): + build_child_xml_node(type_node, "strip-nexthop") + + # add topology + if "topology" in type.keys(): + topologies = type.get("topology") + top_node = build_child_xml_node(type_node, "topology") + for topology in topologies: + if "name" in topology.keys(): + build_child_xml_node( + top_node, + "name", + topology.get("name"), + ) + if "community" in topology.keys(): + communities = topology.get("community") + for community in communities: + build_child_xml_node( + top_node, + "community", + community, + ) + + # add traffic-statistics + if "traffic_statistics" in type.keys(): + ts = type.get("traffic_statistics") + ts_node = build_child_xml_node( + type_node, + "traffic-statistics", + ) + # add node interval + if "interval" in ts.keys(): + build_child_xml_node( + ts_node, + "interval", + ts.get("interval"), + ) + # add node labeled-path + if "labeled_path" in ts.keys and ts.get( + "labeled_path", + ): + build_child_xml_node(ts_node, "labeled-path") + + 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 + """ + bgp_xml = [] + family_root = None + groups_node = None + existing_groups = [] + if have is not None and have.get("address_family"): + h_af = have.get("address_family") + existing_af = [af["afi"] for af in h_af] + + h_groups = have.get("groups") + if h_groups: + for group in h_groups: + existing_groups.append(group["name"]) + if not want or not want.get("address_family"): + want = have + + # Delete root address family + w_af = want["address_family"] + for af in w_af: + if af["afi"] not in existing_af: + continue + if family_root is None: + family_root = build_child_xml_node(self.bgp, "family") + build_child_xml_node( + family_root, + af["afi"], + None, + {"delete": "delete"}, + ) + + # Delete group address-family + w_groups = want.get("groups") + if w_groups: + for group in w_groups: + if group["name"] not in existing_groups: + continue + + groups_node = build_child_xml_node(self.bgp, "group") + build_child_xml_node(groups_node, "name", group["name"]) + + for h_group in h_groups: + if h_group["name"] == group["name"]: + address_family = h_group.get("address_family") + if address_family: + for af in address_family: + family_node = build_child_xml_node( + groups_node, + "family", + ) + build_child_xml_node( + family_node, + af["afi"], + None, + {"delete": "delete"}, + ) + if "neighbors" in h_group.keys(): + h_neighbors = h_group.get("neighbors") + for neighbor in h_neighbors: + if "address_family" in neighbor.keys(): + neighbors_node = build_child_xml_node( + groups_node, + "neighbor", + ) + build_child_xml_node( + neighbors_node, + "name", + neighbor["neighbor_address"], + ) + address_family = neighbor.get( + "address_family", + ) + for af in address_family: + family_node = build_child_xml_node( + neighbors_node, + "family", + ) + build_child_xml_node( + family_node, + af["afi"], + None, + {"delete": "delete"}, + ) + + if groups_node is not None: + bgp_xml.append(groups_node) + if family_root is not None: + bgp_xml.append(family_root) + return bgp_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + bgp_xml = [] + family_root = None + if have is not None and have.get("address_family"): + h_af = have.get("address_family") + existing_af = [af["afi"] for af in h_af] + for af in existing_af: + if family_root is None: + family_root = build_child_xml_node(self.bgp, "family") + build_child_xml_node( + family_root, + af, + None, + {"delete": "delete"}, + ) + if family_root is not None: + bgp_xml.append(family_root) + bgp_xml.extend(self._state_deleted(want, have)) + bgp_xml.extend(self._state_merged(want, have)) + return bgp_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/bgp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/bgp_global.py new file mode 100644 index 000000000..da512c6f9 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/bgp_global/bgp_global.py @@ -0,0 +1,792 @@ +# +# -*- 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 junos_bgp_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Bgp_global(ConfigBase): + """ + The junos_bgp_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["bgp_global"] + + def __init__(self, module): + super(Bgp_global, self).__init__(module) + + def get_bgp_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, + ) + bgp_facts = facts["ansible_network_resources"].get("bgp_global") + if not bgp_facts: + return {} + return bgp_facts + + def execute_module(self): + """Execute the module + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_bgp_global_facts = self.get_bgp_global_facts() + else: + existing_bgp_global_facts = {} + if state == "gathered": + existing_bgp_global_facts = self.get_bgp_global_facts() + result["gathered"] = existing_bgp_global_facts + 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_bgp_global_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_bgp_global_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_bgp_global_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if config_xmls and diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_bgp_global_facts = self.get_bgp_global_facts() + + result["before"] = existing_bgp_global_facts + if result["changed"]: + result["after"] = changed_bgp_global_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_bgp_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_bgp_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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + self.autonomous_system = None + self.root = build_root_xml_node("configuration") + self.protocols = build_child_xml_node(self.root, "protocols") + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + 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, + ), + ) + config_xmls = [] + temp_lst = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state == "purged": + config_xmls = self._state_purged(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + if config_xmls: + for xml in config_xmls: + self.protocols.append(xml) + for xml in self.root.getchildren(): + xml = tostring(xml) + temp_lst.append(xml) + if state == "purged": + for xml in self.root.getchildren(): + xml = tostring(xml) + temp_lst.append(xml) + return temp_lst + + def _state_replaced(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + bgp_xml = [] + bgp_xml.extend(self._state_deleted(want, have)) + bgp_xml.extend(self._state_merged(want, have)) + + return bgp_xml + + def _state_merged(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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + bgp_xml = [] + + want = remove_empties(want) + if want: + bgp_root = build_root_xml_node("bgp") + bool_parser = [ + "accept-remote-nexthop", + "add-path-display-ipv4-address", + "advertise-from-main-vpn-tables", + "advertise-inactive", + "advertise-peer-as", + "damping", + "disable", + "egress-te-sid-stats", + "enforce-first-as", + "holddown-all-stale-labels", + "include-mp-next-hop", + "log-updown", + "mtu-discovery", + "no-advertise-peer-as", + "no-aggregator-id", + "no-client-reflect", + "no-precision-timers", + "passive", + "precision-timers", + "rfc6514-compliant-safi129", + "route-server-client", + "send-addpath-optimization", + "itcp-aggressive-transmission", + "unconfigured-peer-graceful-restart", + "vpn-apply-export", + "as-override", + "unconfigured-peer-graceful-restart", + "vpn-apply-export", + ] + cfg_parser = [ + "authentication-algorithm", + "authentication-key", + "authentication-key-chain", + "description", + "export", + "forwarding-context", + "hold-time", + "import", + "ipsec-sa", + "keep", + "local-address", + "local-interface", + "local-preference", + "peer-as", + "preference", + "out-delay", + "sr-preference-override", + "stale-labels-holddown-period", + "tcp-mss", + "ttl", + "type", + ] + for item in bool_parser: + bgp_root = self._add_node( + want, + item, + item.replace("-", "_"), + bgp_root, + ) + + for item in cfg_parser: + bgp_root = self._add_node( + want, + item, + item.replace("-", "_"), + bgp_root, + True, + ) + + # Generate xml node for autonomous-system + if want.get("as_number"): + as_node = build_child_xml_node( + self.routing_options, + "autonomous-system", + want.get("as_number"), + ) + # Add node for loops + if want.get("loops"): + build_child_xml_node(as_node, "loops", want.get("loops")) + # Add node for asdot_notation + if want.get("asdot_notation"): + if "asdot_notation" in want.keys(): + build_child_xml_node(as_node, "asdot-notation") + + # Generate commands for bgp node + self.parse_attrib(bgp_root, want) + + # Generate commands for groups + if want.get("groups"): + groups = want.get("groups") + + # Generate commands for each group in group list + for group in groups: + groups_node = build_child_xml_node(bgp_root, "group") + build_child_xml_node(groups_node, "name", group["name"]) + # Parse the boolean value attributes + for item in bool_parser: + groups_node = self._add_node( + group, + item, + item.replace("-", "_"), + groups_node, + ) + + # Parse the non-boolean leaf attributes + for item in cfg_parser: + groups_node = self._add_node( + group, + item, + item.replace("-", "_"), + groups_node, + True, + ) + + # Generate commands for nodes with child attributes + self.parse_attrib(groups_node, group) + + # Generate commands for each neighbors + if group.get("neighbors"): + neighbors = group.get("neighbors") + # Generate commands for each neighbor in neighbors list + for neighbor in neighbors: + neighbors_node = build_child_xml_node( + groups_node, + "neighbor", + ) + build_child_xml_node( + neighbors_node, + "name", + neighbor["neighbor_address"], + ) + # Parse the boolean value attributes + for item in bool_parser: + neighbors_node = self._add_node( + neighbor, + item, + item.replace("-", "_"), + neighbors_node, + ) + + # Parse the non-boolean leaf attributes + for item in cfg_parser: + neighbors_node = self._add_node( + neighbor, + item, + item.replace("-", "_"), + neighbors_node, + True, + ) + + # Generate commands for nodes with child attributes + self.parse_attrib(neighbors_node, neighbor) + + bgp_xml.append(bgp_root) + + return bgp_xml + + def parse_attrib(self, bgp_root, want): + # Generate config commands for advertise-bgp-static + if want.get("advertise_bgp_static"): + ad_bgp_static_node = build_child_xml_node( + bgp_root, + "advertise-bgp-static", + ) + ad_bgp_static = want.get("advertise_bgp_static") + if "policy" in ad_bgp_static.keys(): + build_child_xml_node( + ad_bgp_static_node, + "policy", + ad_bgp_static["policy"], + ) + + # Generate config commands for advertise-external + if want.get("advertise_external"): + ad_ext_node = build_child_xml_node(bgp_root, "advertise-external") + ad_ext = want.get("advertise_external") + if "conditional" in ad_ext.keys(): + build_child_xml_node(ad_ext_node, "conditional") + + # Generate config commands for bfd-liveness-detection + if want.get("bfd_liveness_detection"): + bfd_live_node = build_child_xml_node( + bgp_root, + "bfd-liveness-detection", + ) + bfd_live_detect = want.get("bfd_liveness_detection") + + # Add node for authentication + + if "authentication" in bfd_live_detect.keys(): + bld_auth = bfd_live_detect["authentication"] + bld_auth_node = build_child_xml_node( + bfd_live_node, + "authentication", + ) + # Add node for algorithm + if "algorithm" in bld_auth.keys(): + build_child_xml_node( + bld_auth_node, + "algorithm", + bld_auth["algorithm"], + ) + # Add node for key-chain + if "key_chain" in bld_auth.keys(): + build_child_xml_node( + bld_auth_node, + "key-chain", + bld_auth["key_chain"], + ) + # Add node for loose-check + if "loose_check" in bld_auth.keys(): + b_val = bld_auth.get("loose_check") + if b_val is not None: + if b_val is True: + build_child_xml_node(bld_auth_node, "loose-check") + # Add node for detection-time + if "detection_time" in bfd_live_detect.keys(): + d_time = bfd_live_detect["detection_time"] + bld_dtime_node = build_child_xml_node( + bfd_live_node, + "detection-time", + ) + # Add node for threshold + if "threshold" in d_time.keys(): + build_child_xml_node( + bld_dtime_node, + "threshold", + d_time["threshold"], + ) + # Add node for transmit-interval + if "transmit_interval" in bfd_live_detect.keys(): + t_int = bfd_live_detect["transmit_interval"] + t_int_node = build_child_xml_node( + bfd_live_node, + "transmit-interval", + ) + # Add node for minimum-interval + if "minimum_interval" in t_int.keys(): + build_child_xml_node( + t_int_node, + "minimum-interval", + t_int["minimum_interval"], + ) + # Add node for holddown-interval + if "holddown_interval" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "holddown-interval", + bfd_live_detect["holddown_interval"], + ) + # Add node for minimum-receive-interval + if "minimum_receive_interval" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "minimum-receive-interval", + bfd_live_detect["minimum_receive_interval"], + ) + # Add node for minimum-interval + if "minimum_interval" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "minimum-interval", + bfd_live_detect["minimum_interval"], + ) + # Add node for multiplier + if "multiplier" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "multiplier", + bfd_live_detect["multiplier"], + ) + # Add node for no-adaptation + if "no_adaptation" in bfd_live_detect.keys(): + b_val = bfd_live_detect.get("no_adaptation") + if b_val is not None: + if b_val is True: + build_child_xml_node(bfd_live_node, "no-adaptation") + # Add node for session-mode + if "session_mode" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "session-mode", + bfd_live_detect["session_mode"], + ) + # Add node for version + if "version" in bfd_live_detect.keys(): + build_child_xml_node( + bfd_live_node, + "version", + bfd_live_detect["version"], + ) + # Generate config commands for bgp-error-tolerance + if want.get("bgp_error_tolerance"): + bgp_err_tol_node = build_child_xml_node( + bgp_root, + "bgp-error-tolerance", + ) + bgp_err_tol = want.get("bgp_error_tolerance") + # Add node for malformed-route-limit" + if "malformed_route_limit" in bgp_err_tol.keys(): + build_child_xml_node( + bgp_err_tol_node, + "malformed-route-limit", + bgp_err_tol["malformed_route_limit"], + ) + # Add node for malformed-update-log-interval + if "malformed_update_log_interval" in bgp_err_tol.keys(): + build_child_xml_node( + bgp_err_tol_node, + "malformed-update-log-interval", + bgp_err_tol["malformed_update_log_interval"], + ) + # Generate config commands for no-malformed-route-limit + if "no_malformed_route_limit" in bgp_err_tol.keys(): + b_val = bgp_err_tol.get("no_malformed_route_limit") + if b_val is not None: + if b_val is True: + build_child_xml_node( + bgp_err_tol_node, + "no-malformed-route-limit", + ) + + # Generate config commands for bmp + if want.get("bmp"): + bmp_node = build_child_xml_node(bgp_root, "bmp") + bmp = want.get("bmp") + # Add node for monitor + if "monitor" in bmp.keys(): + b_val = bmp.get("monitor") + if b_val is not None: + if b_val is True: + build_child_xml_node(bmp_node, "monitor", "enable") + else: + build_child_xml_node(bmp_node, "monitor", "disable") + + # Add node for route-monitoring + if "route_monitoring" in bmp.keys(): + r_mon_node = build_child_xml_node(bmp_node, "route-monitoring") + r_mon = bmp["route_monitoring"] + # Add node for none + if "none" in r_mon.keys(): + b_val = r_mon.get("none") + if b_val is not None: + if b_val is True: + build_child_xml_node(r_mon_node, "none") + # Add node for post-policy + if "post_policy_exclude_non_eligible" in r_mon.keys(): + b_val = r_mon.get("post_policy_exclude_non_eligible") + if b_val is not None: + if b_val is True: + policy_node = build_child_xml_node( + r_mon_node, + "post-policy", + ) + build_child_xml_node( + policy_node, + "exclude-non-eligible", + ) + elif "post_policy" in r_mon.keys(): + b_val = r_mon.get("post_policy") + if b_val is not None: + if b_val is True: + build_child_xml_node(r_mon_node, "post-policy") + # Add node for post-policy + if "pre_policy_exclude_non_feasible" in r_mon.keys(): + b_val = r_mon.get("pre_policy_exclude_non_feasible") + if b_val is not None: + if b_val is True: + policy_node = build_child_xml_node( + r_mon_node, + "pre-policy", + ) + build_child_xml_node( + policy_node, + "exclude-non-eligible", + ) + elif "pre-policy" in r_mon.keys(): + b_val = r_mon.get("pre_policy") + if b_val is not None: + if b_val is True: + build_child_xml_node(r_mon_node, "pre-policy") + + # Generate config commands for egress-te + if want.get("egress_te"): + et_node = build_child_xml_node(bgp_root, "egress-te") + et = want.get("egress_te") + if "backup_path" in et: + build_child_xml_node( + et_node, + "backup-path", + et.get("backup_path"), + ) + + # Generate config commands for egress-te-backup-paths + if want.get("egress_te_backup_paths"): + etbp_node = build_child_xml_node( + bgp_root, + "egress-te-backup-paths", + ) + etbp = want.get("egress_te_backup_paths") + # generate commands for templates + templates = etbp.get("templates") + for template in templates: + template_node = build_child_xml_node(etbp_node, "template") + # add name node + if "path_name" in template.keys(): + build_child_xml_node( + template_node, + "name", + template.get("path_name"), + ) + # add peers + if "peers" in template.keys(): + peers = template.get("peers") + for peer in peers: + peer_node = build_child_xml_node(template_node, "peer") + build_child_xml_node(peer_node, "name", peer) + # add remote-nexthop + if "remote_nexthop" in template.keys(): + build_child_xml_node( + template_node, + "remote-nexthop", + template.get("remote_nexthop"), + ) + # add ip-forward + if "ip_forward" in template.keys(): + ipf = template.get("ip_forward") + ipf_node = build_child_xml_node( + template_node, + "ip-forward", + ) + + if "rti_name" not in ipf.keys(): + build_child_xml_node( + ipf_node, + "name", + ipf.get("rti_name"), + ) + + # Generate config commands for allow + if want.get("allow"): + allow = want.get("allow") + for network in allow: + build_child_xml_node(bgp_root, "allow", network) + + # Generate config commands for optimal-route-reflection + if want.get("optimal_route_reflection"): + orr_node = build_child_xml_node( + bgp_root, + "optimal-route-reflection", + ) + orr = want.get("optimal_route_reflection") + if "igp_backup" in orr.keys(): + build_child_xml_node( + orr_node, + "igp-backup", + orr.get("igp_backup"), + ) + if "igp_primary" in orr.keys(): + build_child_xml_node( + orr_node, + "igp-primary", + orr.get("igp_primary"), + ) + + 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 + """ + bgp_xml = [] + parser = [ + "accept-remote-nexthop", + "add-path-display-ipv4-address", + "advertise-bgp-static", + "advertise-external", + "advertise-from-main-vpn-tables", + "advertise-inactive", + "advertise-peer-as", + "authentication-algorithm", + "authentication-key", + "authentication-key-chain", + "bfd-liveness-detection", + "bgp-error-tolerance", + "bmp", + "group", + "cluster", + "damping", + "description", + "disable", + "egress-te-sid-stats", + "enforce-first-as", + "export", + "forwarding-context", + "hold-time", + "holddown-all-stale-labels", + "import", + "include-mp-next-hop", + "ipsec-sa", + "keep", + "local-address", + "local-interface", + "local-preference", + "log-updown", + "mtu-discovery", + "no-advertise-peer-as", + "no-aggregator-id", + "no-client-reflect", + "no-precision-timers", + "passive", + "peer-as", + "precision-timers", + "preference", + "out-delay", + "rfc6514-compliant-safi129", + "route-server-client", + "send-addpath-optimization", + "sr-preference-override", + "stale-labels-holddown-period", + "tcp-aggressive-transmission", + "tcp-mss", + "ttl", + "unconfigured-peer-graceful-restart", + "vpn-apply-export", + ] + if have is not None: + bgp_root = build_root_xml_node("bgp") + for attrib in parser: + build_child_xml_node( + bgp_root, + attrib, + None, + {"delete": "delete"}, + ) + autonomous_system = have.get("as_number") + if autonomous_system: + build_child_xml_node( + self.routing_options, + "autonomous-system", + None, + {"delete": "delete"}, + ) + bgp_xml.append(bgp_root) + return bgp_xml + + def _state_purged(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 + """ + bgp_xml = [] + delete = {"delete": "delete"} + build_child_xml_node(self.protocols, "bgp", None, delete) + autonomous_system = have.get("as_number") + if autonomous_system: + build_child_xml_node( + self.routing_options, + "autonomous-system", + None, + {"delete": "delete"}, + ) + return bgp_xml + + def _add_node(self, want, h_key, w_key, node, cfg=False): + """Append the child node to the root node + :param want: the desired configuration as a dictionary + :param h_key: the current configuration key + :param: node: root node + """ + if cfg: + if want.get(w_key): + build_child_xml_node(node, h_key, want[w_key]) + else: + if w_key in want.keys(): + b_val = want.get(w_key) + if b_val is not None: + if b_val is True: + build_child_xml_node(node, h_key) + return node diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/hostname.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/hostname.py new file mode 100644 index 000000000..0fab60f99 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/hostname/hostname.py @@ -0,0 +1,199 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_hostname 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Hostname(ConfigBase): + """ + The junos_hostname class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["hostname"] + + def __init__(self, module): + super(Hostname, self).__init__(module) + + def get_hostname_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, + ) + hostname_facts = facts["ansible_network_resources"].get("hostname") + if not hostname_facts: + return {} + return hostname_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_hostname_facts = self.get_hostname_facts() + else: + existing_hostname_facts = {} + if state == "gathered": + existing_hostname_facts = self.get_hostname_facts() + result["gathered"] = existing_hostname_facts + 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_hostname_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_hostname_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + + else: + diff = None + config_xmls = self.set_config(existing_hostname_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_hostname_facts = self.get_hostname_facts() + + result["before"] = existing_hostname_facts + if result["changed"]: + result["after"] = changed_hostname_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_hostname_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_hostname_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 + """ + self.root = build_root_xml_node("system") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "deleted": + self._state_deleted(want, have) + elif state in ("merged", "rendered", "replaced", "overridden"): + self._state_merged(want, have) + return tostring(self.root) + + 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 + """ + want = remove_empties(want) + + # add hostname node + if "hostname" in want.keys(): + build_child_xml_node(self.root, "host-name", want.get("hostname")) + + 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 + """ + hostname_xml = [] + hostname_root = None + delete = {"delete": "delete"} + if have is not None: + hostname_root = build_child_xml_node( + self.root, + "host-name", + None, + delete, + ) + + if hostname_root is not None: + hostname_xml.append(hostname_root) + return hostname_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/interfaces.py new file mode 100644 index 000000000..ad6b58668 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/interfaces/interfaces.py @@ -0,0 +1,358 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Interfaces(ConfigBase): + """ + The junos_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["interfaces"] + + def __init__(self, module): + super(Interfaces, self).__init__(module) + + def get_interfaces_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + interfaces_facts = facts["ansible_network_resources"].get("interfaces") + if not interfaces_facts: + return [] + return interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + if self.state in self.ACTION_STATES: + existing_interfaces_facts = self.get_interfaces_facts() + else: + existing_interfaces_facts = [] + + if state == "gathered": + existing_interfaces_facts = self.get_interfaces_facts() + result["gathered"] = existing_interfaces_facts + 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) + elif self.state == "rendered": + config_xmls = self.set_config(existing_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_interfaces_facts = self.get_interfaces_facts() + + result["before"] = existing_interfaces_facts + if result["changed"]: + result["after"] = changed_interfaces_facts + + 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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is replaced + + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(want, have)) + intf_xml.extend(self._state_merged(want, have)) + + return intf_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is overridden + + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + interface_xmls_obj = [] + # replace interface config with data in want + interface_xmls_obj.extend(self._state_replaced(want, have)) + + # delete interface config if interface in have not present in want + delete_obj = [] + for have_obj in have: + for want_obj in want: + if have_obj["name"] == want_obj["name"]: + break + else: + delete_obj.append(have_obj) + + if delete_obj: + interface_xmls_obj.extend(self._state_deleted(delete_obj, have)) + return interface_xmls_obj + + def _state_merged(self, want, have): + """The xml configuration generator when state is merged + + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + intf_xml = [] + + for config in want: + intf = build_root_xml_node("interface") + build_child_xml_node(intf, "name", config["name"]) + + intf_fields = ["description", "speed"] + if not config["name"].startswith("fxp"): + intf_fields.append("mtu") + for field in intf_fields: + if config.get(field): + build_child_xml_node(intf, field, config[field]) + + if config.get("duplex"): + build_child_xml_node(intf, "link-mode", config["duplex"]) + + if config.get("enabled") is False: + build_child_xml_node(intf, "disable") + + if config.get("units"): + units = config.get("units") + for unit in units: + unit_node = build_child_xml_node(intf, "unit") + build_child_xml_node(unit_node, "name", str(unit["name"])) + build_child_xml_node( + unit_node, + "description", + unit["description"], + ) + + holdtime = config.get("hold_time") + if holdtime: + holdtime_ele = build_child_xml_node(intf, "hold-time") + + for holdtime_field in ["up", "down"]: + build_child_xml_node( + holdtime_ele, + holdtime_field, + holdtime.get(holdtime_field, ""), + ) + intf_xml.append(intf) + + return intf_xml + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + + :rtype: A list + :returns: the xml configuration necessary to remove the current configuration + of the provided objects + """ + intf_xml = [] + intf_obj = want + + if not intf_obj: + # delete base interfaces attribute from all the existing interface + intf_obj = have + + if have: + for config in intf_obj: + intf = build_root_xml_node("interface") + build_child_xml_node(intf, "name", config["name"]) + + intf_fields = ["description"] + if not any( + [ + config["name"].startswith("gr"), + config["name"].startswith("lo"), + ], + ): + intf_fields.append("speed") + + if not any( + [ + config["name"].startswith("gr"), + config["name"].startswith("fxp"), + config["name"].startswith("lo"), + ], + ): + intf_fields.append("mtu") + + for field in intf_fields: + build_child_xml_node( + intf, + field, + None, + {"delete": "delete"}, + ) + + if not any( + [ + config["name"].startswith("gr"), + config["name"].startswith("lo"), + ], + ): + build_child_xml_node( + intf, + "link-mode", + None, + {"delete": "delete"}, + ) + + build_child_xml_node( + intf, + "disable", + None, + {"delete": "delete"}, + ) + + holdtime_ele = build_child_xml_node(intf, "hold-time") + have_cfg = self.in_have(config["name"], have) + if have_cfg: + logical_cfg = have_cfg + else: + logical_cfg = config + if logical_cfg.get("units"): + units = logical_cfg.get("units") + for unit in units: + unit_node = build_child_xml_node(intf, "unit") + build_child_xml_node( + unit_node, + "name", + str(unit["name"]), + ) + build_child_xml_node( + unit_node, + "description", + None, + {"delete": "delete"}, + ) + + for holdtime_field in ["up", "down"]: + build_child_xml_node( + holdtime_ele, + holdtime_field, + None, + {"delete": "delete"}, + ) + intf_xml.append(intf) + + return intf_xml + + def in_have(self, name, have): + for item in have: + if name == item["name"]: + return item + return None diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/l2_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..7cbb3a825 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l2_interfaces/l2_interfaces.py @@ -0,0 +1,340 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, + build_subtree, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +class L2_interfaces(ConfigBase): + """ + The junos_l2_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["l2_interfaces"] + + def __init__(self, module): + super(L2_interfaces, self).__init__(module) + + 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} + state = self._module.params["state"] + + if self.state in self.ACTION_STATES: + existing_l2_interfaces_facts = self.get_l2_interfaces_facts() + else: + existing_l2_interfaces_facts = [] + if state == "gathered": + existing_l2_interfaces_facts = self.get_l2_interfaces_facts() + result["gathered"] = existing_l2_interfaces_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_l2_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_l2_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_interfaces_facts = self.get_l2_interfaces_facts() + + result["before"] = existing_l2_interfaces_facts + if result["changed"]: + result["after"] = changed_interfaces_facts + + 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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is replaced + + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + l2_intf_xml = [] + l2_intf_xml.extend(self._state_deleted(want, have)) + l2_intf_xml.extend(self._state_merged(want, have)) + + return l2_intf_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is overridden + + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + l2_interface_xmls_obj = [] + # replace interface config with data in want + l2_interface_xmls_obj.extend(self._state_replaced(want, have)) + + # delete interface config if interface in have not present in want + delete_obj = [] + for have_obj in have: + for want_obj in want: + if have_obj["name"] == want_obj["name"]: + break + else: + delete_obj.append(have_obj) + + if delete_obj: + l2_interface_xmls_obj.extend(self._state_deleted(delete_obj, have)) + return l2_interface_xmls_obj + + def _state_merged(self, want, have): + """The xml configuration generator when state is merged + + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + intf_xml = [] + for config in want: + enhanced_layer = True + if config.get("enhanced_layer") is False: + enhanced_layer = False + + mode = "interface-mode" if enhanced_layer else "port-mode" + intf = build_root_xml_node("interface") + build_child_xml_node(intf, "name", config["name"]) + unit_node = build_child_xml_node(intf, "unit") + unit = config["unit"] if config["unit"] else "0" + build_child_xml_node(unit_node, "name", unit) + + eth_node = build_subtree(unit_node, "family/ethernet-switching") + if config.get("access"): + vlan = config["access"].get("vlan") + if vlan: + build_child_xml_node(eth_node, mode, "access") + vlan_node = build_child_xml_node(eth_node, "vlan") + build_child_xml_node(vlan_node, "members", vlan) + intf_xml.append(intf) + elif config.get("trunk"): + allowed_vlans = config["trunk"].get("allowed_vlans") + native_vlan = config["trunk"].get("native_vlan") + if allowed_vlans: + build_child_xml_node(eth_node, mode, "trunk") + vlan_node = build_child_xml_node(eth_node, "vlan") + for vlan in allowed_vlans: + build_child_xml_node(vlan_node, "members", vlan) + if native_vlan: + build_child_xml_node(intf, "native-vlan-id", native_vlan) + + if allowed_vlans or native_vlan: + intf_xml.append(intf) + + return intf_xml + + def get_res_config(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return get_resource_config(connection, config_filter=config_filter) + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + + :rtype: A list + :returns: the xml configuration necessary to remove the current configuration + of the provided objects + """ + l2_intf_xml = [] + l2_intf_obj = want + + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = self.get_res_config( + self._connection, + config_filter=config_filter, + ) + + if not l2_intf_obj: + # delete l2 interfaces attribute from all the existing interface having l2 config + l2_intf_obj = have + + for config in l2_intf_obj: + name = config["name"] + enhanced_layer = True + l2_mode = data.xpath( + "configuration/interfaces/interface[name='%s']/unit/family/ethernet-switching/interface-mode" + % name, + ) + + if not len(l2_mode): + l2_mode = data.xpath( + "configuration/interfaces/interface[name='%s']/unit/family/ethernet-switching/port-mode" + % name, + ) + enhanced_layer = False + + if len(l2_mode): + mode = "interface-mode" if enhanced_layer else "port-mode" + + intf = build_root_xml_node("interface") + build_child_xml_node(intf, "name", name) + + unit_node = build_child_xml_node(intf, "unit") + unit = config["unit"] if config["unit"] else "0" + build_child_xml_node(unit_node, "name", unit) + + eth_node = build_subtree( + unit_node, + "family/ethernet-switching", + ) + build_child_xml_node( + eth_node, + mode, + None, + {"delete": "delete"}, + ) + build_child_xml_node( + eth_node, + "vlan", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + intf, + "native-vlan-id", + None, + {"delete": "delete"}, + ) + + l2_intf_xml.append(intf) + + return l2_intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/l3_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..c037ef03b --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/l3_interfaces/l3_interfaces.py @@ -0,0 +1,288 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class L3_interfaces(ConfigBase): + """ + The junos_l3_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["l3_interfaces"] + + def __init__(self, module): + super(L3_interfaces, self).__init__(module) + + def get_l3_interfaces_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + l3_interfaces_facts = facts["ansible_network_resources"].get( + "l3_interfaces", + ) + if not l3_interfaces_facts: + return [] + return l3_interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_l3_interfaces_facts = self.get_l3_interfaces_facts() + else: + existing_l3_interfaces_facts = [] + if state == "gathered": + existing_l3_interfaces_facts = self.get_l3_interfaces_facts() + result["gathered"] = existing_l3_interfaces_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_l3_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_l3_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_interfaces_facts = self.get_l3_interfaces_facts() + + result["before"] = existing_l3_interfaces_facts + if result["changed"]: + result["after"] = changed_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 list xml configuration necessary to migrate the current + configuration + to the desired configuration + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _get_common_xml_node(self, name): + root_node = build_root_xml_node("interface") + build_child_xml_node(root_node, "name", name) + intf_unit_node = build_child_xml_node(root_node, "unit") + return root_node, intf_unit_node + + def _state_replaced(self, want, have): + """The xml generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(want, have)) + intf_xml.extend(self._state_merged(want, have)) + return intf_xml + + def _state_overridden(self, want, have): + """The xml generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(have, have)) + intf_xml.extend(self._state_merged(want, have)) + return intf_xml + + def _state_merged(self, want, have): + """The xml generator when state is merged + + :rtype: A list + :returns: the xml necessary to merge the provided into + the current configuration + """ + intf_xml = [] + for config in want: + root_node, unit_node = self._get_common_xml_node(config["name"]) + build_child_xml_node(unit_node, "name", str(config["unit"])) + if config.get("ipv4"): + self.build_ipaddr_et(config, unit_node) + if config.get("ipv6"): + self.build_ipaddr_et(config, unit_node, protocol="ipv6") + intf_xml.append(root_node) + return intf_xml + + def build_ipaddr_et( + self, + config, + unit_node, + protocol="ipv4", + delete=False, + ): + family = build_child_xml_node(unit_node, "family") + inet = "inet" + if protocol == "ipv6": + inet = "inet6" + ip_protocol = build_child_xml_node(family, inet) + for ip_addr in config[protocol]: + if ip_addr["address"] == "dhcp" and protocol == "ipv4": + build_child_xml_node(ip_protocol, "dhcp") + else: + ip_addresses = build_child_xml_node(ip_protocol, "address") + build_child_xml_node(ip_addresses, "name", ip_addr["address"]) + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + + :rtype: A list + :returns: the xml configuration necessary to remove the current + configuration of the provided objects + """ + intf_xml = [] + existing_l3_intfs = [l3_intf["name"] for l3_intf in have] + + if not want: + want = have + + for config in want: + if config["name"] not in existing_l3_intfs: + continue + root_node, unit_node = self._get_common_xml_node(config["name"]) + build_child_xml_node(unit_node, "name", str(config["unit"])) + family = build_child_xml_node(unit_node, "family") + ipv4 = build_child_xml_node(family, "inet") + intf = next( + (intf for intf in have if intf["name"] == config["name"]), + None, + ) + if "ipv4" in intf: + if "dhcp" in [ + x["address"] for x in intf.get("ipv4") if intf.get("ipv4") is not None + ]: + build_child_xml_node( + ipv4, + "dhcp", + None, + {"delete": "delete"}, + ) + else: + build_child_xml_node( + ipv4, + "address", + None, + {"delete": "delete"}, + ) + ipv6 = build_child_xml_node(family, "inet6") + build_child_xml_node(ipv6, "address", None, {"delete": "delete"}) + + intf_xml.append(root_node) + return intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/lacp.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/lacp.py new file mode 100644 index 000000000..47ec993e1 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp/lacp.py @@ -0,0 +1,255 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, + build_subtree, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Lacp(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lacp_facts = self.get_lacp_facts() + else: + existing_lacp_facts = {} + if state == "gathered": + existing_lacp_facts = self.get_lacp_facts() + result["gathered"] = existing_lacp_facts + 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) + elif self.state == "rendered": + config_xmls = self.set_config(existing_lacp_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_lacp_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_lacp_facts = self.get_lacp_facts() + + result["before"] = existing_lacp_facts + if result["changed"]: + result["after"] = changed_lacp_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lacp_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lacp_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("chassis") + ethernet_ele = build_subtree(root, "aggregated-devices/ethernet") + 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 == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + ethernet_ele.append(xml) + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + lacp_xml = [] + lacp_xml.extend(self._state_deleted(want, have)) + lacp_xml.extend(self._state_merged(want, have)) + + return lacp_xml + + 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 + """ + lacp_xml = [] + lacp_xml.extend(self._state_deleted(want, have)) + lacp_xml.extend(self._state_merged(want, have)) + + return lacp_xml + + def _state_merged(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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + lacp_xml = [] + + lacp_root = build_root_xml_node("lacp") + build_child_xml_node( + lacp_root, + "system-priority", + want.get("system_priority"), + ) + if want.get("link_protection") == "non-revertive": + build_subtree(lacp_root, "link-protection/non-revertive") + elif want.get("link_protection") == "revertive": + link_root = build_child_xml_node(lacp_root, "link-protection") + build_child_xml_node( + link_root, + "non-revertive", + None, + {"delete": "delete"}, + ) + lacp_xml.append(lacp_root) + return lacp_xml + + 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 + """ + lacp_xml = [] + + lacp_root = build_root_xml_node("lacp") + build_child_xml_node( + lacp_root, + "system-priority", + None, + {"delete": "delete"}, + ) + element = build_child_xml_node( + lacp_root, + "link-protection", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + element, + "non-revertive", + None, + {"delete": "delete"}, + ) + + lacp_xml.append(lacp_root) + return lacp_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/lacp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..79b099988 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,345 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, + build_subtree, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Lacp_interfaces(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + + if self.state in self.ACTION_STATES: + existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + else: + existing_lacp_interfaces_facts = [] + + if state == "gathered": + existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + result["gathered"] = existing_lacp_interfaces_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_lacp_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_lacp_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_interfaces_facts = self.get_lacp_interfaces_facts() + + result["before"] = existing_lacp_interfaces_facts + if result["changed"]: + result["after"] = changed_interfaces_facts + 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 + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is replaced + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(want, have)) + intf_xml.extend(self._state_merged(want, have)) + + return intf_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is overridden + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + interface_xmls_obj = [] + # replace interface config with data in want + interface_xmls_obj.extend(self._state_replaced(want, have)) + + # delete interface config if interface in have not present in want + delete_obj = [] + for have_obj in have: + for want_obj in want: + if have_obj["name"] == want_obj["name"]: + break + else: + delete_obj.append(have_obj) + + if delete_obj: + interface_xmls_obj.extend(self._state_deleted(delete_obj, have)) + return interface_xmls_obj + + def _state_merged(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + intf_xml = [] + + for config in want: + lacp_intf_name = config["name"] + lacp_intf_root = build_root_xml_node("interface") + + build_child_xml_node(lacp_intf_root, "name", lacp_intf_name) + if lacp_intf_name.startswith("ae"): + element = build_subtree( + lacp_intf_root, + "aggregated-ether-options/lacp", + ) + if config["period"]: + build_child_xml_node(element, "periodic", config["period"]) + if config["sync_reset"]: + build_child_xml_node( + element, + "sync-reset", + config["sync_reset"], + ) + + system = config["system"] + if system: + mac = system.get("mac") + if mac: + if mac.get("address"): + build_child_xml_node( + element, + "system-id", + mac["address"], + ) + if system.get("priority"): + build_child_xml_node( + element, + "system-priority", + system["priority"], + ) + intf_xml.append(lacp_intf_root) + elif config["port_priority"] or config["force_up"] is not None: + element = build_subtree( + lacp_intf_root, + "ether-options/ieee-802.3ad/lacp", + ) + if config["port_priority"] is not None: + build_child_xml_node( + element, + "port-priority", + config["port_priority"], + ) + else: + build_child_xml_node( + element, + "port-priority", + None, + {"delete": "delete"}, + ) + if config["force_up"]: + build_child_xml_node(element, "force-up") + else: + build_child_xml_node( + element, + "force-up", + None, + {"delete": "delete"}, + ) + intf_xml.append(lacp_intf_root) + + return intf_xml + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + :rtype: A list + :returns: the xml configuration necessary to remove the current configuration + of the provided objects + """ + intf_xml = [] + intf_obj = want + + if not intf_obj: + # delete lag interfaces attribute for all the interface + intf_obj = have + + for config in intf_obj: + lacp_intf_name = config["name"] + lacp_intf_root = build_root_xml_node("interface") + build_child_xml_node(lacp_intf_root, "name", lacp_intf_name) + if lacp_intf_name.startswith("ae"): + element = build_subtree( + lacp_intf_root, + "aggregated-ether-options/lacp", + ) + build_child_xml_node( + element, + "periodic", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + element, + "sync-reset", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + element, + "system-id", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + element, + "system-priority", + None, + {"delete": "delete"}, + ) + else: + element = build_subtree( + lacp_intf_root, + "ether-options/ieee-802.3ad/lacp", + ) + build_child_xml_node( + element, + "port-priority", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + element, + "force-up", + None, + {"delete": "delete"}, + ) + + intf_xml.append(lacp_intf_root) + + return intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/lag_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..2b17498ee --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lag_interfaces/lag_interfaces.py @@ -0,0 +1,365 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, + build_subtree, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +class Lag_interfaces(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lag_interfaces_facts = self.get_lag_interfaces_facts() + else: + existing_lag_interfaces_facts = [] + if state == "gathered": + existing_lag_interfaces_facts = self.get_lag_interfaces_facts() + result["gathered"] = existing_lag_interfaces_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_lag_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_lag_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_lag_interfaces_facts = self.get_lag_interfaces_facts() + + result["before"] = existing_lag_interfaces_facts + if result["changed"]: + result["after"] = changed_lag_interfaces_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lag_interfaces_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lag_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("interfaces") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is replaced + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(want, have)) + intf_xml.extend(self._state_merged(want, have)) + + return intf_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is overridden + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + interface_xmls_obj = [] + # replace interface config with data in want + interface_xmls_obj.extend(self._state_replaced(want, have)) + + # delete interface config if interface in have not present in want + delete_obj = [] + for have_obj in have: + for want_obj in want: + if have_obj["name"] == want_obj["name"]: + break + else: + delete_obj.append(have_obj) + + if delete_obj: + interface_xmls_obj.extend(self._state_deleted(delete_obj, have)) + return interface_xmls_obj + + def _state_merged(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + intf_xml = [] + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + if self._module.params["state"] != "rendered": + data = get_resource_config( + self._connection, + config_filter=config_filter, + ) + + for config in want: + lag_name = config["name"] + + # if lag interface not already configured fail module. + if self._module.params["state"] != "rendered": + if not data.xpath( + "configuration/interfaces/interface[name='%s']" % lag_name, + ): + self._module.fail_json( + msg="lag interface %s not configured, configure interface" + " %s before assigning members to lag" % (lag_name, lag_name), + ) + + lag_intf_root = build_root_xml_node("interface") + build_child_xml_node(lag_intf_root, "name", lag_name) + ether_options_node = build_subtree( + lag_intf_root, + "aggregated-ether-options", + ) + if config["mode"]: + + lacp_node = build_child_xml_node(ether_options_node, "lacp") + build_child_xml_node(lacp_node, config["mode"]) + + link_protection = config["link_protection"] + if link_protection: + build_child_xml_node(ether_options_node, "link-protection") + elif link_protection is False: + build_child_xml_node( + ether_options_node, + "link-protection", + None, + {"delete": "delete"}, + ) + + intf_xml.append(lag_intf_root) + + members = config["members"] + for member in members: + lag_member_intf_root = build_root_xml_node("interface") + build_child_xml_node( + lag_member_intf_root, + "name", + member["member"], + ) + lag_node = build_subtree( + lag_member_intf_root, + "ether-options/ieee-802.3ad", + ) + build_child_xml_node(lag_node, "bundle", config["name"]) + + link_type = member.get("link_type") + if link_type == "primary": + build_child_xml_node(lag_node, "primary") + elif link_type == "backup": + build_child_xml_node(lag_node, "backup") + + intf_xml.append(lag_member_intf_root) + + return intf_xml + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + :rtype: A list + :returns: the xml configuration necessary to remove the current configuration + of the provided objects + """ + intf_xml = [] + intf_obj = want + + if not intf_obj: + # delete lag interfaces attribute for all the interface + intf_obj = have + + for config in intf_obj: + lag_name = config["name"] + lag_intf_root = build_root_xml_node("interface") + build_child_xml_node(lag_intf_root, "name", lag_name) + + lag_node = build_subtree(lag_intf_root, "aggregated-ether-options") + build_child_xml_node( + lag_node, + "link-protection", + None, + {"delete": "delete"}, + ) + + lacp_node = build_child_xml_node(lag_node, "lacp") + build_child_xml_node( + lacp_node, + "active", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lacp_node, + "passive", + None, + {"delete": "delete"}, + ) + + intf_xml.append(lag_intf_root) + + # delete lag configuration from member interfaces + for interface_obj in have: + if lag_name == interface_obj["name"]: + for member in interface_obj.get("members", []): + lag_member_intf_root = build_root_xml_node("interface") + build_child_xml_node( + lag_member_intf_root, + "name", + member["member"], + ) + lag_node = build_subtree( + lag_member_intf_root, + "ether-options/ieee-802.3ad", + ) + build_child_xml_node( + lag_node, + "bundle", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lag_node, + "primary", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lag_node, + "backup", + None, + {"delete": "delete"}, + ) + intf_xml.append(lag_member_intf_root) + + return intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/lldp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/lldp_global.py new file mode 100644 index 000000000..4efac0315 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_global/lldp_global.py @@ -0,0 +1,269 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_lldp 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Lldp_global(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lldp_global_facts = self.get_lldp_global_facts() + else: + existing_lldp_global_facts = {} + if state == "gathered": + existing_lldp_global_facts = self.get_lldp_global_facts() + result["gathered"] = existing_lldp_global_facts + 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) + elif self.state == "rendered": + config_xmls = self.set_config(existing_lldp_global_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_lldp_global_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_lldp_global_facts = self.get_lldp_global_facts() + + result["before"] = existing_lldp_global_facts + if result["changed"]: + result["after"] = changed_lldp_global_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lldp_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lldp_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("protocols") + 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": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + lldp_xml = [] + lldp_xml.extend(self._state_deleted(want, have)) + lldp_xml.extend(self._state_merged(want, have)) + + return lldp_xml + + def _state_merged(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 list xml configuration necessary to migrate the current configuration + to the desired configuration + """ + lldp_xml = [] + + lldp_root = build_root_xml_node("lldp") + if want.get("address"): + build_child_xml_node( + lldp_root, + "management-address", + want["address"], + ) + if want.get("interval"): + build_child_xml_node( + lldp_root, + "advertisement-interval", + want["interval"], + ) + if want.get("transmit_delay"): + build_child_xml_node( + lldp_root, + "transmit-delay", + want["transmit_delay"], + ) + if want.get("hold_multiplier"): + build_child_xml_node( + lldp_root, + "hold-multiplier", + want["hold_multiplier"], + ) + enable = want.get("enable") + if enable is not None: + if enable is False: + build_child_xml_node(lldp_root, "disable") + else: + build_child_xml_node( + lldp_root, + "disable", + None, + {"delete": "delete"}, + ) + else: + build_child_xml_node( + lldp_root, + "disable", + None, + {"delete": "delete"}, + ) + lldp_xml.append(lldp_root) + + return lldp_xml + + 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 + """ + lldp_xml = [] + + lldp_root = build_root_xml_node("lldp") + build_child_xml_node( + lldp_root, + "management-address", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lldp_root, + "advertisement-interval", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lldp_root, + "transmit-delay", + None, + {"delete": "delete"}, + ) + build_child_xml_node( + lldp_root, + "hold-multiplier", + None, + {"delete": "delete"}, + ) + build_child_xml_node(lldp_root, "disable", None, {"delete": "delete"}) + lldp_xml.append(lldp_root) + return lldp_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/lldp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..262827459 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,264 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, + build_subtree, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Lldp_interfaces(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + else: + existing_lldp_interfaces_facts = [] + if state == "gathered": + existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + result["gathered"] = existing_lldp_interfaces_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_lldp_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_lldp_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + + result["before"] = existing_lldp_interfaces_facts + if result["changed"]: + result["after"] = 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 + """ + root = build_root_xml_node("protocols") + lldp_intf_ele = build_subtree(root, "lldp") + + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + lldp_intf_ele.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The xml configuration generator when state is replaced + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + lldp_intf_xml = [] + lldp_intf_xml.extend(self._state_deleted(want, have)) + lldp_intf_xml.extend(self._state_merged(want, have)) + + return lldp_intf_xml + + def _state_overridden(self, want, have): + """The xml configuration generator when state is overridden + :rtype: A list + :returns: the xml configuration necessary to migrate the current configuration + to the desired configuration + """ + lldp_intf_xmls_obj = [] + + # replace interface config with data in want + lldp_intf_xmls_obj.extend(self._state_replaced(want, have)) + + # delete interface config if interface in have not present in want + delete_obj = [] + for have_obj in have: + for want_obj in want: + if have_obj["name"] == want_obj["name"]: + break + else: + delete_obj.append(have_obj) + + if len(delete_obj): + lldp_intf_xmls_obj.extend(self._state_deleted(delete_obj, have)) + + return lldp_intf_xmls_obj + + def _state_merged(self, want, have): + """The xml configuration generator when state is merged + :rtype: A list + :returns: the xml configuration necessary to merge the provided into + the current configuration + """ + lldp_intf_xml = [] + for config in want: + lldp_intf_root = build_root_xml_node("interface") + + if config.get("name"): + build_child_xml_node(lldp_intf_root, "name", config["name"]) + + if config.get("enabled") is not None: + if config["enabled"] is False: + build_child_xml_node(lldp_intf_root, "disable") + else: + build_child_xml_node( + lldp_intf_root, + "disable", + None, + {"delete": "delete"}, + ) + else: + build_child_xml_node( + lldp_intf_root, + "disable", + None, + {"delete": "delete"}, + ) + lldp_intf_xml.append(lldp_intf_root) + return lldp_intf_xml + + def _state_deleted(self, want, have): + """The xml configuration generator when state is deleted + :rtype: A list + :returns: the xml configuration necessary to remove the current configuration + of the provided objects + """ + lldp_intf_xml = [] + intf_obj = want + + if not intf_obj: + # delete lldp interfaces attribute from all the existing interface + intf_obj = have + + for config in intf_obj: + lldp_intf_root = build_root_xml_node("interface") + lldp_intf_root.attrib.update({"delete": "delete"}) + build_child_xml_node(lldp_intf_root, "name", config["name"]) + + lldp_intf_xml.append(lldp_intf_root) + + return lldp_intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/logging_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/logging_global.py new file mode 100644 index 000000000..7411f87e0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/logging_global/logging_global.py @@ -0,0 +1,528 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_logging_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.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Logging_global(ConfigBase): + """ + The junos_logging_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["logging_global"] + + def __init__(self, module): + super(Logging_global, self).__init__(module) + + def get_logging_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, + ) + logging_global_facts = facts["ansible_network_resources"].get( + "logging_global", + ) + if not logging_global_facts: + return {} + return logging_global_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_logging_global_facts = self.get_logging_global_facts() + else: + existing_logging_global_facts = {} + if state == "gathered": + existing_logging_global_facts = self.get_logging_global_facts() + result["gathered"] = existing_logging_global_facts + 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_logging_global_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_logging_global_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + + else: + diff = None + config_xmls = self.set_config(existing_logging_global_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_logging_global_facts = self.get_logging_global_facts() + + result["before"] = existing_logging_global_facts + if result["changed"]: + result["after"] = changed_logging_global_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_logging_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_logging_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 + """ + self.root = build_root_xml_node("system") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + elif state == "overridden": + config_xmls = self._state_replaced(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + logging_xml = [] + logging_xml.extend(self._state_deleted(want, have)) + logging_xml.extend(self._state_merged(want, have)) + return logging_xml + + 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 + """ + logging_xml = [] + want = remove_empties(want) + level_parser = [ + "any", + "authorization", + "change_log", + "conflict_log", + "daemon", + "dfc", + "external", + "firewall", + "ftp", + "interactive_commands", + "kernel", + "ntp", + "pfe", + "security", + "user", + ] + want = remove_empties(want) + logging_node = build_root_xml_node("syslog") + + # add allow-duplicates node + if "allow_duplicates" in want.keys() and want.get("allow_duplicates"): + build_child_xml_node(logging_node, "allow-duplicates") + + # add archive node + if "archive" in want.keys(): + self.render_archive(logging_node, want) + + # add console node + if "console" in want.keys(): + console = want.get("console") + # add any level node + for k, v in iteritems(console): + if v is not None: + console_node = build_child_xml_node( + logging_node, + "console", + ) + build_child_xml_node( + console_node, + "name", + k.replace("_", "-"), + ) + build_child_xml_node(console_node, v.get("level")) + + # add file node + if "files" in want.keys(): + files = want.get("files") + for file in files: + file_node = build_child_xml_node(logging_node, "file") + # add name node + build_child_xml_node(file_node, "name", file.get("name")) + # add allow-duplicates node + if "allow_duplicates" in file.keys() and file.get( + "allow_duplicates", + ): + build_child_xml_node(file_node, "allow-duplicates") + # add contents + for k, v in iteritems(file): + if k in level_parser and v is not None: + content_node = build_child_xml_node( + file_node, + "contents", + ) + build_child_xml_node( + content_node, + "name", + k.replace("_", "-"), + ) + build_child_xml_node(content_node, v.get("level")) + # add archive node + if "archive" in file.keys(): + self.render_archive(file_node, file) + # add explicit-priority + if "explicit_priority" in file.keys() and file.get( + "explicit_priority", + ): + build_child_xml_node(file_node, "explicit-priority") + # add match node + if "match" in file.keys(): + build_child_xml_node(file_node, "match", file.get("match")) + # add match-strings node + if "match_strings" in file.keys(): + match_strings = file.get("match_strings") + for match in match_strings: + build_child_xml_node(file_node, "match-strings", match) + # add structured-data + if "structured_data" in file.keys(): + structured_data = file.get("structured_data") + s_data_node = build_child_xml_node( + file_node, + "structured-data", + ) + if "brief" in structured_data.keys() and structured_data.get("brief"): + build_child_xml_node(s_data_node, "brief") + + # add host node + if "hosts" in want.keys(): + hosts = want.get("hosts") + for host in hosts: + host_node = build_child_xml_node(logging_node, "host") + # add name node + build_child_xml_node(host_node, "name", host.get("name")) + # add allow-duplicates node + if "allow_duplicates" in host.keys() and host.get( + "allow_duplicates", + ): + build_child_xml_node(host_node, "allow-duplicates") + # add contents + for k, v in iteritems(host): + if k in level_parser and v is not None: + content_node = build_child_xml_node( + host_node, + "contents", + ) + build_child_xml_node( + content_node, + "name", + k.replace("_", "-"), + ) + build_child_xml_node(content_node, v.get("level")) + # add exclude-hostname node + if "exclude_hostname" in host.keys() and host.get( + "exclude_hostname", + ): + build_child_xml_node(host_node, "exclude-hostname") + # add facility_override node + if "facility_override" in host.keys(): + build_child_xml_node( + host_node, + "facility-override", + host.get("facility_override"), + ) + # add log_prefix node + if "log_prefix" in host.keys(): + build_child_xml_node( + host_node, + "log-prefix", + host.get("log_prefix"), + ) + # add match node + if "match" in host.keys(): + build_child_xml_node(host_node, "match", host.get("match")) + # add match-strings node + if "match_strings" in host.keys(): + match_strings = host.get("match_strings") + for match in match_strings: + build_child_xml_node(host_node, "match-strings", match) + # add port node + if "port" in host.keys(): + build_child_xml_node(host_node, "port", host.get("port")) + # add routing_instance node + if "routing_instance" in host.keys(): + build_child_xml_node( + host_node, + "routing-instance", + host.get("routing_instance"), + ) + # add source_address node + if "source_address" in host.keys(): + build_child_xml_node( + host_node, + "source-address", + host.get("source_address"), + ) + # add structured-data + if "structured_data" in host.keys(): + structured_data = host.get("structured_data") + if "set" not in structured_data.keys() or structured_data.get("set"): + s_data_node = build_child_xml_node( + host_node, + "structured-data", + ) + if "brief" in structured_data.keys() and structured_data.get("brief"): + build_child_xml_node(s_data_node, "brief") + + # add log_rotate_frequency node + if "log_rotate_frequency" in want.keys(): + build_child_xml_node( + logging_node, + "log-rotate-frequency", + want.get("log_rotate_frequency"), + ) + + # add routing_instance node + if "routing_instance" in want.keys(): + build_child_xml_node( + logging_node, + "routing-instance", + want.get("routing_instance"), + ) + + # add server node + if "server" in want.keys(): + server = want.get("server") + if "set" not in server.keys() or server.get("set"): + server_node = build_child_xml_node(logging_node, "server") + if "routing_instance" in server.keys(): + routing_instance = server.get("routing_instance") + if "all" in routing_instance.keys() and routing_instance.get( + "all", + ): + build_child_xml_node(server_node, "all") + if "default" in routing_instance.keys() and routing_instance.get("default"): + build_child_xml_node(server_node, "default") + if "routing_instances" in routing_instance.keys(): + r_instances = routing_instance.get("routing_instances") + for instance in r_instances: + instance_node = build_child_xml_node( + server_node, + "name", + instance.get("name"), + ) + if "disable" in instance.keys() and instance.get( + "disable", + ): + build_child_xml_node(instance_node, "disable") + + # add source_address node + if "source_address" in want.keys(): + build_child_xml_node( + logging_node, + "source-address", + want.get("source_address"), + ) + + # add time_format + if "time_format" in want.keys(): + time_format = want.get("time_format") + + time_node = build_child_xml_node(logging_node, "time-format") + if "millisecond" in time_format.keys() and time_format.get( + "millisecond", + ): + build_child_xml_node(time_node, "millisecond") + if "year" in time_format.keys() and time_format.get("year"): + build_child_xml_node(time_node, "year") + + # add user node + if "users" in want.keys(): + users = want.get("users") + for user in users: + user_node = build_child_xml_node(logging_node, "user") + # add name node + build_child_xml_node(user_node, "name", user.get("name")) + # add allow-duplicates node + if "allow_duplicates" in user.keys() and user.get( + "allow_duplicates", + ): + build_child_xml_node(user_node, "allow-duplicates") + # add contents + for k, v in iteritems(user): + if k in level_parser and v is not None: + content_node = build_child_xml_node( + user_node, + "contents", + ) + build_child_xml_node( + content_node, + "name", + k.replace("_", "-"), + ) + build_child_xml_node(content_node, v.get("level")) + # add match node + if "match" in user.keys(): + build_child_xml_node(user_node, "match", user.get("match")) + # add match-strings node + if "match_strings" in user.keys(): + match_strings = user.get("match_strings") + for match in match_strings: + build_child_xml_node(user_node, "match-strings", match) + + if logging_node is not None: + logging_xml.append(logging_node) + return logging_xml + + 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 + """ + logging_xml = [] + logging_root = None + delete = {"delete": "delete"} + if have is not None: + logging_root = build_child_xml_node( + self.root, + "syslog", + None, + delete, + ) + + if logging_root is not None: + logging_xml.append(logging_root) + return logging_xml + + def render_archive(self, root, want): + archive = want.get("archive") + archive_node = build_child_xml_node(root, "archive") + + # add binary-data node + if "binary_data" in archive.keys() and archive.get("binary_data"): + build_child_xml_node(archive_node, "binary-data") + # add files node + if "files" in archive.keys(): + build_child_xml_node(archive_node, "files", archive.get("files")) + # add no-binary-data node + if "no_binary_data" in archive.keys() and archive.get( + "no_binary_data", + ): + build_child_xml_node(archive_node, "no-binary-data") + # add size node + if "file_size" in archive.keys(): + build_child_xml_node( + archive_node, + "size", + archive.get("file_size"), + ) + # add world-readable node + if "world_readable" in archive.keys() and archive.get( + "world_readable", + ): + build_child_xml_node(archive_node, "world-readable") + # add no-world-readable node + if "no_world_readable" in archive.keys() and archive.get( + "no_world_readable", + ): + build_child_xml_node(archive_node, "no-world-readable") diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/ntp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/ntp_global.py new file mode 100644 index 000000000..60da74702 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ntp_global/ntp_global.py @@ -0,0 +1,388 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_ntp_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Ntp_global(ConfigBase): + """ + The junos_ntp_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ntp_global"] + + def __init__(self, module): + super(Ntp_global, self).__init__(module) + + def get_ntp_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, + ) + ntp_global_facts = facts["ansible_network_resources"].get("ntp_global") + if not ntp_global_facts: + return {} + return ntp_global_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_ntp_global_facts = self.get_ntp_global_facts() + else: + existing_ntp_global_facts = {} + if state == "gathered": + existing_ntp_global_facts = self.get_ntp_global_facts() + result["gathered"] = existing_ntp_global_facts + 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_ntp_global_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_ntp_global_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + + else: + diff = None + config_xmls = self.set_config(existing_ntp_global_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_ntp_global_facts = self.get_ntp_global_facts() + + result["before"] = existing_ntp_global_facts + if result["changed"]: + result["after"] = changed_ntp_global_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_ntp_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_ntp_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 + """ + self.root = build_root_xml_node("system") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + elif state == "overridden": + config_xmls = self._state_replaced(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + ntp_xml = [] + ntp_xml.extend(self._state_deleted(want, have)) + ntp_xml.extend(self._state_merged(want, have)) + return ntp_xml + + 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 + """ + ntp_xml = [] + want = remove_empties(want) + ntp_node = build_root_xml_node("ntp") + + # add authentication-keys node + if "authentication_keys" in want.keys(): + auth_keys = want.get("authentication_keys") + for key in auth_keys: + key_node = build_child_xml_node(ntp_node, "authentication-key") + # add name node + build_child_xml_node(key_node, "name", key.get("id")) + # add type node + build_child_xml_node(key_node, "type", key.get("algorithm")) + # add value node + build_child_xml_node(key_node, "value", key.get("key")) + + # add boot_server node + if "boot_server" in want.keys(): + build_child_xml_node( + ntp_node, + "boot-server", + want.get("boot_server"), + ) + + # add broadcast node + if "broadcasts" in want.keys(): + broadcasts = want.get("broadcasts") + for item in broadcasts: + broadcast_node = build_child_xml_node(ntp_node, "broadcast") + # add name node + build_child_xml_node( + broadcast_node, + "name", + item.get("address"), + ) + # add key node + if "key" in item.keys(): + build_child_xml_node( + broadcast_node, + "key", + item.get("key"), + ) + # add routing-instance-name node + if "routing_instance_name" in item.keys(): + build_child_xml_node( + broadcast_node, + "routing-instance-name", + item.get("routing_instance_name"), + ) + # add ttl node + if "ttl" in item.keys(): + build_child_xml_node( + broadcast_node, + "ttl", + item.get("ttl"), + ) + # add version node + if "version" in item.keys(): + build_child_xml_node( + broadcast_node, + "version", + item.get("version"), + ) + + # add broadcast_client node + if "broadcast_client" in want.keys() and want.get("broadcast_client"): + build_child_xml_node(ntp_node, "broadcast-client") + + # add interval_range node + if "interval_range" in want.keys(): + build_child_xml_node( + ntp_node, + "interval-range", + want.get("interval_range"), + ) + + # add multicast_client node + if "multicast_client" in want.keys(): + build_child_xml_node( + ntp_node, + "multicast-client", + want.get("multicast_client"), + ) + + # add peers node + if "peers" in want.keys(): + peers = want.get("peers") + for item in peers: + peer_node = build_child_xml_node(ntp_node, "peer") + # add name node + build_child_xml_node(peer_node, "name", item.get("peer")) + # add key node + if "key_id" in item.keys(): + build_child_xml_node(peer_node, "key", item.get("key_id")) + # add prefer node + if "prefer" in item.keys() and item.get("prefer"): + build_child_xml_node(peer_node, "prefer") + # add version node + if "version" in item.keys(): + build_child_xml_node( + peer_node, + "version", + item.get("version"), + ) + + # add server node + if "servers" in want.keys(): + servers = want.get("servers") + for item in servers: + server_node = build_child_xml_node(ntp_node, "server") + # add name node + build_child_xml_node(server_node, "name", item.get("server")) + # add key node + if "key_id" in item.keys(): + build_child_xml_node( + server_node, + "key", + item.get("key_id"), + ) + # add routing-instance node + if "routing_instance" in item.keys(): + build_child_xml_node( + server_node, + "routing-instance", + item.get("routing_instance"), + ) + # add prefer node + if "prefer" in item.keys() and item.get("prefer"): + build_child_xml_node(server_node, "prefer") + # add version node + if "version" in item.keys(): + build_child_xml_node( + server_node, + "version", + item.get("version"), + ) + # add source_address node + if "source_addresses" in want.keys(): + source_addresses = want.get("source_addresses") + for item in source_addresses: + source_node = build_child_xml_node(ntp_node, "source-address") + # add name node + build_child_xml_node( + source_node, + "name", + item.get("source_address"), + ) + # add routing-instance node + if "routing_instance" in item.keys(): + build_child_xml_node( + source_node, + "routing-instance", + item.get("routing_instance"), + ) + # add threshold node + if "threshold" in want.keys(): + threshold = want.get("threshold") + threshold_node = build_child_xml_node(ntp_node, "threshold") + if "value" in threshold.keys(): + build_child_xml_node( + threshold_node, + "value", + threshold.get("value"), + ) + if "action" in threshold.keys(): + build_child_xml_node( + threshold_node, + "action", + threshold.get("action"), + ) + + # add trusted key + if "trusted_keys" in want.keys(): + trusted_keys = want.get("trusted_keys") + for key in trusted_keys: + build_child_xml_node(ntp_node, "trusted-key", key["key_id"]) + + if ntp_node is not None: + ntp_xml.append(ntp_node) + return ntp_xml + + 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 + """ + ntp_xml = [] + ntp_root = None + delete = {"delete": "delete"} + if have is not None: + ntp_root = build_child_xml_node(self.root, "ntp", None, delete) + + if ntp_root is not None: + ntp_xml.append(ntp_root) + return ntp_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/ospf_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..2cbc8f24f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,362 @@ +# +# -*- 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 junos_ospf_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Ospf_interfaces(ConfigBase): + """ + The junos_ospf_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospf_interfaces"] + + def __init__(self, module): + super(Ospf_interfaces, self).__init__(module) + + def get_ospf_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, + ) + ospf_interfaces_facts = facts["ansible_network_resources"].get( + "junos_ospf_interfaces", + ) + if not ospf_interfaces_facts: + return [] + return ospf_interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_ospf_interfaces_facts = self.get_ospf_interfaces_facts() + else: + existing_ospf_interfaces_facts = [] + if state == "gathered": + existing_ospf_interfaces_facts = self.get_ospf_interfaces_facts() + result["gathered"] = existing_ospf_interfaces_facts + 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_ospf_interfaces_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_ospf_interfaces_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_ospf_interfaces_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_ospf_interfaces_facts = self.get_ospf_interfaces_facts() + + result["before"] = existing_ospf_interfaces_facts + if result["changed"]: + result["after"] = changed_ospf_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_ospf_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_ospf_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 + """ + self.router_id = None + self.root = build_root_xml_node("configuration") + self.protocols = build_child_xml_node(self.root, "protocols") + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + self.protocols.append(xml) + + return [tostring(xml) for xml in self.root.getchildren()] + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_interfaces_xml = [] + ospf_interfaces_xml.extend(self._state_deleted(want, have)) + ospf_interfaces_xml.extend(self._state_merged(want, have)) + return ospf_interfaces_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_interfaces_xml = [] + ospf_interfaces_xml.extend(self._state_deleted(None, have)) + ospf_interfaces_xml.extend(self._state_merged(want, have)) + return ospf_interfaces_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_interfaces_xml = [] + delete = {"delete": "delete"} + protocol = build_root_xml_node("ospf") + if not want: + for h in have: + for af in h["address_family"]: + area_node = build_child_xml_node(protocol, "area") + processes = af.get("processes") + area = processes.get("area") + area_id = area.get("area_id") + build_child_xml_node(area_node, "name", area_id) + ospf_interfacesnode = build_child_xml_node( + area_node, + "interface", + ) + build_child_xml_node( + ospf_interfacesnode, + "name", + h.get("name"), + ) + ospf_interfacesnode.attrib.update(delete) + else: + for w in want: + for h in have: + if h["name"] == w["name"]: + for af in h["address_family"]: + area_node = build_child_xml_node(protocol, "area") + processes = af.get("processes") + area = processes.get("area") + area_id = area.get("area_id") + build_child_xml_node(area_node, "name", area_id) + ospf_interfacesnode = build_child_xml_node( + area_node, + "interface", + ) + build_child_xml_node( + ospf_interfacesnode, + "name", + h.get("name"), + ) + ospf_interfacesnode.attrib.update(delete) + ospf_interfaces_xml.append(protocol) + return ospf_interfaces_xml + + def _state_merged(self, want, have, delete=None): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_interfaces_xml = [] + protocol = build_root_xml_node("ospf") + for ospf_interfaces in want: + ospf_interfaces = remove_empties(ospf_interfaces) + if "router_id" in ospf_interfaces.keys(): + self.router_id = ospf_interfaces.get("router_id") + build_child_xml_node( + self.routing_options, + "router-id", + self.router_id, + ) + for af in ospf_interfaces["address_family"]: + area_node = build_child_xml_node(protocol, "area") + processes = af.get("processes") + area = processes.get("area") + area_id = area.get("area_id") + build_child_xml_node(area_node, "name", area_id) + + intf_node = build_child_xml_node(area_node, "interface") + build_child_xml_node( + intf_node, + "name", + ospf_interfaces.get("name"), + ) + if delete: + if have: + existing_config = have[0] + if existing_config["name"] == ospf_interfaces["name"]: + intf_node.attrib.update(delete) + if processes.get("priority"): + build_child_xml_node( + intf_node, + "priority", + processes.get("priority"), + ) + if processes.get("flood_reduction"): + build_child_xml_node(intf_node, "flood-reduction") + + if processes.get("metric"): + build_child_xml_node( + intf_node, + "metric", + processes.get("metric"), + ) + + if processes.get("passive"): + build_child_xml_node(intf_node, "passive") + + if processes.get("bandwidth_based_metrics"): + bw_metrics_node = build_child_xml_node( + intf_node, + "bandwidth-based-metrics", + ) + bw_metrics = processes.get("bandwidth_based_metrics") + for bw_metric in bw_metrics: + bw_metric_node = build_child_xml_node( + bw_metrics_node, + "bandwidth", + ) + build_child_xml_node( + bw_metric_node, + "name", + bw_metric.get("bandwidth"), + ) + build_child_xml_node( + bw_metric_node, + "metric", + bw_metric.get("metric"), + ) + if processes.get("dead_interval"): + build_child_xml_node( + intf_node, + "dead-interval", + processes.get("dead_interval"), + ) + if processes.get("hello_interval"): + build_child_xml_node( + intf_node, + "hello-interval", + processes.get("hello_interval"), + ) + if processes.get("poll_interval"): + build_child_xml_node( + intf_node, + "poll-interval", + processes.get("poll_interval"), + ) + if processes.get("retransmit_interval"): + build_child_xml_node( + intf_node, + "retransmit-interval", + processes.get("retransmit_interval"), + ) + + ospf_interfaces_xml.append(protocol) + return ospf_interfaces_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/ospfv2.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/ospfv2.py new file mode 100644 index 000000000..23730746f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv2/ospfv2.py @@ -0,0 +1,439 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +The junos_ospfv2 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Ospfv2(ConfigBase): + """ + The junos_ospfv2 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospf"] + + def __init__(self, module): + super(Ospfv2, self).__init__(module) + + def get_ospf_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, + ) + ospf_facts = facts["ansible_network_resources"].get("ospfv2") + if not ospf_facts: + return [] + return ospf_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_ospf_facts = self.get_ospf_facts() + else: + existing_ospf_facts = [] + if state == "gathered": + existing_ospf_facts = self.get_ospf_facts() + result["gathered"] = existing_ospf_facts + 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_ospf_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_ospf_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_ospf_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if config_xmls and diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_ospf_facts = self.get_ospf_facts() + + result["before"] = existing_ospf_facts + if result["changed"]: + result["after"] = changed_ospf_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_ospf_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_ospf_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 + """ + self.router_id = None + self.root = build_root_xml_node("configuration") + self.protocols = build_child_xml_node(self.root, "protocols") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + commands = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + if config_xmls: + for xml in config_xmls: + self.protocols.append(xml) + + commands = [tostring(xml) for xml in self.root.getchildren()] + return commands + # return [tostring(xml) for xml in self.root.getchildren()] + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_xml = [] + ospf_xml.extend(self._state_deleted(want, have)) + ospf_xml.extend(self._state_merged(want, have)) + return ospf_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_xml = [] + ospf_xml.extend(self._state_deleted(want, have)) + ospf_xml.extend(self._state_merged(want, have)) + return ospf_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_xml = [] + delete = {"delete": "delete"} + if have: + for have_ospf in have: + have_areas = have_ospf.get("areas") or [] + for area in have_areas: + ospf_node = build_child_xml_node(self.protocols, "ospf") + area_node = build_child_xml_node( + ospf_node, + "area", + area["area_id"], + ) + area_node.attrib.update(delete) + ospf_xml.append(ospf_node) + return ospf_xml + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospf_xml = [] + protocol = build_root_xml_node("ospf") + for ospf in want: + ospf = remove_empties(ospf) + if "router_id" in ospf.keys(): + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + ospf = remove_empties(ospf) + self.router_id = ospf.get("router_id") + build_child_xml_node( + self.routing_options, + "router-id", + self.router_id, + ) + + if ospf.get("spf_options"): + spf_options_node = build_child_xml_node( + protocol, + "spf-options", + ) + + if ospf["spf_options"].get("delay"): + build_child_xml_node( + spf_options_node, + "delay", + ospf["spf_options"].get("delay"), + ) + + if ospf["spf_options"].get("holddown"): + build_child_xml_node( + spf_options_node, + "holddown", + ospf["spf_options"].get("holddown"), + ) + + if ospf["spf_options"].get("delay"): + build_child_xml_node( + spf_options_node, + "rapid-runs", + ospf["spf_options"].get("rapid_runs"), + ) + + if ospf.get("overload"): + overload_node = build_child_xml_node(protocol, "overload") + if ospf["overload"].get("timeout"): + build_child_xml_node( + overload_node, + "timeout", + ospf["overload"].get("timeout"), + ) + + if ospf.get("external_preference"): + build_child_xml_node( + protocol, + "external-preference", + ospf["external_preference"], + ) + + if ospf.get("preference"): + build_child_xml_node( + protocol, + "preference", + ospf["preference"], + ) + + if ospf.get("prefix_export_limit"): + build_child_xml_node( + protocol, + "prefix-export-limit", + ospf["prefix_export_limit"], + ) + + if ospf.get("reference_bandwidth"): + build_child_xml_node( + protocol, + "reference-bandwidth", + ospf.get("reference_bandwidth"), + ) + + if "rfc1583compatibility" in ospf.keys(): + if not ospf["rfc1583compatibility"]: + build_child_xml_node(protocol, "no-rfc-1583") + + if "areas" in ospf.keys(): + for area in ospf["areas"]: + area_node = build_child_xml_node(protocol, "area") + area_id = area.get("area_id") + build_child_xml_node(area_node, "name", area_id) + if area.get("area_range"): + area_range_node = build_child_xml_node( + area_node, + "area-range", + ) + build_child_xml_node( + area_range_node, + "name", + area["area_range"], + ) + + for intf in area.get("interfaces"): + intf_node = build_child_xml_node( + area_node, + "interface", + ) + build_child_xml_node( + intf_node, + "name", + intf.get("name"), + ) + + if intf.get("priority"): + build_child_xml_node( + intf_node, + "priority", + intf.get("priority"), + ) + + if intf.get("flood_reduction"): + build_child_xml_node( + intf_node, + "flood-reduction", + None, + ) + + if intf.get("metric"): + build_child_xml_node( + intf_node, + "metric", + intf["metric"], + ) + + if intf.get("passive"): + build_child_xml_node(intf_node, "passive") + + if intf.get("bandwidth_based_metrics"): + bw_metrics_node = build_child_xml_node( + intf_node, + "bandwidth-based-metrics", + ) + bw_metrics = intf.get("bandwidth_based_metrics") + for bw_metric in bw_metrics: + bw_metric_node = build_child_xml_node( + bw_metrics_node, + "bandwidth", + ) + build_child_xml_node( + bw_metric_node, + "name", + bw_metric.get("bandwidth"), + ) + build_child_xml_node( + bw_metric_node, + "metric", + bw_metric.get("metric"), + ) + if intf.get("timers"): + if intf["timers"].get("dead_interval"): + build_child_xml_node( + intf_node, + "dead-interval", + intf["timers"].get("dead_interval"), + ) + if intf["timers"].get("hello_interval"): + build_child_xml_node( + intf_node, + "hello-interval", + intf["timers"].get("hello_interval"), + ) + if intf["timers"].get("poll_interval"): + build_child_xml_node( + intf_node, + "poll-interval", + intf["timers"].get("poll_interval"), + ) + if intf["timers"].get("retransmit_interval"): + build_child_xml_node( + intf_node, + "retransmit-interval", + intf["timers"].get("retransmit_interval"), + ) + + if area.get("stub"): + if area["stub"]["set"]: + stub_node = build_child_xml_node(area_node, "stub") + if area["stub"].get("default_metric"): + build_child_xml_node( + stub_node, + "default-metric", + area["stub"].get("default_metric"), + ) + + ospf_xml.append(protocol) + return ospf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/ospfv3.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/ospfv3.py new file mode 100644 index 000000000..a9aea34ec --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/ospfv3/ospfv3.py @@ -0,0 +1,454 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + +""" +The junos_ospfv3 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Ospfv3(ConfigBase): + """ + The junos_ospfv3 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospfv3"] + + def __init__(self, module): + super(Ospfv3, self).__init__(module) + + def get_ospfv3_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + ospfv3_facts = facts["ansible_network_resources"].get("junos_ospfv3") + if not ospfv3_facts: + return [] + return ospfv3_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_ospfv3_facts = self.get_ospfv3_facts() + else: + existing_ospfv3_facts = [] + if state == "gathered": + existing_ospfv3_facts = self.get_ospfv3_facts() + result["gathered"] = existing_ospfv3_facts + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_ospfv3_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_ospfv3_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_ospfv3_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_ospfv3_facts = self.get_ospfv3_facts() + + result["before"] = existing_ospfv3_facts + if result["changed"]: + result["after"] = changed_ospfv3_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_ospfv3_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_ospfv3_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, 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 + """ + self.router_id = None + self.root = build_root_xml_node("configuration") + self.protocols = build_child_xml_node(self.root, "protocols") + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + self.protocols.append(xml) + + return [tostring(xml) for xml in self.root.getchildren()] + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospfv3_xml = [] + ospfv3_xml.extend(self._state_deleted(want, have)) + ospfv3_xml.extend(self._state_merged(want, have)) + return ospfv3_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospfv3_xml = [] + ospfv3_xml.extend(self._state_deleted(None, have)) + ospfv3_xml.extend(self._state_merged(want, have)) + return ospfv3_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospfv3_xml = [] + delete = {"delete": "delete"} + + if not want: + ospfv3node = build_child_xml_node(self.protocols, "ospf3") + ospfv3node.attrib.update(delete) + if have: + router_id = have[0].get("router_id") + if router_id: + build_child_xml_node( + self.routing_options, + "router-id", + self.router_id, + attrib=delete, + ) + return ospfv3node + + ospfv3_xml = self._state_merged(want, have, delete=delete) + return ospfv3_xml + + def _state_merged(self, want, have, delete=None): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + ospfv3_xml = [] + protocol = build_root_xml_node("ospf3") + for ospfv3 in want: + ospfv3 = remove_empties(ospfv3) + if "router_id" in ospfv3.keys(): + self.router_id = ospfv3.get("router_id") + build_child_xml_node( + self.routing_options, + "router-id", + self.router_id, + ) + + if ospfv3.get("spf_options"): + spf_options_node = build_child_xml_node( + protocol, + "spf-options", + ) + if delete and not ospfv3.get("spf_options").values(): + spf_options_node.attrib.update(delete) + + if ospfv3["spf_options"].get("delay"): + delay_node = build_child_xml_node( + spf_options_node, + "delay", + ospfv3["spf_options"].get("delay"), + ) + if delete: + delay_node.attrib.update(delete) + + if ospfv3["spf_options"].get("holddown"): + holddown_node = build_child_xml_node( + spf_options_node, + "holddown", + ospfv3["spf_options"].get("holddown"), + ) + if delete: + holddown_node.attrib.update(delete) + + if ospfv3["spf_options"].get("delay"): + delay_node = build_child_xml_node( + spf_options_node, + "rapid-runs", + ospfv3["spf_options"].get("rapid_runs"), + ) + if delete: + delay_node.attrib.update(delete) + + if ospfv3.get("overload"): + overload_node = build_child_xml_node(protocol, "overload") + if ospfv3["overload"].get("timeout"): + build_child_xml_node( + overload_node, + "timeout", + ospfv3["overload"].get("timeout"), + ) + if delete: + overload_node.attrib.update(delete) + + if ospfv3.get("external_preference"): + ext_pref_node = build_child_xml_node( + protocol, + "external-preference", + ospfv3["externall_preference"], + ) + if delete: + ext_pref_node.attrib.update(delete) + + if ospfv3.get("preference"): + pref_node = build_child_xml_node( + protocol, + "preference", + ospfv3["preference"], + ) + if delete: + pref_node.attrib.update(delete) + + if ospfv3.get("prefix_export_limit"): + prefix_export_node = build_child_xml_node( + protocol, + "prefix-export-limit", + ospfv3["prefix_export_limit"], + ) + if delete: + prefix_export_node.attrib.update(delete) + + if ospfv3.get("reference_bandwidth"): + ref_bw_node = build_child_xml_node( + protocol, + "reference-bandwidth", + ospfv3.get("reference_bandwidth"), + ) + if delete: + ref_bw_node.attrib.update(delete) + + if "rfc1583compatibility" in ospfv3.keys(): + if not ospfv3["rfc1583compatibility"]: + build_child_xml_node(protocol, "no-rfc-1583") + + for area in ospfv3["areas"]: + area_node = build_child_xml_node(protocol, "area") + area_id = area.get("area_id") + build_child_xml_node(area_node, "name", area_id) + if area.get("area_range"): + area_range_node = build_child_xml_node( + area_node, + "area-range", + ) + build_child_xml_node( + area_range_node, + "name", + area["area_range"], + ) + if delete: + area_range_node.attrib.update(delete) + + for intf in area.get("interfaces"): + intf_node = build_child_xml_node(area_node, "interface") + build_child_xml_node(intf_node, "name", intf.get("name")) + if delete: + if have: + existing_config = have[0] + for existing_area in existing_config["areas"]: + if existing_area["area_id"] == area_id: + if len(existing_area["interfaces"]) == 1: + area_node.attrib.update(delete) + else: + intf_node.attrib.update(delete) + + if intf.get("priority"): + build_child_xml_node( + intf_node, + "priority", + intf.get("priority"), + ) + + if intf.get("flood_reduction"): + build_child_xml_node(intf_node, "flood-reduction") + + if intf.get("metric"): + build_child_xml_node( + intf_node, + "metric", + intf["metric"], + ) + + if intf.get("passive"): + build_child_xml_node(intf_node, "passive") + + if intf.get("bandwidth_based_metrics"): + bw_metrics_node = build_child_xml_node( + intf_node, + "bandwidth-based-metrics", + ) + bw_metrics = intf.get("bandwidth_based_metrics") + for bw_metric in bw_metrics: + bw_metric_node = build_child_xml_node( + bw_metrics_node, + "bandwidth", + ) + build_child_xml_node( + bw_metric_node, + "name", + bw_metric.get("bandwidth"), + ) + build_child_xml_node( + bw_metric_node, + "metric", + bw_metric.get("metric"), + ) + if intf.get("timers"): + if intf["timers"].get("dead_interval"): + build_child_xml_node( + intf_node, + "dead-interval", + intf["timers"].get("dead_interval"), + ) + if intf["timers"].get("hello_interval"): + build_child_xml_node( + intf_node, + "hello-interval", + intf["timers"].get("hello_interval"), + ) + if intf["timers"].get("poll_interval"): + build_child_xml_node( + intf_node, + "poll-interval", + intf["timers"].get("poll_interval"), + ) + if intf["timers"].get("retransmit_interval"): + build_child_xml_node( + intf_node, + "retransmit-interval", + intf["timers"].get("retransmit_interval"), + ) + + if area.get("stub"): + if area["stub"]["set"]: + stub_node = build_child_xml_node(area_node, "stub") + if area["stub"].get("default_metric"): + build_child_xml_node( + stub_node, + "default-metric", + area["stub"].get("default_metric"), + ) + ospfv3_xml.append(protocol) + return ospfv3_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/prefix_lists.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..e98a272d8 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/prefix_lists/prefix_lists.py @@ -0,0 +1,280 @@ +# +# -*- 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 junos_prefix_lists 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Prefix_lists(ConfigBase): + """ + The junos_prefix_lists class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["prefix_lists"] + + def __init__(self, module): + super(Prefix_lists, self).__init__(module) + + def get_prefix_lists_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, + ) + prefix_lists_facts = facts["ansible_network_resources"].get( + "prefix_lists", + ) + if not prefix_lists_facts: + return [] + return prefix_lists_facts + + def execute_module(self): + """Execute the module + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_prefix_lists_facts = self.get_prefix_lists_facts() + else: + existing_prefix_lists_facts = {} + if state == "gathered": + existing_prefix_lists_facts = self.get_prefix_lists_facts() + result["gathered"] = existing_prefix_lists_facts + 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_prefix_lists_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_prefix_lists_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + diff = None + config_xmls = self.set_config(existing_prefix_lists_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_prefix_lists_facts = self.get_prefix_lists_facts() + + result["before"] = existing_prefix_lists_facts + if result["changed"]: + result["after"] = changed_prefix_lists_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_prefix_lists_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_prefix_lists_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 + """ + self.root = build_root_xml_node("policy-options") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + elif state == "overridden": + config_xmls = self._state_overridden(want, have) + + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + rinst_xml = [] + rinst_xml.extend(self._state_deleted(want, have)) + rinst_xml.extend(self._state_merged(want, have)) + + return rinst_xml + + 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 + """ + plist_xml = [] + delete = {"delete": "delete"} + if have is not None: + have_plist = [entry["name"] for entry in have] + want_plist = [entry["name"] for entry in want] + + for instance in have_plist: + if instance not in want_plist: + plist_node = build_root_xml_node("prefix-list") + build_child_xml_node(plist_node, "name", instance) + plist_node.attrib.update(delete) + plist_xml.append(plist_node) + plist_xml.extend(self._state_deleted(want, have)) + plist_xml.extend(self._state_merged(want, have)) + + return plist_xml + + 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 + """ + prefix_list_xml = [] + for instance in want: + instance = remove_empties(instance) + + # generate node: prefix-list + prefix_root = build_root_xml_node("prefix-list") + + # generate node: name + if "name" in instance.keys(): + build_child_xml_node(prefix_root, "name", instance.get("name")) + + # generate node: prefix-list-item + if "address_prefixes" in instance.keys(): + address_prefixes = instance.get("address_prefixes") + + for entry in address_prefixes: + add_prefix_node = build_child_xml_node( + prefix_root, + "prefix-list-item", + ) + # generate name node + build_child_xml_node(add_prefix_node, "name", entry) + + # generate node: dynamic_db + if "dynamic_db" in instance.keys() and instance.get("dynamic_db"): + build_child_xml_node(prefix_root, "dynamic-db") + + prefix_list_xml.append(prefix_root) + + return prefix_list_xml + + 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 + """ + plist_xml = [] + existing_plist = [] + plist_node = None + delete = {"delete": "delete"} + if have is not None: + # get the instances in running config + # form existing instance list + for h_rinst in have: + existing_plist.append(h_rinst["name"]) + + # Delete target routing-instance + if want: + for entry in want: + if entry["name"] not in existing_plist: + continue + plist_node = build_root_xml_node("prefix-list") + build_child_xml_node(plist_node, "name", entry["name"]) + plist_node.attrib.update(delete) + plist_xml.append(plist_node) + + else: + # Delete all the routing-instance + plist_node = build_root_xml_node("prefix-list") + plist_node.attrib.update(delete) + plist_xml.append(plist_node) + + if plist_node is not None: + plist_xml.append(plist_node) + return plist_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/routing_instances.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/routing_instances.py new file mode 100644 index 000000000..445454d57 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_instances/routing_instances.py @@ -0,0 +1,365 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_routing_instances 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Routing_instances(ConfigBase): + """ + The junos_routing_instances class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["routing_instances"] + + def __init__(self, module): + super(Routing_instances, self).__init__(module) + + def get_routing_instances_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, + ) + routing_instances_facts = facts["ansible_network_resources"].get( + "routing_instances", + ) + if not routing_instances_facts: + return [] + return routing_instances_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_routing_instances_facts = self.get_routing_instances_facts() + else: + existing_routing_instances_facts = {} + if state == "gathered": + existing_routing_instances_facts = self.get_routing_instances_facts() + result["gathered"] = existing_routing_instances_facts + 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_routing_instances_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_routing_instances_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + diff = None + config_xmls = self.set_config(existing_routing_instances_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_routing_instances_facts = self.get_routing_instances_facts() + + result["before"] = existing_routing_instances_facts + if result["changed"]: + result["after"] = changed_routing_instances_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_routing_instances_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_routing_instances_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 + """ + self.root = build_root_xml_node("routing-instances") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + elif state == "overridden": + config_xmls = self._state_overridden(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + rinst_xml = [] + rinst_xml.extend(self._state_deleted(want, have)) + rinst_xml.extend(self._state_merged(want, have)) + + return rinst_xml + + 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 + """ + rinst_xml = [] + delete = {"delete": "delete"} + if have is not None: + have_rinst = [instance["name"] for instance in have] + want_rinst = [instance["name"] for instance in want] + + for instance in have_rinst: + if instance not in want_rinst: + rinstance_node = build_root_xml_node("instance") + build_child_xml_node(rinstance_node, "name", instance) + rinstance_node.attrib.update(delete) + rinst_xml.append(rinstance_node) + rinst_xml.extend(self._state_deleted(want, have)) + rinst_xml.extend(self._state_merged(want, have)) + + return rinst_xml + + 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 + """ + instance_xml = [] + rinst_node = None + for instance in want: + rinst_node = build_root_xml_node("instance") + + # add node routing table-name + build_child_xml_node(rinst_node, "name", instance["name"]) + + # add node connector-id-advertise + if instance.get("connector_id_advertise"): + build_child_xml_node(rinst_node, "connector-id-advertise") + + # add node description + if instance.get("description"): + build_child_xml_node( + rinst_node, + "description", + instance["description"], + ) + + # add node instance-role + if instance.get("instance_role"): + build_child_xml_node( + rinst_node, + "instance-role", + instance["insatnce_role"], + ) + + # add node instance-type + if instance.get("type"): + build_child_xml_node( + rinst_node, + "instance-type", + instance["type"], + ) + + # add child node interface + if instance.get("interfaces"): + interfaces = instance.get("interfaces") + for interface in interfaces: + int_node = build_child_xml_node(rinst_node, "interface") + if interface.get("protect_interface"): + build_child_xml_node( + int_node, + "protect-interface", + interface["protect-interface"], + ) + build_child_xml_node(int_node, "name", interface["name"]) + + # add node l2vpn-id TODO + if instance.get("l2vpn_id"): + build_child_xml_node( + rinst_node, + "l2vpn-id", + instance["l2vpn_id"], + ) + + # add node no-irb-layer2-copy + if instance.get("no_irb_layer2_copy"): + build_child_xml_node(rinst_node, "no-irb-layer2-copy") + + # add node no-local-switching + if instance.get("no_local_switching"): + build_child_xml_node(rinst_node, "no-local-switching") + + # add node no-vrf-advertise + if instance.get("no_vrf_advertise"): + build_child_xml_node(rinst_node, "no-vrf-advertise") + + # add node no-vrf-propagate-ttl + if instance.get("no_vrf_propagate_ttl"): + build_child_xml_node(rinst_node, "no-vrf-propagate-ttl") + + # add node qualified-bum-pruning-mode + if instance.get("qualified_bum_pruning_mode"): + build_child_xml_node(rinst_node, "qualified-bum-pruning-mode") + + # add node route-distinguisher + if instance.get("route_distinguisher"): + rd_instance = build_child_xml_node( + rinst_node, + "route-distinguisher", + ) + build_child_xml_node( + rd_instance, + "rd-type", + instance.get("route_distinguisher"), + ) + + # add node vrf-import + if instance.get("vrf_imports"): + vrf_imports = instance.get("vrf_imports") + for vrf in vrf_imports: + build_child_xml_node(rinst_node, "vrf-import", vrf) + + # add node vrf-export + if instance.get("vrf_exports"): + vrf_exports = instance.get("vrf_exports") + for vrf in vrf_exports: + build_child_xml_node(rinst_node, "vrf-export", vrf) + + if rinst_node is not None: + instance_xml.append(rinst_node) + + return instance_xml + + 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 + """ + rinst_xml = [] + existing_rinsts = [] + rinstance_node = None + delete = {"delete": "delete"} + if have is not None: + # get the instances in running config + # form existing instance list + for h_rinst in have: + existing_rinsts.append(h_rinst["name"]) + + # Delete target routing-instance + if want: + for instance in want: + if instance["name"] not in existing_rinsts: + continue + rinstance_node = build_root_xml_node("instance") + build_child_xml_node( + rinstance_node, + "name", + instance["name"], + ) + rinstance_node.attrib.update(delete) + rinst_xml.append(rinstance_node) + + else: + # Delete all the routing-instance + rinstance_node = build_root_xml_node("instance") + rinstance_node.attrib.update(delete) + rinst_xml.append(rinstance_node) + + if rinstance_node is not None: + rinst_xml.append(rinstance_node) + return rinst_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/routing_options.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/routing_options.py new file mode 100644 index 000000000..1bdacb6f6 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/routing_options/routing_options.py @@ -0,0 +1,250 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_routing_options 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Routing_options(ConfigBase): + """ + The junos_routing_options class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["routing_options"] + + def __init__(self, module): + super(Routing_options, self).__init__(module) + + def get_routing_options_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, + ) + routing_options_facts = facts["ansible_network_resources"].get( + "routing_options", + ) + if not routing_options_facts: + return {} + return routing_options_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES: + existing_routing_options_facts = self.get_routing_options_facts() + else: + existing_routing_options_facts = {} + if state == "gathered": + existing_routing_options_facts = self.get_routing_options_facts() + result["gathered"] = existing_routing_options_facts + 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_routing_options_facts( + data=running_config, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_routing_options_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + + else: + diff = None + config_xmls = self.set_config(existing_routing_options_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_routing_options_facts = self.get_routing_options_facts() + + result["before"] = existing_routing_options_facts + if result["changed"]: + result["after"] = changed_routing_options_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_routing_options_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_routing_options_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 + """ + self.root = build_root_xml_node("configuration") + self.routing_options = build_child_xml_node( + self.root, + "routing-options", + ) + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + temp_lst = [] + if state == "deleted": + self._state_deleted(want, have) + elif state in ("merged", "rendered"): + self._state_merged(want, have) + elif state == "replaced": + self._state_replaced(want, have) + elif state == "overridden": + self._state_replaced(want, have) + if self.root is not None: + for xml in self.root.getchildren(): + xml = tostring(xml) + temp_lst.append(xml) + return temp_lst + + 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 + """ + self._state_deleted(want, have) + self._state_merged(want, have) + + 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 + """ + routing_xml = [] + want = remove_empties(want) + + # add authentication-keys node + if "autonomous_system" in want.keys(): + as_num = want.get("autonomous_system") + # Generate xml node for autonomous-system + if as_num.get("as_number"): + as_node = build_child_xml_node( + self.routing_options, + "autonomous-system", + as_num.get("as_number"), + ) + # Add node for loops + if as_num.get("loops"): + build_child_xml_node(as_node, "loops", as_num.get("loops")) + # Add node for asdot_notation + if as_num.get("asdot_notation"): + if "asdot_notation" in as_num.keys(): + build_child_xml_node(as_node, "asdot-notation") + if "router_id" in want.keys(): + build_child_xml_node( + self.routing_options, + "router-id", + want.get("router_id"), + ) + return routing_xml + + 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 + """ + delete = {"delete": "delete"} + if have is not None: + if "autonomous_system" in have.keys(): + build_child_xml_node( + self.routing_options, + "autonomous-system", + None, + delete, + ) + if "router_id" in have.keys(): + build_child_xml_node( + self.routing_options, + "router-id", + None, + delete, + ) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/security_policies.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/security_policies.py new file mode 100644 index 000000000..12933198e --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies/security_policies.py @@ -0,0 +1,796 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_security_policies 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Security_policies(ConfigBase): + """ + The junos_security_policies class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["security_policies"] + + def __init__(self, module): + super(Security_policies, self).__init__(module) + + def get_security_policies_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, + ) + security_policies_facts = facts["ansible_network_resources"].get( + "security_policies", + ) + if not security_policies_facts: + return {} + return security_policies_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_security_policies_facts = self.get_security_policies_facts() + else: + existing_security_policies_facts = {} + if self.state == "gathered": + existing_security_policies_facts = self.get_security_policies_facts() + result["gathered"] = existing_security_policies_facts + + 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_security_policies_facts( + data=running_config, + ) + + elif self.state == "rendered": + config_xmls = self.set_config(existing_security_policies_facts) + if config_xmls: + result["rendered"] = config_xmls + + else: + diff = None + config_xmls = self.set_config(existing_security_policies_facts) + with locked_config(self._module): + diff = load_config(self._module, config_xmls, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_security_policies_facts = self.get_security_policies_facts() + + result["before"] = existing_security_policies_facts + if result["changed"]: + result["after"] = changed_security_policies_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_security_policies_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_security_policies_facts + resp = self.set_state(want, have) + return 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 + """ + self.root = build_root_xml_node("security") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + security_policies_xml = [] + security_policies_xml.extend(self._state_deleted(want, have)) + security_policies_xml.extend(self._state_merged(want, have)) + return security_policies_xml + + 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 + """ + security_policies_xml = [] + security_policies_xml.extend(self._state_deleted(want, have)) + security_policies_xml.extend(self._state_merged(want, have)) + return security_policies_xml + + 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 + """ + security_policies_xml = [] + want = remove_empties(want) + security_policies_node = build_root_xml_node("policies") + + def build_policies(node, policies): + for policy in policies: + policy_node = build_child_xml_node(node, "policy") + + build_child_xml_node(policy_node, "name", policy["name"]) + + if "description" in policy: + build_child_xml_node( + policy_node, + "description", + policy["description"], + ) + + if "scheduler_name" in policy: + build_child_xml_node( + policy_node, + "scheduler-name", + policy["scheduler_name"], + ) + + # add match criteria node + match_node = build_child_xml_node(policy_node, "match") + match = policy["match"] + + for source_address in match["source_address"]: + if source_address == "any_ipv6": + build_child_xml_node( + match_node, + "source-address", + "any-ipv6", + ) + elif source_address == "any_ipv4": + build_child_xml_node( + match_node, + "source-address", + "any-ipv4", + ) + elif source_address == "any": + build_child_xml_node( + match_node, + "source-address", + "any", + ) + elif source_address == "addresses": + for address in match["source_address"]["addresses"]: + build_child_xml_node( + match_node, + "source-address", + address, + ) + + if "source_address_excluded" in match: + build_child_xml_node(match_node, "source-address-excluded") + + for destination_address in match["destination_address"]: + if destination_address == "any_ipv6": + build_child_xml_node( + match_node, + "destination-address", + "any-ipv6", + ) + elif destination_address == "any_ipv4": + build_child_xml_node( + match_node, + "destination-address", + "any-ipv4", + ) + elif destination_address == "any": + build_child_xml_node( + match_node, + "destination-address", + "any", + ) + elif destination_address == "addresses": + for address in match["destination_address"]["addresses"]: + build_child_xml_node( + match_node, + "destination-address", + address, + ) + else: + pass + + if "destination_address_excluded" in match: + build_child_xml_node( + match_node, + "destination-address-excluded", + ) + + for application in match["application"]: + if application == "any": + build_child_xml_node(match_node, "application", "any") + elif application == "names": + for name in match["application"]["names"]: + build_child_xml_node( + match_node, + "application", + name, + ) + + if "source_end_user_profile" in match: + build_child_xml_node( + match_node, + "source-end-user-profile", + match["source_end_user_profile"], + ) + + if "source_identity" in match: + source_identities = match["source_identity"] + for source_identity in source_identities: + if source_identity == "any": + build_child_xml_node( + match_node, + "source-identity", + "any", + ) + if source_identity == "authenticated_user": + build_child_xml_node( + match_node, + "source-identity", + "authenticated-user", + ) + if source_identity == "unauthenticated_user": + build_child_xml_node( + match_node, + "source-identity", + "unauthenticated-user", + ) + if source_identity == "unknown_user": + build_child_xml_node( + match_node, + "source-identity", + "unknown-user", + ) + elif source_identity == "names": + for name in match["source_identity"]["names"]: + build_child_xml_node( + match_node, + "source-identity", + name, + ) + + if "url_category" in match: + url_categories = match["url_category"] + for url_category in url_categories: + if url_category == "any": + build_child_xml_node( + match_node, + "url-category", + "any", + ) + elif url_category == "none": + build_child_xml_node( + match_node, + "url-category", + "none", + ) + elif url_category == "names": + for name in match["url_category"]["names"]: + build_child_xml_node( + match_node, + "url-category", + name, + ) + + if "dynamic_application" in match: + dynamic_applications = match["dynamic_application"] + for dynamic_application in dynamic_applications: + if dynamic_application == "any": + build_child_xml_node( + match_node, + "dynamic-application", + "any", + ) + elif dynamic_application == "none": + build_child_xml_node( + match_node, + "dynamic-application", + "none", + ) + elif dynamic_application == "names": + for name in match["dynamic_application"]["names"]: + build_child_xml_node( + match_node, + "dynamic-application", + name, + ) + + # add action node + then_node = build_child_xml_node(policy_node, "then") + then = policy["then"] + + if "deny" in then: + build_child_xml_node(then_node, "deny") + + if "count" in then: + build_child_xml_node(then_node, "count", " ") + + if "log" in then: + log_node = build_child_xml_node(then_node, "log") + if policy["then"]["log"] and "session_init" in policy["then"]["log"]: + build_child_xml_node(log_node, "session-init") + if policy["then"]["log"] and "session_close" in policy["then"]["log"]: + build_child_xml_node(log_node, "session-close") + + if "reject" in then: + reject = then["reject"] + reject_node = build_child_xml_node( + then_node, + "reject", + " ", + ) + if reject and "profile" in reject: + build_child_xml_node( + reject_node, + "profile", + reject["profile"], + ) + if reject and "ssl_proxy" in reject: + ssl_node = build_child_xml_node( + reject_node, + "ssl-proxy", + " ", + ) + if reject["ssl_proxy"] and "profile_name" in reject["ssl_proxy"]: + build_child_xml_node( + ssl_node, + "profile-name", + reject["ssl_proxy"]["profile_name"], + ) + + if "permit" in then: + permit_node = build_child_xml_node(then_node, "permit") + permit = then["permit"] + if "application_services" in permit: + application_services = permit["application_services"] + application_services_node = build_child_xml_node( + permit_node, + "application-services", + ) + if "advanced_anti_malware_policy" in application_services: + build_child_xml_node( + application_services_node, + "advanced-anti-malware-policy", + application_services["advanced_anti_malware_policy"], + ) + if "application_traffic_control_rule_set" in application_services: + application_traffic_control_node = build_child_xml_node( + application_services_node, + "application-traffic-control", + ) + build_child_xml_node( + application_traffic_control_node, + "rule-set", + application_services["application_traffic_control_rule_set"], + ) + if "gprs_gtp_profile" in application_services: + build_child_xml_node( + application_services_node, + "gprs-gtp-profile", + application_services["gprs_gtp_profile"], + ) + if "gprs_sctp_profile" in application_services: + build_child_xml_node( + application_services_node, + "gprs-sctp-profile", + application_services["gprs_sctp_profile"], + ) + if "icap_redirect" in application_services: + build_child_xml_node( + application_services_node, + "icap-redirect", + application_services["icap_redirect"], + ) + if "idp" in application_services: + build_child_xml_node( + application_services_node, + "idp", + ) + if "idp_policy" in application_services: + build_child_xml_node( + application_services_node, + "idp-policy", + application_services["idp_policy"], + ) + if "redirect_wx" in application_services: + build_child_xml_node( + application_services_node, + "redirect-wx", + ) + if "reverse_redirect_wx" in application_services: + build_child_xml_node( + application_services_node, + "reverse-redirect-wx", + ) + if "security_intelligence_policy" in application_services: + build_child_xml_node( + application_services_node, + "security-intelligence-policy", + application_services["security_intelligence_policy"], + ) + if "ssl_proxy" in application_services: + ssl_node = build_child_xml_node( + application_services_node, + "ssl-proxy", + " ", + ) + if ( + application_services["ssl_proxy"] + and "profile_name" in application_services["ssl_proxy"] + ): + build_child_xml_node( + ssl_node, + "profile-name", + application_services["ssl_proxy"]["profile_name"], + ) + if "uac_policy" in application_services: + uac_node = build_child_xml_node( + application_services_node, + "uac-policy", + " ", + ) + if ( + application_services["uac_policy"] + and "captive_portal" in application_services["uac_policy"] + ): + build_child_xml_node( + uac_node, + "captive-portal", + application_services["uac_policy"]["captive_portal"], + ) + if "utm_policy" in application_services: + build_child_xml_node( + application_services_node, + "utm-policy", + application_services["utm_policy"], + ) + if "destination_address" in permit: + permit_destination_address_node = build_child_xml_node( + permit_node, + "destination-address", + ) + if permit["destination_address"] == "drop-untranslated": + build_child_xml_node( + permit_destination_address_node, + "drop-untranslated", + ) + if permit["destination_address"] == "drop-translated": + build_child_xml_node( + permit_destination_address_node, + "drop-translated", + ) + if "firewall_authentication" in permit: + f_a_node = build_child_xml_node( + permit_node, + "firewall-authentication", + ) + f_a = permit["firewall_authentication"] + if "pass_through" in f_a: + pass_through_node = build_child_xml_node( + f_a_node, + "pass-through", + " ", + ) + if "access_profile" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "access-profile", + f_a["pass_through"]["access_profile"], + ) + if "auth_only_browser" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "auth-only-browser", + " ", + ) + if "auth_user_agent" in f_a["pass_through"]: + if ( + f_a["pass_through"]["auth_user_agent"] + and f_a["pass_through"]["auth_user_agent"] is not True + ): + build_child_xml_node( + pass_through_node, + "auth-user-agent", + f_a["pass_through"]["auth_user_agent"], + ) + else: + build_child_xml_node( + pass_through_node, + "auth-user-agent", + " ", + ) + + if "client_match" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "client-match", + f_a["pass_through"]["client_match"], + ) + if "ssl_termination_profile" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "ssl-termination-profile", + f_a["pass_through"]["ssl_termination_profile"], + ) + if "web_redirect" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "web-redirect", + ) + if "web_redirect_to_https" in f_a["pass_through"]: + build_child_xml_node( + pass_through_node, + "web-redirect-to-https", + ) + if "push_to_identity_management" in f_a: + build_child_xml_node( + f_a_node, + "push-to-identity-management", + ) + if "user_firewall" in f_a: + user_firewall_node = build_child_xml_node( + f_a_node, + "user-firewall", + " ", + ) + if "access_profile" in f_a["user_firewall"]: + build_child_xml_node( + user_firewall_node, + "access-profile", + f_a["user_firewall"]["access_profile"], + ) + if "auth_only_browser" in f_a["user_firewall"]: + if ( + f_a["pass_through"]["auth_user_agent"] + and f_a["pass_through"]["auth_user_agent"] is not True + ): + build_child_xml_node( + pass_through_node, + "auth-user-agent", + f_a["pass_through"]["auth_user_agent"], + ) + else: + build_child_xml_node( + pass_through_node, + "auth-user-agent", + " ", + ) + if "domain" in f_a["user_firewall"]: + build_child_xml_node( + user_firewall_node, + "domain", + f_a["user_firewall"]["domain"], + ) + if "ssl_termination_profile" in f_a["user_firewall"]: + build_child_xml_node( + user_firewall_node, + "ssl-termination-profile", + f_a["user_firewall"]["ssl_termination_profile"], + ) + if "web_redirect" in f_a["user_firewall"]: + build_child_xml_node( + user_firewall_node, + "web-redirect", + " ", + ) + if "web_redirect_to_https" in f_a["user_firewall"]: + build_child_xml_node( + user_firewall_node, + "web-redirect-to-https", + " ", + ) + + if "web_authentication" in f_a: + web_authentication_node = build_child_xml_node( + f_a_node, + "web-authentication", + " ", + ) + for client_match in f_a["web_authentication"]: + build_child_xml_node( + web_authentication_node, + "client-match", + client_match, + ) + + if "tcp_options" in permit: + tcp_options_node = build_child_xml_node( + permit_node, + "tcp-options", + " ", + ) + tcp_options = permit["tcp_options"] + if "initial_tcp_mss" in tcp_options: + build_child_xml_node( + tcp_options_node, + "initial-tcp-mss", + tcp_options["initial_tcp_mss"], + ) + if "reverse_tcp_mss" in tcp_options: + build_child_xml_node( + tcp_options_node, + "reverse-tcp-mss", + tcp_options["reverse_tcp_mss"], + ) + if "sequence_check_required" in tcp_options: + build_child_xml_node( + tcp_options_node, + "sequence-check-required", + ) + if "syn_check_required" in tcp_options: + build_child_xml_node( + tcp_options_node, + "syn-check-required", + ) + if "window_scale" in tcp_options: + build_child_xml_node( + tcp_options_node, + "window-scale", + ) + + if "tunnel" in permit: + tunnel_node = build_child_xml_node( + permit_node, + "tunnel", + " ", + ) + if "ipsec_vpn" in permit["tunnel"]: + build_child_xml_node(tunnel_node, "ipsec-vpn") + if "pair_policy" in permit["tunnel"]: + build_child_xml_node(tunnel_node, "pair-policy") + + # add zone-pair policies + if "from_zones" in want.keys(): + from_zones = want.get("from_zones") + for from_zone in from_zones: + for to_zone in from_zone["to_zones"]: + policy_node = build_child_xml_node( + security_policies_node, + "policy", + ) + build_child_xml_node( + policy_node, + "from-zone-name", + from_zone["name"], + ) + build_child_xml_node( + policy_node, + "to-zone-name", + to_zone["name"], + ) + build_policies(policy_node, to_zone["policies"]) + + # add global policies + if "global" in want.keys(): + global_node = build_child_xml_node( + security_policies_node, + "global", + ) + global_policies = want.get("global").get("policies") + build_policies(global_node, global_policies) + + if security_policies_node is not None: + security_policies_xml.append(security_policies_node) + return security_policies_xml + + 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 + """ + security_policies_xml = [] + security_policies_root = None + delete = {"delete": "delete"} + if have is not None: + security_policies_root = build_child_xml_node( + self.root, + "policies", + None, + delete, + ) + + if security_policies_root is not None: + security_policies_xml.append(security_policies_root) + return security_policies_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/security_policies_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/security_policies_global.py new file mode 100644 index 000000000..5fcbfc8c8 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_policies_global/security_policies_global.py @@ -0,0 +1,394 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_security_policies_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Security_policies_global(ConfigBase): + """ + The junos_security_policies_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["security_policies_global"] + + def __init__(self, module): + super(Security_policies_global, self).__init__(module) + + def get_security_policies_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, + ) + security_policies_global_facts = facts["ansible_network_resources"].get( + "security_policies_global", + ) + if not security_policies_global_facts: + return {} + return security_policies_global_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_security_policies_global_facts = self.get_security_policies_global_facts() + else: + existing_security_policies_global_facts = {} + if self.state == "gathered": + existing_security_policies_global_facts = self.get_security_policies_global_facts() + result["gathered"] = existing_security_policies_global_facts + + 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_security_policies_global_facts( + data=running_config, + ) + + elif self.state == "rendered": + config_xmls = self.set_config( + existing_security_policies_global_facts, + ) + if config_xmls: + result["rendered"] = config_xmls + + else: + diff = None + config_xmls = self.set_config( + existing_security_policies_global_facts, + ) + with locked_config(self._module): + diff = load_config(self._module, config_xmls, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_security_policies_global_facts = self.get_security_policies_global_facts() + + result["before"] = existing_security_policies_global_facts + if result["changed"]: + result["after"] = changed_security_policies_global_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_security_policies_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_security_policies_global_facts + resp = self.set_state(want, have) + return 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 + """ + self.root = build_root_xml_node("security") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + security_policies_global_xml = [] + security_policies_global_xml.extend(self._state_deleted(want, have)) + security_policies_global_xml.extend(self._state_merged(want, have)) + return security_policies_global_xml + + 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 + """ + security_policies_global_xml = [] + security_policies_global_xml.extend(self._state_deleted(want, have)) + security_policies_global_xml.extend(self._state_merged(want, have)) + return security_policies_global_xml + + 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 + """ + security_policies_global_xml = [] + want = remove_empties(want) + security_policies_global_node = build_root_xml_node("policies") + if "default_policy" in want.keys(): + default_policy_node = build_child_xml_node( + security_policies_global_node, + "default-policy", + ) + default_policy = want.get("default_policy") + if default_policy == "deny-all": + build_child_xml_node(default_policy_node, "deny-all") + if default_policy == "permit-all": + build_child_xml_node(default_policy_node, "permit-all") + + if "policy_rematch" in want.keys(): + policy_rematch_node = build_child_xml_node( + security_policies_global_node, + "policy-rematch", + " ", + ) + policy_rematch = want.get("policy_rematch") or {} + if "extensive" in policy_rematch and policy_rematch["extensive"] is True: + build_child_xml_node(policy_rematch_node, "extensive") + + if "policy_stats" in want.keys(): + policy_stats_node = build_child_xml_node( + security_policies_global_node, + "policy-stats", + " ", + ) + policy_stats = want.get("policy_stats") or {} + if "system_wide" in policy_stats: + if policy_stats["system_wide"] is True: + build_child_xml_node( + policy_stats_node, + "system-wide", + "enable", + ) + else: + build_child_xml_node( + policy_stats_node, + "system-wide", + "disable", + ) + + if "pre_id_default_policy_action" in want.keys(): + pre_id_node = build_child_xml_node( + security_policies_global_node, + "pre-id-default-policy", + ) + pre_id_node = build_child_xml_node(pre_id_node, "then") + pre_id = want.get("pre_id_default_policy_action") + + if "log" in pre_id: + log_node = build_child_xml_node(pre_id_node, "log") + if "session_close" in pre_id["log"]: + build_child_xml_node(log_node, "session-close") + if "session_init" in pre_id["log"]: + build_child_xml_node(log_node, "session-init") + + if "session_timeout" in pre_id: + session_timeout_node = build_child_xml_node( + pre_id_node, + "session-timeout", + ) + if "icmp" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "icmp", + pre_id["session_timeout"]["icmp"], + ) + if "icmp6" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "icmp6", + pre_id["session_timeout"]["icmp6"], + ) + if "ospf" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "ospf", + pre_id["session_timeout"]["ospf"], + ) + if "others" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "others", + pre_id["session_timeout"]["others"], + ) + if "tcp" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "tcp", + pre_id["session_timeout"]["tcp"], + ) + if "udp" in pre_id["session_timeout"]: + build_child_xml_node( + session_timeout_node, + "udp", + pre_id["session_timeout"]["udp"], + ) + + if "traceoptions" in want.keys(): + traceoptions_node = build_child_xml_node( + security_policies_global_node, + "traceoptions", + ) + traceoptions = want.get("traceoptions") + + if "file" in traceoptions: + file_node = build_child_xml_node(traceoptions_node, "file") + if "files" in traceoptions["file"]: + build_child_xml_node( + file_node, + "files", + traceoptions["file"]["files"], + ) + if "match" in traceoptions["file"]: + build_child_xml_node( + file_node, + "match", + traceoptions["file"]["match"], + ) + if "size" in traceoptions["file"]: + build_child_xml_node( + file_node, + "size", + traceoptions["file"]["size"], + ) + if "world_readable" in traceoptions["file"]: + build_child_xml_node(file_node, "world-readable") + if "no_world_readable" in traceoptions["file"]: + build_child_xml_node(file_node, "no-world-readable") + + if "flag" in traceoptions: + if traceoptions["flag"] == "all": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "all") + elif traceoptions["flag"] == "configuration": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "configuration") + elif traceoptions["flag"] == "compilation": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "compilation") + elif traceoptions["flag"] == "ipc": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "ipc") + elif traceoptions["flag"] == "lookup": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "lookup") + elif traceoptions["flag"] == "routing-socket": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "routing-socket") + elif traceoptions["flag"] == "rules": + flag_node = build_child_xml_node(traceoptions_node, "flag") + build_child_xml_node(flag_node, "name", "rules") + + if "no_remote_trace" in traceoptions: + build_child_xml_node(traceoptions_node, "no-remote-trace") + + if security_policies_global_node is not None: + security_policies_global_xml.append(security_policies_global_node) + return security_policies_global_xml + + 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 + """ + security_policies_global_xml = [] + security_policies_global_root = None + delete = {"delete": "delete"} + if have is not None: + security_policies_global_root = build_child_xml_node( + self.root, + "policies", + None, + delete, + ) + + if security_policies_global_root is not None: + security_policies_global_xml.append(security_policies_global_root) + return security_policies_global_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/security_zones.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/security_zones.py new file mode 100644 index 000000000..af69c9a45 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/security_zones/security_zones.py @@ -0,0 +1,515 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_security_zones 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Security_zones(ConfigBase): + """ + The junos_security_zones class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["security_zones"] + + def __init__(self, module): + super(Security_zones, self).__init__(module) + + def get_security_zones_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, + ) + security_zones_facts = facts["ansible_network_resources"].get( + "security_zones", + ) + if not security_zones_facts: + return {} + return security_zones_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_security_zones_facts = self.get_security_zones_facts() + else: + existing_security_zones_facts = {} + if self.state == "gathered": + existing_security_zones_facts = self.get_security_zones_facts() + result["gathered"] = existing_security_zones_facts + + 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_security_zones_facts( + data=running_config, + ) + + elif self.state == "rendered": + config_xmls = self.set_config(existing_security_zones_facts) + if config_xmls: + result["rendered"] = config_xmls + + else: + diff = None + config_xmls = self.set_config(existing_security_zones_facts) + with locked_config(self._module): + diff = load_config(self._module, config_xmls, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_security_zones_facts = self.get_security_zones_facts() + + result["before"] = existing_security_zones_facts + if result["changed"]: + result["after"] = changed_security_zones_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_security_zones_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_security_zones_facts + resp = self.set_state(want, have) + return 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 + """ + self.root = build_root_xml_node("security") + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + config_xmls = [] + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + for xml in config_xmls: + self.root.append(xml) + return tostring(self.root) + + 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 + """ + security_zones_xml = [] + security_zones_xml.extend(self._state_deleted(want, have)) + security_zones_xml.extend(self._state_merged(want, have)) + return security_zones_xml + + 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 + """ + security_zones_xml = [] + security_zones_xml.extend(self._state_deleted(want, have)) + security_zones_xml.extend(self._state_merged(want, have)) + return security_zones_xml + + 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 + """ + zones_xml = [] + want = remove_empties(want) + zones_node = build_root_xml_node("zones") + + def build_host_inbound_traffic(node, host_inbound_traffic): + host_inbound_traffic_node = build_child_xml_node( + node, + "host-inbound-traffic", + ) + + if "protocols" in host_inbound_traffic: + for protocol in host_inbound_traffic["protocols"]: + protocol_node = build_child_xml_node( + host_inbound_traffic_node, + "protocols", + ) + build_child_xml_node( + protocol_node, + "name", + protocol["name"], + ) + if "except" in protocol: + build_child_xml_node(protocol_node, "except") + if "system_services" in host_inbound_traffic: + for system_service in host_inbound_traffic["system_services"]: + system_service_node = build_child_xml_node( + host_inbound_traffic_node, + "system-services", + ) + build_child_xml_node( + system_service_node, + "name", + system_service["name"], + ) + if "except" in system_service: + build_child_xml_node(system_service_node, "except") + + # add zone-pair policies + if "functional_zone_management" in want.keys(): + functional_zone_management = want.get("functional_zone_management") + functional_zone_node = build_child_xml_node( + zones_node, + "functional-zone", + ) + functional_zone_management_node = build_child_xml_node( + functional_zone_node, + "management", + ) + + if "description" in functional_zone_management: + build_child_xml_node( + functional_zone_management_node, + "description", + functional_zone_management["description"], + ) + if "host_inbound_traffic" in functional_zone_management: + build_host_inbound_traffic( + functional_zone_management_node, + functional_zone_management["host_inbound_traffic"], + ) + if "interfaces" in functional_zone_management: + for interface in functional_zone_management["interfaces"]: + interface_node = build_child_xml_node( + functional_zone_management_node, + "interfaces", + ) + build_child_xml_node(interface_node, "name", interface) + if "screen" in functional_zone_management: + build_child_xml_node( + functional_zone_management_node, + "screen", + functional_zone_management["screen"], + ) + + # add global policies + if "zones" in want.keys(): + security_zones = want.get("zones") + + for security_zone in security_zones: + security_zone_node = build_child_xml_node( + zones_node, + "security-zone", + ) + if "name" in security_zone: + build_child_xml_node( + security_zone_node, + "name", + security_zone["name"], + ) + if "address_book" in security_zone: + address_book_node = build_child_xml_node( + security_zone_node, + "address-book", + ) + + if "addresses" in security_zone["address_book"]: + for address in security_zone["address_book"]["addresses"]: + address_node = build_child_xml_node( + address_book_node, + "address", + ) + + build_child_xml_node( + address_node, + "name", + address["name"], + ) + if "ip_prefix" in address: + build_child_xml_node( + address_node, + "ip-prefix", + address["ip_prefix"], + ) + elif "dns_name" in address: + dns_node = build_child_xml_node( + address_node, + "dns-name", + ) + build_child_xml_node( + dns_node, + "name", + address["dns_name"]["name"], + ) + if "ipv4_only" in address["dns_name"]: + build_child_xml_node(dns_node, "ipv4-only") + if "ipv6_only" in address["dns_name"]: + build_child_xml_node(dns_node, "ipv6-only") + elif "range_address" in address: + range_address_node = build_child_xml_node( + address_node, + "range-address", + ) + build_child_xml_node( + range_address_node, + "name", + address["range_address"]["from"], + ) + to_node = build_child_xml_node( + range_address_node, + "to", + ) + build_child_xml_node( + to_node, + "range-high", + address["range_address"]["to"], + ) + elif "wildcard_address" in address: + wildcard_node = build_child_xml_node( + address_node, + "wildcard-address", + ) + build_child_xml_node( + wildcard_node, + "name", + address["wildcard_address"], + ) + if "description" in address: + build_child_xml_node( + address_node, + "description", + address["description"], + ) + if "address_sets" in security_zone["address_book"]: + for address_set in security_zone["address_book"]["address_sets"]: + address_set_node = build_child_xml_node( + address_book_node, + "address-set", + ) + + build_child_xml_node( + address_set_node, + "name", + address_set["name"], + ) + if "addresses" in address_set: + for address in address_set["addresses"]: + addr_node = build_child_xml_node( + address_set_node, + "address", + ) + build_child_xml_node( + addr_node, + "name", + address, + ) + if "address_sets" in address_set: + for address in address_set["address_sets"]: + addr_node = build_child_xml_node( + address_set_node, + "address-set", + ) + build_child_xml_node( + addr_node, + "name", + address, + ) + if "description" in address_set: + build_child_xml_node( + address_set_node, + "description", + address_set["description"], + ) + + if "advance_policy_based_routing_profile" in security_zone: + routing_profile_node = build_child_xml_node( + security_zone_node, + "advance-policy-based-routing-profile", + ) + build_child_xml_node( + routing_profile_node, + "profile", + security_zone["advance_policy_based_routing_profile"], + ) + if "advanced_connection_tracking" in security_zone: + act_node = build_child_xml_node( + security_zone_node, + "advanced-connection-tracking", + ) + if "mode" in security_zone["advanced_connection_tracking"]: + build_child_xml_node( + act_node, + "mode", + security_zone["advanced_connection_tracking"]["mode"], + ) + if "timeout" in security_zone["advanced_connection_tracking"]: + build_child_xml_node( + act_node, + "timeout", + security_zone["advanced_connection_tracking"]["timeout"], + ) + if ( + "track_all_policies_to_this_zone" + in security_zone["advanced_connection_tracking"] + ): + build_child_xml_node( + act_node, + "track-all-policies-to-this-zone", + ) + if "application_tracking" in security_zone: + build_child_xml_node( + security_zone_node, + "application-tracking", + ) + if "description" in security_zone: + build_child_xml_node( + security_zone_node, + "description", + security_zone["description"], + ) + if "enable_reverse_reroute" in security_zone: + build_child_xml_node( + security_zone_node, + "enable-reverse-reroute", + ) + if "host_inbound_traffic" in security_zone: + build_host_inbound_traffic( + security_zone_node, + security_zone["host_inbound_traffic"], + ) + if "interfaces" in security_zone: + for interface in security_zone["interfaces"]: + interface_node = build_child_xml_node( + security_zone_node, + "interfaces", + ) + build_child_xml_node(interface_node, "name", interface) + if "screen" in security_zone: + build_child_xml_node( + security_zone_node, + "screen", + security_zone["screen"], + ) + if "source_identity_log" in security_zone: + build_child_xml_node( + security_zone_node, + "source-identity-log", + ) + if "tcp_rst" in security_zone: + build_child_xml_node(security_zone_node, "tcp-rst") + if "unidirectional_session_refreshing" in security_zone: + build_child_xml_node( + security_zone_node, + "unidirectional-session-refreshing", + ) + + if zones_node is not None: + zones_xml.append(zones_node) + return zones_xml + + 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 + """ + security_zones_xml = [] + security_zones_root = None + delete = {"delete": "delete"} + if have is not None: + security_zones_root = build_child_xml_node( + self.root, + "zones", + None, + delete, + ) + + if security_zones_root is not None: + security_zones_xml.append(security_zones_root) + return security_zones_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/snmp_server.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/snmp_server.py new file mode 100644 index 000000000..754b890d1 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/snmp_server/snmp_server.py @@ -0,0 +1,897 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos_snmp_server 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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, + to_list, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Snmp_server(ConfigBase): + """ + The junos_snmp_server class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["snmp_server"] + + def __init__(self, module): + super(Snmp_server, self).__init__(module) + + def get_snmp_server_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, + ) + snmp_server_facts = facts["ansible_network_resources"].get( + "snmp_server", + ) + if not snmp_server_facts: + return {} + return snmp_server_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + + warnings = list() + + if self.state in self.ACTION_STATES or self.state == "purged": + existing_snmp_server_facts = self.get_snmp_server_facts() + else: + existing_snmp_server_facts = {} + if state == "gathered": + existing_snmp_server_facts = self.get_snmp_server_facts() + result["gathered"] = existing_snmp_server_facts + 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_snmp_server_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_snmp_server_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + + else: + diff = None + config_xmls = self.set_config(existing_snmp_server_facts) + with locked_config(self._module): + for config_xml in config_xmls: + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_snmp_server_facts = self.get_snmp_server_facts() + + result["before"] = existing_snmp_server_facts + if result["changed"]: + result["after"] = changed_snmp_server_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_snmp_server_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_snmp_server_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 + """ + self.root = build_root_xml_node("configuration") + self.snmp = build_child_xml_node(self.root, "snmp") + cmd_lst = [] + state = self._module.params["state"] + if state in ("merged", "replaced", "rendered", "overridden") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "deleted": + self._state_deleted(want, have) + elif state in ("merged", "rendered"): + self._state_merged(want, have) + elif state == "replaced": + self._state_replaced(want, have) + elif state == "overridden": + self._state_replaced(want, have) + + if self.root is not None: + for xml in self.root.getchildren(): + xml = tostring(xml) + cmd_lst.append(xml) + return cmd_lst + + 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 + """ + self._state_deleted(want, have) + self.snmp = build_child_xml_node(self.root, "snmp") + self._state_merged(want, have) + + 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 + """ + want = remove_empties(want) + snmp_node = self.snmp + # add arp node + if "arp" in want.keys(): + arp = want.get("arp") + if arp.get("host_name_resolution"): + arp_node = build_child_xml_node(snmp_node, "arp") + build_child_xml_node(arp_node, "host-name-resolution") + elif arp.get("set"): + build_child_xml_node(snmp_node, "arp") + + # add node client list + if "client_lists" in want.keys(): + client_lists = want.get("client_lists") + for client in client_lists: + client_node = build_child_xml_node(snmp_node, "client-list") + # add name node + build_child_xml_node(client_node, "name", client.get("name")) + if "addresses" in client.keys(): + addresses = client.get("addresses") + for address in addresses: + add_lst_node = build_child_xml_node( + client_node, + "client-address-list", + ) + build_child_xml_node( + add_lst_node, + "name", + address["address"], + ) + if address.get("restrict"): + build_child_xml_node(add_lst_node, "restrict") + + # add routing_instance_access + if "routing_instance_access" in want.keys(): + ria = want.get("routing_instance_access") + + if "access_lists" not in ria.keys() and ria.get("set"): + build_child_xml_node(snmp_node, "routing-instance-access") + elif "access_lists" in ria.keys(): + ria_node = build_child_xml_node( + snmp_node, + "routing-instance-access", + ) + access_lists = ria.get("access_lists") + for item in access_lists: + al_node = build_child_xml_node(ria_node, "access-list") + build_child_xml_node(al_node, "name", item) + # add communities node + if "communities" in want.keys(): + communities = want.get("communities") + for community in communities: + comm_node = build_child_xml_node(snmp_node, "community") + build_child_xml_node(comm_node, "name", community["name"]) + if "authorization" in community.keys(): + build_child_xml_node( + comm_node, + "authorization", + community["authorization"], + ) + if "clients" in community.keys(): + clients = community.get("clients") + for client in clients: + client_node = build_child_xml_node( + comm_node, + "clients", + ) + build_child_xml_node( + client_node, + "name", + client["address"], + ) + if client.get("restrict"): + build_child_xml_node(client_node, "restrict") + if "client_list_name" in community.keys(): + build_child_xml_node( + comm_node, + "client-list-name", + community.get("client_list_name"), + ) + if "routing_instances" in community.keys(): + routing_instances = community.get("routing_instances") + for inst in routing_instances: + inst_node = build_child_xml_node( + comm_node, + "routing-instance", + ) + build_child_xml_node(inst_node, "name", inst["name"]) + if "clients" in inst.keys(): + clients = inst.get("clients") + for client in clients: + client_node = build_child_xml_node( + inst_node, + "clients", + ) + build_child_xml_node( + client_node, + "name", + client["address"], + ) + if client.get("restrict"): + build_child_xml_node( + client_node, + "restrict", + ) + if "client_list_name" in inst.keys(): + build_child_xml_node( + inst_node, + "client-list-name", + inst.get("client_list_name"), + ) + if "view" in community.keys(): + build_child_xml_node( + comm_node, + "view", + community.get("view"), + ) + + # add contact node + if "contact" in want.keys(): + build_child_xml_node(snmp_node, "contact", want.get("contact")) + + # add customization node + if "customization" in want.keys(): + custom = want.get("customization") + if custom.get("ether_stats_ifd_only"): + custom_node = build_child_xml_node(snmp_node, "customization") + build_child_xml_node(custom_node, "ether-stats-ifd-only") + + # add description node + if "description" in want.keys(): + build_child_xml_node( + snmp_node, + "description", + want.get("description"), + ) + + # add engine_id + if "engine_id" in want.keys(): + engine = want.get("engine_id") + engine_node = build_child_xml_node(snmp_node, "engine-id") + if "local" in engine.keys(): + build_child_xml_node(engine_node, "local", engine["local"]) + if engine.get("use_default_ip_address"): + build_child_xml_node(engine_node, "use-default-ip-address") + if engine.get("use_mac_address"): + build_child_xml_node(engine_node, "use-mac-address") + + # add filter_duplicates node + if want.get("filter_duplicates"): + build_child_xml_node(snmp_node, "filter-duplicates") + + # add filter_interfaces node + if "filter_interfaces" in want.keys(): + fints = want.get("filter_interfaces") + + if "all_internal_interfaces" not in fints.keys() and "interfaces" not in fints.keys(): + build_child_xml_node(snmp_node, "filter-interfaces") + else: + fints_node = build_child_xml_node( + snmp_node, + "filter-interfaces", + ) + if "all_internal_interfaces" in fints.keys(): + build_child_xml_node(fints_node, "all-internal-interfaces") + if "interfaces" in fints.keys(): + interfaces = fints.get("interfaces") + for item in interfaces: + int_node = build_child_xml_node( + fints_node, + "interfaces", + ) + build_child_xml_node(int_node, "name", item) + + # add health_monitor node + if "health_monitor" in want.keys(): + health = want.get("health_monitor") + + if "falling_threshold" not in health.keys() and "rising_threshold" not in health.keys(): + if "idp" not in health.keys() and "interval" not in health.keys(): + build_child_xml_node(snmp_node, "health-monitor") + else: + health_node = build_child_xml_node(snmp_node, "health-monitor") + if "falling_threshold" in health.keys(): + build_child_xml_node( + health_node, + "falling-threshold", + health.get("falling_threshold"), + ) + if "rising_threshold" in health.keys(): + build_child_xml_node( + health_node, + "rising-threshold", + health.get("rising_threshold"), + ) + if "interval" in health.keys(): + build_child_xml_node( + health_node, + "interval", + health.get("interval"), + ) + if health.get("idp"): + build_child_xml_node(health_node, "idp") + + # add if_count_with_filter_interfaces + if want.get("if_count_with_filter_interfaces"): + build_child_xml_node(snmp_node, "if-count-with-filter-interfaces") + + # add interfaces node + if "interfaces" in want.keys(): + interfaces = want.get("interfaces") + for item in interfaces: + build_child_xml_node(snmp_node, "interface", item) + + # add location + if "location" in want.keys(): + build_child_xml_node(snmp_node, "location", want.get("location")) + + # add logical_system_trap_filter + if want.get("logical_system_trap_filter"): + build_child_xml_node(snmp_node, "logical-system-trap-filter") + + # add name + if "name" in want.keys(): + build_child_xml_node(snmp_node, "system-name", want.get("name")) + + # add nonvolatile + if "nonvolatile" in want.keys(): + nonvolatile = want.get("nonvolatile") + non_node = build_child_xml_node(snmp_node, "nonvolatile") + if "commit_delay" in nonvolatile.keys(): + build_child_xml_node( + non_node, + "commit-delay", + nonvolatile.get("commit_delay"), + ) + + # add rmon + if "rmon" in want.keys(): + rmon = want.get("rmon") + + if "alarms" not in rmon.keys() and "events" not in rmon.keys() and want.get("set"): + build_child_xml_node(snmp_node, "rmon") + else: + rmon_node = build_child_xml_node(snmp_node, "rmon") + if "alarms" in rmon.keys(): + alarms = rmon.get("alarms") + for alarm in alarms: + alarm_node = build_child_xml_node(rmon_node, "alarm") + for key in alarm.keys(): + if key == "id": + build_child_xml_node( + alarm_node, + "name", + alarm.get("id"), + ) + else: + build_child_xml_node( + alarm_node, + key.replace("_", "-"), + alarm.get(key), + ) + if "events" in rmon.keys(): + events = rmon.get("events") + for event in events: + event_node = build_child_xml_node(rmon_node, "event") + for key in event.keys(): + if key == "id": + build_child_xml_node( + event_node, + "name", + event.get("id"), + ) + else: + build_child_xml_node( + event_node, + key.replace("_", "-"), + event.get(key), + ) + # subagent + if "subagent" in want.keys(): + subagent = want.get("subagent") + sub_node = build_child_xml_node(snmp_node, "subagent") + if "tcp" in subagent.keys(): + tcp = subagent.get("tcp") + if tcp.get("set") and "routing_instances_default" not in tcp.keys(): + build_child_xml_node(sub_node, "tcp") + elif tcp.get("routing_instances_default"): + tcp_node = build_child_xml_node(sub_node, "tcp") + routing_node = build_child_xml_node( + tcp_node, + "routing-instance", + ) + build_child_xml_node(routing_node, "default") + + # traceoptions + if "traceoptions" in want.keys(): + options = want.get("traceoptions") + trace_node = build_child_xml_node(snmp_node, "traceoptions") + + if "file" in options.keys(): + file = options.get("file") + file_node = build_child_xml_node(trace_node, "file") + if "match" in file.keys(): + build_child_xml_node(file_node, "match", file.get("match")) + if "files" in file.keys(): + build_child_xml_node(file_node, "files", file.get("files")) + if "no_world_readable" in file.keys(): + build_child_xml_node(file_node, "no-world-readable") + if "world_readable" in file.keys(): + build_child_xml_node(file_node, "world-readable") + if "size" in file.keys(): + build_child_xml_node(file_node, "size", file.get("size")) + + if "flag" in options.keys(): + flags = options.get("flag") + for key in flags.keys(): + flag_node = build_child_xml_node(trace_node, "flag") + build_child_xml_node( + flag_node, + "name", + key.replace("_", "-"), + ) + + if "memory_trace" in options.keys(): + mem = options.get("memory_trace") + + if mem.get("set") and "size" not in mem.keys(): + build_child_xml_node(trace_node, "memory-trace") + elif "size" in mem.keys(): + mem_node = build_child_xml_node(trace_node, "memory-trace") + build_child_xml_node(mem_node, "size", mem.get("size")) + + if "trap_groups" in want.keys(): + groups = want.get("trap_groups") + + for trap in groups: + trap_node = build_child_xml_node(snmp_node, "trap-group") + for key in trap.keys(): + if key == "name": + build_child_xml_node( + trap_node, + "name", + trap["name"], + ) + if key == "destination_port": + build_child_xml_node( + trap_node, + "destination-port", + trap["destination_port"], + ) + if key == "categories": + cat_node = build_child_xml_node( + trap_node, + "categories", + ) + categories = trap.get("categories") + for key in categories: + if key == "otn_alarms": + alarms = categories.get("otn_alarms") + alarm_node = build_child_xml_node( + cat_node, + "otn-alarms", + ) + for key in alarms: + build_child_xml_node( + alarm_node, + key.replace("_", "-"), + ) + else: + build_child_xml_node( + cat_node, + key.replace("_", "-"), + ) + if key == "routing_instance": + build_child_xml_node( + trap_node, + "routing-instance", + trap.get(key), + ) + if key == "version": + build_child_xml_node( + trap_node, + "version", + trap.get(key), + ) + if key == "targets": + targets = trap.get("targets") + for target in targets: + tar_node = build_child_xml_node( + trap_node, + "targets", + ) + build_child_xml_node(tar_node, "name", target) + + # trap_options + if "trap_options" in want.keys(): + options = want.get("trap_options") + + if options.keys() == {"set"}: + build_child_xml_node(snmp_node, "trap-options") + else: + trap_node = build_child_xml_node(snmp_node, "trap-options") + if "agent_address" in options.keys(): + agent = options.get("agent_address") + if agent.get("outgoing_interface"): + build_child_xml_node( + trap_node, + "agent-address", + "outgoing-interface", + ) + if options.get("context_oid"): + build_child_xml_node(trap_node, "context-id") + # TODO logical_system + if "routing_instance" in options.keys(): + inst = options.get("routing_instances") + inst_node = build_child_xml_node( + trap_node, + "routing-instance", + ) + build_child_xml_node(inst_node, "name", inst) + if "source_address" in options.keys(): + address = options.get("source_address") + source_node = build_child_xml_node( + trap_node, + "source-address", + ) + if "address" in address.keys(): + build_child_xml_node( + source_node, + "address", + address.get("address"), + ) + if "lowest_loopback" in address.keys(): + build_child_xml_node( + source_node, + "lowest-loopback", + ) + # snmp_v3 + if "snmp_v3" in want.keys(): + snmpv3 = want.get("snmp_v3") + v3_node = build_child_xml_node(snmp_node, "v3") + + if "notify" in snmpv3.keys(): + notify = snmpv3.get("notify") + for item in notify: + not_node = build_child_xml_node(v3_node, "notify") + for key in item.keys(): + build_child_xml_node(not_node, key, item.get(key)) + if "notify_filter" in snmpv3.keys(): + filters = snmpv3.get("notify_filter") + for filter in filters: + fil_node = build_child_xml_node(v3_node, "notify-filter") + if "name" in filter.keys(): + build_child_xml_node( + fil_node, + "name", + filter.get("name"), + ) + if "oids" in filter.keys(): + oids = filter.get("oids") + for oid in oids: + oids_node = build_child_xml_node(fil_node, "oid") + if "oid" in oid.keys(): + build_child_xml_node( + oids_node, + "name", + oid["oid"], + ) + if "exclude" in oid.keys(): + build_child_xml_node(oids_node, "exclude") + if "include" in oid.keys(): + build_child_xml_node(oids_node, "include") + if "snmp_community" in snmpv3.keys(): + snmp_community = snmpv3.get("snmp_community") + for snmp in snmp_community: + snmp_node = build_child_xml_node(v3_node, "snmp-community") + for key in snmp.keys(): + if key == "community_index": + build_child_xml_node( + snmp_node, + "name", + snmp.get(key), + ) + else: + build_child_xml_node( + snmp_node, + key.replace("_", "-"), + snmp.get(key), + ) + + if "target_addresses" in snmpv3.keys(): + addresses = snmpv3.get("target_addresses") + for address in addresses: + tar_node = build_child_xml_node(v3_node, "target-address") + for key in address: + build_child_xml_node( + tar_node, + key.replace("_", "-"), + address.get(key), + ) + + # target_parameters + if "target_parameters" in snmpv3.keys(): + parameters = snmpv3.get("target_parameters") + + for param in parameters: + param_node = build_child_xml_node( + v3_node, + "target-parameters", + ) + + if "name" in param: + build_child_xml_node( + param_node, + "name", + param.get("name"), + ) + if "notify_filter" in param: + build_child_xml_node( + param_node, + "notify-filter", + param.get("notify_filter"), + ) + if "parameters" in param: + subnode = build_child_xml_node( + param_node, + "parameters", + ) + parameter = param.get("parameters") + for key in parameter.keys(): + build_child_xml_node( + subnode, + key.replace("_", "-"), + parameter.get(key), + ) + # usm + if "usm" in snmpv3.keys(): + usm = snmpv3.get("usm") + + usm_node = build_child_xml_node(v3_node, "usm") + if "local_engine" in usm.keys(): + local = usm.get("local_engine") + + local_node = build_child_xml_node(usm_node, "local-engine") + if "users" in local.keys(): + users = local.get("users") + for user in users: + user_node = build_child_xml_node( + local_node, + "user", + ) + for key in user.keys(): + if key == "name": + build_child_xml_node( + user_node, + "name", + user["name"], + ) + elif key == "authentication_none": + build_child_xml_node( + user_node, + "authentication-none", + ) + elif key == "privacy_none": + build_child_xml_node( + user_node, + "privacy-none", + ) + else: + sub_dict = user.get(key) + sub_node = build_child_xml_node( + user_node, + key.replace("_", "-"), + ) + + if "authentication" in key: + build_child_xml_node( + sub_node, + "authentication-key", + sub_dict["key"], + ) + build_child_xml_node( + sub_node, + "authentication-password", + sub_dict["password"], + ) + else: + build_child_xml_node( + sub_node, + "privacy-key", + sub_dict["key"], + ) + build_child_xml_node( + sub_node, + "privacy-password", + sub_dict["password"], + ) + if "remote_engine" in usm.keys(): + remotes = usm.get("remote_engine") + + for remote in remotes: + remote_node = build_child_xml_node( + usm_node, + "remote-engine", + ) + if "id" in remote: + build_child_xml_node( + remote_node, + "name", + remote.get("id"), + ) + if "users" in remote.keys(): + users = remote.get("users") + for user in users: + user_node = build_child_xml_node( + remote_node, + "user", + ) + for key in user.keys(): + if key == "name": + build_child_xml_node( + user_node, + "name", + user["name"], + ) + elif key == "authentication_none": + build_child_xml_node( + user_node, + "authentication-none", + ) + elif key == "privacy_none": + build_child_xml_node( + user_node, + "privacy-none", + ) + else: + sub_dict = user.get(key) + sub_node = build_child_xml_node( + user_node, + key.replace("_", "-"), + ) + + if "authentication" in key: + build_child_xml_node( + sub_node, + "authentication-key", + sub_dict["key"], + ) + build_child_xml_node( + sub_node, + "authentication-password", + sub_dict["password"], + ) + else: + build_child_xml_node( + sub_node, + "privacy-key", + sub_dict["key"], + ) + build_child_xml_node( + sub_node, + "privacy-password", + sub_dict["password"], + ) + if "views" in want.keys(): + views = want.get("views") + for view in views: + view_node = build_child_xml_node(snmp_node, "view") + + if "name" in view.keys(): + build_child_xml_node( + view_node, + "name", + view.get("name"), + ) + if "oids" in view.keys(): + oids = view.get("oids") + for oid in oids: + oids_node = build_child_xml_node(view_node, "oid") + if "oid" in oid.keys(): + build_child_xml_node( + oids_node, + "name", + oid["oid"], + ) + if "exclude" in oid.keys(): + build_child_xml_node(oids_node, "exclude") + if "include" in oid.keys(): + build_child_xml_node(oids_node, "include") + + 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 + """ + delete = {"delete": "delete"} + if have is not None: + build_child_xml_node(self.root, "snmp", None, delete) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/static_routes.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/static_routes.py new file mode 100644 index 000000000..a7ba49c9f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/static_routes/static_routes.py @@ -0,0 +1,324 @@ +# +# -*- 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 junos_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.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Static_routes(ConfigBase): + """ + The junos_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} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_static_routes_facts = self.get_static_routes_facts() + else: + existing_static_routes_facts = [] + if state == "gathered": + existing_static_routes_facts = self.get_static_routes_facts() + result["gathered"] = existing_static_routes_facts + 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, + ) + elif self.state == "rendered": + config_xmls = self.set_config(existing_static_routes_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_static_routes_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_static_routes_facts = self.get_static_routes_facts() + + result["before"] = existing_static_routes_facts + if result["changed"]: + result["after"] = changed_static_routes_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_static_routes_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_static_routes_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params["state"] + if state in ("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, + ), + ) + root = build_root_xml_node("configuration") + routing_options = build_child_xml_node(root, "routing-options") + routing_instances = build_child_xml_node(root, "routing-instances") + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + if xml["root_type"] == "routing-options": + routing_options.append(xml["static_route_xml"]) + elif xml["root_type"] == "routing-instances": + routing_instances.append(xml["static_route_xml"]) + + return [tostring(xml) for xml in root.getchildren()] + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + static_route_xml = [] + static_route_xml.extend(self._state_deleted(want, have)) + static_route_xml.extend(self._state_merged(want, have)) + return static_route_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + static_route_xml = [] + static_route_xml.extend(self._state_deleted(have, have)) + static_route_xml.extend(self._state_merged(want, have)) + return static_route_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + if not want: + want = have + static_route_xml = self._state_merged( + want, + have, + delete={"delete": "delete"}, + ) + return static_route_xml + + def _state_merged(self, want, have, delete=None): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + static_route_xml = [] + root_type = "" + vrf_name = None + for config in want: + if config.get("vrf"): + vrf_name = config["vrf"] + if vrf_name: + root_type = "routing-instances" + instance = build_root_xml_node("instance") + build_child_xml_node(instance, "name", vrf_name) + routing_options = build_child_xml_node( + instance, + "routing-options", + ) + else: + root_type = "routing-options" + + for afi in config["address_families"]: + protocol = afi["afi"] + if protocol == "ipv6": + if vrf_name: + rib_route_root = build_child_xml_node( + routing_options, + "rib", + ) + build_child_xml_node( + rib_route_root, + "name", + vrf_name + ".inet6.0", + ) + else: + rib_route_root = build_root_xml_node("rib") + build_child_xml_node(rib_route_root, "name", "inet6.0") + static_route_root = build_child_xml_node( + rib_route_root, + "static", + ) + elif protocol == "ipv4": + if vrf_name: + static_route_root = build_child_xml_node( + routing_options, + "static", + ) + else: + static_route_root = build_root_xml_node("static") + + if afi.get("routes"): + for route in afi["routes"]: + route_node = build_child_xml_node( + static_route_root, + "route", + ) + if delete: + route_node.attrib.update(delete) + if route.get("dest"): + build_child_xml_node( + route_node, + "name", + route["dest"], + ) + if not delete: + if route.get("metric"): + build_child_xml_node( + route_node, + "metric", + route["metric"], + ) + if route.get("next_hop"): + for hop in route["next_hop"]: + build_child_xml_node( + route_node, + "next-hop", + hop["forward_router_address"], + ) + elif delete: + if vrf_name: + instance.attrib.update(delete) + static_route_root.attrib.update(delete) + + if vrf_name: + static_route_xml.append( + {"root_type": root_type, "static_route_xml": instance}, + ) + else: + if protocol == "ipv6": + static_route_xml.append( + { + "root_type": root_type, + "static_route_xml": rib_route_root, + }, + ) + else: + static_route_xml.append( + { + "root_type": root_type, + "static_route_xml": static_route_root, + }, + ) + return static_route_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/vlans.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/vlans.py new file mode 100644 index 000000000..2a8323319 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/config/vlans/vlans.py @@ -0,0 +1,254 @@ +# Copyright (C) 2019 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +""" +The junos_vlans class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + build_child_xml_node, + build_root_xml_node, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + tostring, +) + + +class Vlans(ConfigBase): + """ + The junos_vlans class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["vlans"] + + def __init__(self, module): + super(Vlans, self).__init__(module) + + def get_vlans_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + vlans_facts = facts["ansible_network_resources"].get("vlans") + if not vlans_facts: + return [] + return vlans_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + state = self._module.params["state"] + warnings = list() + + if self.state in self.ACTION_STATES: + existing_vlans_facts = self.get_vlans_facts() + else: + existing_vlans_facts = [] + if state == "gathered": + existing_vlans_facts = self.get_vlans_facts() + result["gathered"] = existing_vlans_facts + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_vlans_facts(data=running_config) + elif self.state == "rendered": + config_xmls = self.set_config(existing_vlans_facts) + if config_xmls: + result["rendered"] = config_xmls[0] + else: + result["rendered"] = "" + + else: + config_xmls = self.set_config(existing_vlans_facts) + with locked_config(self._module): + for config_xml in to_list(config_xmls): + diff = load_config(self._module, config_xml, []) + + commit = not self._module.check_mode + if diff: + if commit: + commit_configuration(self._module) + else: + discard_changes(self._module) + result["changed"] = True + + if self._module._diff: + result["diff"] = {"prepared": diff} + + result["commands"] = config_xmls + + changed_vlans_facts = self.get_vlans_facts() + + result["before"] = existing_vlans_facts + if result["changed"]: + result["after"] = changed_vlans_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_vlans_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_vlans_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + root = build_root_xml_node("vlans") + state = self._module.params["state"] + if state in ("merged", "replaced", "overridden", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state, + ), + ) + if state == "overridden": + config_xmls = self._state_overridden(want, have) + elif state == "deleted": + config_xmls = self._state_deleted(want, have) + elif state in ("merged", "rendered"): + config_xmls = self._state_merged(want, have) + elif state == "replaced": + config_xmls = self._state_replaced(want, have) + + for xml in config_xmls: + root.append(xml) + + return tostring(root) + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(want, have)) + intf_xml.extend(self._state_merged(want, have)) + return intf_xml + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the xml necessary to migrate the current configuration + to the desired configuration + """ + intf_xml = [] + intf_xml.extend(self._state_deleted(have, have)) + intf_xml.extend(self._state_merged(want, have)) + return intf_xml + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the xml necessary to merge the provided into + the current configuration + """ + intf_xml = [] + + for config in want: + vlan_name = str(config["name"]) + vlan_id = str(config["vlan_id"]) + vlan_description = config.get("description") + vlan_root = build_root_xml_node("vlan") + build_child_xml_node(vlan_root, "name", vlan_name) + build_child_xml_node(vlan_root, "vlan-id", vlan_id) + if vlan_description: + build_child_xml_node( + vlan_root, + "description", + vlan_description, + ) + if config.get("l3_interface"): + build_child_xml_node( + vlan_root, + "l3-interface", + config.get("l3_interface"), + ) + + intf_xml.append(vlan_root) + return intf_xml + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the xml necessary to remove the current configuration + of the provided objects + """ + intf_xml = [] + + if not want: + want = have + + for config in want: + vlan_name = config["name"] + vlan_root = build_root_xml_node("vlan") + vlan_root.attrib.update({"delete": "delete"}) + build_child_xml_node(vlan_root, "name", vlan_name) + intf_xml.append(vlan_root) + return intf_xml diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/acl_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..ac91f53ba --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acl_interfaces/acl_interfaces.py @@ -0,0 +1,160 @@ +# +# -*- 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 junos 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.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Acl_interfacesFacts(object): + """The junos 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 HAS_LXML: + self._module.fail_json(msg=missing_required_lib("lxml")) + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = connection.get_configuration(filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/interfaces/interface") + + objs = [] + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + obj = self.render_config(self.generated_spec, xml) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["junos_acl_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["junos_acl_interfaces"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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["access_groups"] = [] + + if "unit" in conf["interface"] and "family" in conf["interface"]["unit"]: + for family in conf["interface"]["unit"]["family"].keys(): + access_groups = { + "afi": "ipv6" if family == "inet6" else "ipv4", + "acls": [], + } + if conf["interface"]["unit"]["family"][family] is not None and conf["interface"][ + "unit" + ]["family"][family].get( + "filter", + ): + for direction in ["input-list", "output-list"]: + rendered_direction = "in" if direction == "input-list" else "out" + if conf["interface"]["unit"]["family"][family]["filter"].get(direction): + acl_name = conf["interface"]["unit"]["family"][family]["filter"][ + direction + ] + if not isinstance(acl_name, list): + acl_name = [acl_name] + for filter_name in acl_name: + access_groups["acls"].append( + { + "name": filter_name, + "direction": rendered_direction, + }, + ) + if access_groups["acls"]: + config["name"] = conf["interface"]["name"] + config["access_groups"].append(access_groups) + + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/acls.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/acls.py new file mode 100644 index 000000000..a48217d64 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/acls/acls.py @@ -0,0 +1,246 @@ +# +# -*- 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 junos acls fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.acls.acls import ( + AclsArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class AclsFacts(object): + """The junos 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 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 HAS_LXML: + self._module.fail_json(msg=missing_required_lib("lxml")) + + if not data: + config_filter = """ + <configuration> + <firewall/> + </configuration> + """ + data = connection.get_configuration(filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/firewall") + + objs = [] + for resource in resources: + if resource: + xml = self._get_xml_dict(resource) + for family, sub_dict in xml["firewall"]["family"].items(): + sub_dict["family"] = family + obj = self.render_config( + self.generated_spec, + dict(firewall=sub_dict), + ) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["junos_acls"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["junos_acls"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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["afi"] = "ipv6" if conf["firewall"].pop("family") == "inet6" else "ipv4" + acls = conf.get("firewall").get("filter") + if not isinstance(acls, list): + acls = [acls] + config["acls"] = [] + for acl in acls: + acl_dict = {"name": acl["name"], "aces": []} + if acl.get("term"): + terms = acl["term"] + if not isinstance(terms, list): + terms = [terms] + for term in terms: + ace = {"name": term.get("name")} + if term.get("from"): + if term["from"].get("source-address"): + ace["source"] = ace.get("source", {}) + source_address = term["from"].get("source-address") + if not isinstance(source_address, list): + ace["source"]["address"] = source_address["name"] + else: + ace["source"]["address"] = [] + for address in source_address: + ace["source"]["address"].append( + address["name"], + ) + if term["from"].get("source-prefix-list"): + ace["source"] = ace.get("source", {}) + ace["source"]["prefix_list"] = term["from"]["source-prefix-list"] + if term["from"].get("source-port"): + ace["source"] = ace.get("source", {}) + ace["source"]["port_protocol"] = dict( + eq=term["from"]["source-port"], + ) + if term["from"].get("destination-address"): + ace["destination"] = ace.get("destination", {}) + destination_address = term["from"].get( + "destination-address", + ) + if not isinstance(destination_address, list): + ace["destination"]["address"] = destination_address["name"] + else: + ace["destination"]["address"] = [] + for address in destination_address: + ace["destination"]["address"].append( + address["name"], + ) + if term["from"].get("destination-prefix-list"): + ace["destination"] = ace.get("destination", {}) + ace["destination"]["prefix_list"] = term["from"][ + "destination-prefix-list" + ] + if term["from"].get("destination-port"): + ace["destination"] = ace.get("destination", {}) + ace["destination"]["port_protocol"] = dict( + eq=term["from"]["destination-port"], + ) + if term["from"].get("protocol"): + ace["protocol"] = term["from"]["protocol"] + if term["from"].get("icmp-type"): + ace["protocol_options"] = dict(icmp={}) + icmp_type = term["from"]["icmp-type"] + if icmp_type == "echo-reply": + ace["protocol_options"]["icmp"]["echo_reply"] = True + if icmp_type == "echo-request": + ace["protocol_options"]["icmp"]["echo"] = True + if icmp_type == "redirect": + ace["protocol_options"]["icmp"]["redirect"] = True + if icmp_type == "router-advertisement": + ace["protocol_options"]["icmp"]["router_advertisement"] = True + if icmp_type == "router-solicit": + ace["protocol_options"]["icmp"]["router_solicitation"] = True + if icmp_type == "time-exceeded": + ace["protocol_options"]["icmp"]["time_exceeded"] = True + if term["from"].get("icmp-code"): + ace["protocol_options"] = dict(icmp={}) + icmp_code = term["from"]["icmp-code"] + if icmp_code == "destination-host-prohibited": + ace["protocol_options"]["icmp"]["dod_host_prohibited"] = True + if icmp_code == "destination-host-unknown": + ace["protocol_options"]["icmp"]["host_unknown"] = True + if icmp_code == "destination-network-prohibited": + ace["protocol_options"]["icmp"]["dod_net_prohibited"] = True + if icmp_code == "destination-network-unknown": + ace["protocol_options"]["icmp"]["network_unknown"] = True + if icmp_code == "host-unreachable": + ace["protocol_options"]["icmp"]["host_unreachable"] = True + if icmp_code == "host-unreachable-for-tos": + ace["protocol_options"]["icmp"]["host_tos_unreachable"] = True + if icmp_code == "port-unreachable": + ace["protocol_options"]["icmp"]["port_unreachable"] = True + if icmp_code == "protocol-unreachable": + ace["protocol_options"]["icmp"]["protocol_unreachable"] = True + if icmp_code == "redirect-for-host": + ace["protocol_options"]["icmp"]["host_redirect"] = True + if icmp_code == "redirect-for-network": + ace["protocol_options"]["icmp"]["net_redirect"] = True + if icmp_code == "redirect-for-tos-and-host": + ace["protocol_options"]["icmp"]["host_tos_redirect"] = True + if icmp_code == "redirect-for-tos-and-net": + ace["protocol_options"]["icmp"]["net_tos_redirect"] = True + if icmp_code == "source-route-failed": + ace["protocol_options"]["icmp"]["source_route_failed"] = True + if icmp_code == "ttl-eq-zero-during-reassembly": + ace["protocol_options"]["icmp"]["reassembly-timeout"] = True + if icmp_code == "ttl-eq-zero-during-transit": + ace["protocol_options"]["icmp"]["ttl_exceeded"] = True + if term.get("then"): + if "accept" in term["then"]: + ace["grant"] = "permit" + if "discard" in term["then"]: + ace["grant"] = "deny" + acl_dict["aces"].append(ace) + config["acls"].append(acl_dict) + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/bgp_address_family.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..0a403fa79 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_address_family/bgp_address_family.py @@ -0,0 +1,741 @@ +# +# -*- 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 junos bgp_address_family fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Bgp_address_familyFacts(object): + """The junos bgp_address_family fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_address_familyArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for bgp_address_family + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <bgp> + </bgp> + </protocols> + <routing-options> + <autonomous-system/> + </routing-options> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/protocols/bgp") + autonomous_system_path = data.xpath( + "configuration/routing-options/autonomous-system", + ) + if autonomous_system_path: + self.autonomous_system = self._get_xml_dict( + autonomous_system_path.pop(), + ) + else: + self.autonomous_system = "" + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["bgp_address_family"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["bgp_address_family"] = utils.remove_empties( + params["config"], + ) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + bgp_af_config = {} + + # Parse facts for BGP address-family global node + conf = conf.get("bgp") + bgp_af_config["address_family"] = self.parse_af_facts(conf) + + # Parse BGP group address-family config node + if "group" in conf.keys(): + groups = conf.get("group") + groups_af_lst = [] + group_af_dict = {} + if isinstance(groups, list): + for group in groups: + af_facts = self.parse_af_facts(group) + if af_facts is not None: + group_af_dict["address_family"] = af_facts + # Parse neighbors af node + if "neighbor" in group.keys(): + neighbors_af_lst = [] + nh_af_dict = {} + neighbors = group.get("neighbor") + if isinstance(neighbors, list): + for neighbor in neighbors: + # nh_af_dict["name"] = neighbor.get("neighbor_address") + naf_facts = self.parse_af_facts(neighbor) + if naf_facts is not None: + nh_af_dict["address_family"] = naf_facts + nh_af_dict["neighbor_address"] = neighbor.get("name") + if nh_af_dict: + neighbors_af_lst.append(nh_af_dict) + nh_af_dict = {} + else: + naf_facts = self.parse_af_facts(neighbors) + if naf_facts is not None: + nh_af_dict["address_family"] = naf_facts + nh_af_dict["neighbor_address"] = neighbors.get( + "name", + ) + if nh_af_dict: + neighbors_af_lst.append(nh_af_dict) + if neighbors_af_lst: + group_af_dict["neighbors"] = neighbors_af_lst + if group_af_dict: + group_af_dict["name"] = group.get("name") + groups_af_lst.append(group_af_dict) + group_af_dict = {} + else: + af_facts = self.parse_af_facts(groups) + if af_facts is not None: + group_af_dict["address_family"] = af_facts + # Parse neighbors af node + if "neighbor" in groups.keys(): + neighbors_af_lst = [] + nh_af_dict = {} + neighbors = groups.get("neighbor") + if isinstance(neighbors, list): + for neighbor in neighbors: + # nh_af_dict["name"] = neighbor.get("name") + naf_facts = self.parse_af_facts(neighbor) + if naf_facts is not None: + nh_af_dict["address_family"] = naf_facts + nh_af_dict["neighbor_address"] = neighbor.get( + "name", + ) + if nh_af_dict: + neighbors_af_lst.append(nh_af_dict) + nh_af_dict = {} + else: + naf_facts = self.parse_af_facts(neighbors) + if naf_facts is not None: + nh_af_dict["address_family"] = naf_facts + nh_af_dict["neighbor_address"] = neighbors.get( + "name", + ) + if nh_af_dict: + neighbors_af_lst.append(nh_af_dict) + if neighbors_af_lst: + group_af_dict["neighbors"] = neighbors_af_lst + if group_af_dict: + group_af_dict["name"] = groups.get("name") + groups_af_lst.append(group_af_dict) + if groups_af_lst is not None: + bgp_af_config["groups"] = groups_af_lst + + return utils.remove_empties(bgp_af_config) + + def parse_af_facts(self, conf): + """ + + :return: + """ + nlri_params = [ + "evpn", + "inet", + "inet-mdt", + "inet-mvpn", + "inet-vpn", + "inet6", + "inet6-mvpn", + "inet6-vpn", + "iso-vpn", + "l2vpn", + "traffic-engineering", + ] + # TBD wrap route-target' + nlri_types = [ + "any", + "flow", + "multicast", + "labeled-unicast", + "segment-routing-te", + "unicast", + "signaling", + ] + bgp = conf.get("family") + address_family = [] + # Parse NLRI Parameters + for param in nlri_params: + af_dict = {} + if bgp and param in bgp.keys(): + af_type = [] + nlri_param = bgp.get(param) + for nlri in nlri_types: + af_dict["afi"] = param + if nlri in nlri_param.keys(): + nlri_dict = self.parse_nlri(nlri_param, nlri) + if nlri_dict: + af_type.append(nlri_dict) + if af_type: + af_dict["af_type"] = af_type + if af_dict: + address_family.append(af_dict) + + return address_family + + def parse_nlri(self, cfg, nlri_t): + """ + + :param cfg: + :return: + """ + nlri_dict = {} + if cfg and nlri_t in cfg.keys(): + nlri_dict["type"] = nlri_t + nlri = cfg.get(nlri_t) + + if not nlri: + nlri_dict["set"] = True + return nlri_dict + # Parse accepted-prefix-limit + if "accepted-prefix-limit" in nlri.keys(): + apl_dict = self.parse_accepted_prefix_limit(nlri) + # populate accepted_prefix_limit + if apl_dict: + nlri_dict["accepted_prefix_limit"] = apl_dict + + # Parse add-path + if "add-path" in nlri.keys(): + ap_dict = self.parse_add_path(nlri) + # populate accepted_prefix_limit + if ap_dict: + nlri_dict["add_path"] = ap_dict + + # Parse aggregate-label + if "aggregate-label" in nlri.keys(): + al_dict = self.parse_aggregate_label(nlri) + # populate aggregate-label + if apl_dict: + nlri_dict["aggregate_label"] = al_dict + + # Parse aigp + if "aigp" in nlri.keys(): + aigp_dict = self.parse_aigp(nlri) + # populate aigp + if aigp_dict: + nlri_dict["aigp"] = aigp_dict + + # Parse and populate damping + if "damping" in nlri.keys(): + nlri_dict["damping"] = True + + # Parse defer-initial-multipath-build + if "defer-initial-multipath-build" in nlri.keys(): + dimb_dict = self.parse_defer_initial_multipath_build(nlri) + # populate defer_initial_multipath_build + if dimb_dict: + nlri_dict["defer_initial_multipath_build"] = dimb_dict + + # Parse delay-route-advertisements + if "delay-route-advertisements" in nlri.keys(): + dra_dict = self.parse_delay_route_advertisements(nlri) + # populate delay_route_advertisements + if dra_dict: + nlri_dict["delay_route_advertisements"] = dra_dict + + # Parse entropy-label + if "entropy-label" in nlri.keys(): + el_dict = self.parse_entropy_label(nlri) + # populate entropy-label + if el_dict: + nlri_dict["entropy_label"] = el_dict + + # Parse explicit-null + if "explicit-null" in nlri.keys(): + en_dict = self.parse_explicit_null(nlri) + # populate explicit-null + if en_dict: + nlri_dict["explicit_null"] = en_dict + + # Parse extended-nexthop + if "extended-nexthop" in nlri.keys(): + nlri_dict["extended_nexthop"] = True + + # Parse extended-nexthop-color + if "extended-nexthop-color" in nlri.keys(): + nlri_dict["extended_nexthop_color"] = True + + # Parse forwarding-state-bit + if "graceful-restart" in nlri.keys(): + gr = nlri.get("graceful-restart") + if "forwarding-state-bit" in gr.keys(): + fsb = gr.get("forwarding-state-bit") + nlri_dict["graceful_restart_forwarding_state_bit"] = fsb + + # Parse legacy-redirect-ip-action + if "legacy-redirect-ip-action" in nlri.keys(): + lria_dict = self.parse_legacy_redirect_ip_action(nlri) + # populate legacy_redirect_ip_action + if lria_dict: + nlri_dict["legacy_redirect_ip_action"] = lria_dict + + # Parse local-ipv4-address + if "local-ipv4-address" in nlri.keys(): + nlri_dict["local_ipv4_address"] = nlri.get( + "local-ipv4-address", + ) + + # Parse loops + if "loops" in nlri.keys(): + loops = nlri.get("loops") + nlri_dict["loops"] = loops.get("loops") + + # Parse no-install + if "no-install" in nlri.keys(): + nlri_dict["no_install"] = True + + # Parse no-validate + if "no-validate" in nlri.keys(): + nlri_dict["no_validate"] = nlri.get("no-validate") + + # Parse output-queue-priority + if "output-queue-priority" in nlri.keys(): + oqp = nlri.get("output-queue-priority") + if "expedited" in oqp.keys(): + nlri_dict["output_queue_priority_expedited"] = True + if "priority" in oqp.keys(): + nlri_dict["output_queue_priority_priority"] = oqp.get( + "priority", + ) + + # Parse per-group-label + if "per-group-label" in nlri.keys(): + nlri_dict["per_group_label"] = True + + # Parse per-prefix-label + if "per-prefix-label" in nlri.keys(): + nlri_dict["per_prefix_label"] = True + + # Parse resolve-vpn + if "resolve-vpn" in nlri.keys(): + nlri_dict["resolve_vpn"] = True + + # Parse prefix-limit + if "prefix-limit" in nlri.keys(): + pl_dict = self.parse_accepted_prefix_limit(nlri) + # populate delay_route_advertisements + if pl_dict: + nlri_dict["prefix_limit"] = pl_dict + + # Parse resolve-vpn + if "resolve-vpn" in nlri.keys(): + nlri_dict["resolve_vpn"] = True + + # Parse rib + if "rib" in nlri.keys(): + nlri_dict["rib"] = "inet.3" + + # Parse rib-group + if "rib-group" in nlri.keys(): + nlri_dict["rib_group"] = nlri.get("rib-group") + + # Parse route-refresh-priority + if "route-refresh-priority" in nlri.keys(): + oqp = nlri.get("route-refresh-priority") + if "expedited" in oqp.keys(): + nlri_dict["route_refresh_priority_expedited"] = True + if "priority" in oqp.keys(): + nlri_dict["route_refresh_priority_priority"] = oqp.get( + "priority", + ) + + # Parse secondary-independent-resolution + if "secondary-independent-resolution" in nlri.keys(): + nlri_dict["secondary_independent_resolution"] = True + + # Parse strip-nexthop + if "strip-nexthop" in nlri.keys(): + nlri_dict["strip_nexthop"] = True + + # Parse topology + if "topology" in nlri.keys(): + t_list = self.parse_topology(nlri) + # populate topology + if t_list: + nlri_dict["topology"] = t_list + + # Parse traffic-statistics + if "traffic-statistics" in nlri.keys(): + ts_dict = self.parse_traffic_statistics(nlri) + # populate topology + if ts_dict: + nlri_dict["traffic_statistics"] = ts_dict + + # Parse withdraw-priority + if "withdraw-priority" in nlri.keys(): + oqp = nlri.get("withdraw-priority") + if "expedited" in oqp.keys(): + nlri_dict["withdraw_priority_expedited"] = True + if "priority" in oqp.keys(): + nlri_dict["withdraw_priority_priority"] = oqp.get( + "priority", + ) + return nlri_dict + + def parse_accepted_prefix_limit(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + apl_dict = {} + if "accepted-prefix-limit" in cfg.keys(): + apl = cfg.get("accepted-prefix-limit") + else: + apl = cfg.get("prefix-limit") + if "maximum" in apl.keys(): + apl_dict["maximum"] = apl.get("maximum") + if "teardown" in apl.keys(): + if not apl.get("teardown"): + apl_dict["teardown"] = True + else: + td = apl.get("teardown") + if "idle-timeout" in td.keys(): + if not td.get("idle-timeout"): + apl_dict["idle_timeout"] = True + elif "forever" in td["idle-timeout"].keys(): + apl_dict["forever"] = True + elif "timeout" in td["idle-timeout"].keys(): + apl_dict["idle_timeout_value"] = td["idle-timeout"].get("timeout") + if "limit-threshold" in td.keys(): + apl_dict["limit_threshold"] = td.get("limit-threshold") + return apl_dict + + def parse_add_path(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + ap_dict = {} + ap = cfg.get("add-path") + if "receive" in ap.keys(): + ap_dict["receive"] = True + if "send" in ap.keys(): + send = ap.get("send") + s_dict = {} + if "include-backup-path" in send.keys(): + s_dict["include_backup_path"] = send.get("include-backup-path") + if "path-count" in send.keys(): + s_dict["path_count"] = send.get("path-count") + if "multipath" in send.keys(): + s_dict["multipath"] = True + if "path-selection-mode" in send.keys(): + psm = send.get("path-selection-mode") + psm_dict = {} + if "all-paths" in psm.keys(): + psm_dict["all_paths"] = True + if "equal-cost-paths" in psm.keys(): + psm_dict["equal_cost_paths"] = True + s_dict["path_selection_mode"] = psm_dict + if "prefix-policy" in send.keys(): + s_dict["prefix_policy"] = send.get("prefix-policy") + ap_dict["send"] = s_dict + return ap_dict + + def parse_aggregate_label(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + al_dict = {} + al = cfg.get("aggregate-label") + if not al: + al_dict["set"] = True + else: + al_dict["community"] = al.get("community") + return al_dict + + def parse_aigp(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + aigp_dict = {} + aigp = cfg.get("aigp") + if aigp and "disable" in aigp.keys(): + aigp_dict["disable"] = True + else: + aigp_dict["set"] = True + return aigp_dict + + def parse_defer_initial_multipath_build(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + dimb_dict = {} + dimb = cfg.get("defer-initial-multipath-build") + if not dimb: + dimb_dict["set"] = True + + elif "maximum-delay" in dimb.keys(): + dimb_dict["maximum_delay"] = dimb.get("maximum-delay") + return dimb_dict + + def parse_legacy_redirect_ip_action(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + lria_dict = {} + lria = cfg.get("legacy-redirect-ip-action") + if not lria: + lria_dict["set"] = True + else: + if "send" in lria.keys(): + lria_dict["send"] = True + if "receive" in lria.keys(): + lria_dict["receive"] = True + return lria_dict + + def parse_delay_route_advertisements(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + dra_dict = {} + dra = cfg.get("delay-route-advertisements") + if not dra: + dra_dict["set"] = True + else: + if "maximum-delay" in dra.keys(): + mxd = dra.get("maximum-delay") + if "route-age" in mxd.keys(): + dra_dict["max_delay_route_age"] = mxd.get("route-age") + if "routing-uptime" in mxd.keys(): + dra_dict["max_delay_routing_uptime"] = mxd.get( + "routing-uptime", + ) + if "minimum-delay" in dra.keys(): + mid = dra.get("minimum-delay") + if "inbound-convergence" in mid.keys(): + dra_dict["min_delay_inbound_convergence"] = mid.get( + "inbound-convergence", + ) + if "routing-uptime" in mid.keys(): + dra_dict["min_delay_routing_uptime"] = mid.get( + "routing-uptime", + ) + return dra_dict + + def parse_entropy_label(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + el_dict = {} + el = cfg.get("entropy-label") + if not el: + el_dict["set"] = True + else: + if "import" in el.keys(): + el_dict["import"] = el.get("import") + if "no-next-hop-validation" in el.keys(): + el_dict["no_next_hop_validation"] = True + return el_dict + + def parse_explicit_null(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + en_dict = {} + en = cfg.get("explicit-null") + if not en: + en_dict["set"] = True + elif "connected-only" in en.keys(): + en_dict["connected_only"] = True + return en_dict + + def parse_topology(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + top_dict = {} + topology_list = [] + topologies = cfg.get("topology") + if isinstance(topologies, list): + for topology in topologies: + top_dict["name"] = topology["name"] + communities = topology.get("community") + community_lst = [] + if isinstance(communities, list): + for community in communities: + community_lst.append(community) + else: + community_lst.append(communities) + if community_lst is not None: + top_dict["community"] = community_lst + if top_dict is not None: + topology_list.append(top_dict) + top_dict = {} + else: + top_dict["name"] = topologies["name"] + communities = topologies.get("community") + community_lst = [] + if isinstance(communities, list): + for community in communities: + community_lst.append(community) + else: + community_lst.append(communities) + if community_lst is not None: + top_dict["community"] = community_lst + if top_dict is not None: + topology_list.append(top_dict) + return topology_list + + def parse_traffic_statistics(self, cfg): + """ + + :param self: + :param cfg: + :return: + """ + ts_dict = {} + ts = cfg.get("traffic-statistics") + if not ts: + ts_dict["set"] = True + else: + if "interval" in ts.keys(): + ts_dict["interval"] = ts.get("interval") + if "labeled-path" in ts.keys(): + ts_dict["labeled_path"] = True + if "file" in ts.keys(): + file = ts.get("file") + file_dict = {} + if "files" in file.keys(): + file_dict["files"] = file.get("files") + if "no-world-readable" in file.keys(): + file_dict["no_world_readable"] = True + if "size" in file.keys(): + file_dict["size"] = file.get("size") + if "world-readable" in file.keys(): + file_dict["world_readable"] = True + + return ts_dict diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/bgp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/bgp_global.py new file mode 100644 index 000000000..a8cfa99c7 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/bgp_global/bgp_global.py @@ -0,0 +1,1090 @@ +# +# -*- 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 junos bgp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + generate_dict, + remove_empties, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + _validate_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Bgp_globalFacts(object): + """The junos bgp_global fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = generate_dict(facts_argument_spec) + + def get_device_data(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for bgp_global + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <bgp> + </bgp> + </protocols> + <routing-options> + <autonomous-system/> + </routing-options> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/protocols/bgp") + autonomous_system_path = data.xpath( + "configuration/routing-options/autonomous-system", + ) + if autonomous_system_path: + self.autonomous_system = self._get_xml_dict( + autonomous_system_path.pop(), + ) + else: + self.autonomous_system = "" + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + if not objs: + if self.autonomous_system and self.autonomous_system.get( + "autonomous-system", + ): + objs["as_number"] = self.autonomous_system["autonomous-system"].get("as-number") + if self.autonomous_system["autonomous-system"].get("loops"): + objs["loops"] = self.autonomous_system["autonomous-system"].get("loops") + if "asdot-notation" in self.autonomous_system["autonomous-system"]: + objs["asdot_notation"] = True + facts = {} + if objs: + facts["bgp_global"] = {} + params = _validate_config( + self._module, + self.argument_spec, + {"config": objs}, + redact=True, + ) + facts["bgp_global"] = remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + bgp_global = {} + bgp = conf.get("bgp") + # Set ASN value into facts + if self.autonomous_system and self.autonomous_system.get( + "autonomous-system", + ): + bgp_global["as_number"] = self.autonomous_system["autonomous-system"].get("as-number") + if self.autonomous_system["autonomous-system"].get("loops"): + bgp_global["loops"] = self.autonomous_system["autonomous-system"].get("loops") + if "asdot-notation" in self.autonomous_system["autonomous-system"]: + bgp_global["asdot_notation"] = True + + self.parse_attrib(bgp_global, bgp) + + # Read group + if "group" in bgp.keys(): + bgp_groups = [] + bgp_group = {} + groups = bgp.get("group") + if isinstance(groups, dict): + self.parse_attrib(bgp_group, groups) + # parse neighbors + if "neighbor" in groups.keys(): + neighbors_lst = [] + neighbors_dict = {} + neighbors = groups.get("neighbor") + if isinstance(neighbors, dict): + self.parse_attrib( + neighbors_dict, + neighbors, + "neighbor", + ) + if neighbors_dict: + neighbors_lst.append(neighbors_dict) + else: + for neighbor in neighbors: + self.parse_attrib( + neighbors_dict, + neighbor, + "neighbor", + ) + if neighbors_dict: + neighbors_lst.append(neighbors_dict) + neighbors_dict = {} + if neighbors_lst: + bgp_group["neighbors"] = neighbors_lst + + if bgp_group: + bgp_groups.append(bgp_group) + else: + for group in groups: + bgp_group = {} + self.parse_attrib(bgp_group, group) + # Parse neighbors in the group list + if "neighbor" in group.keys(): + neighbors_lst = [] + neighbors_dict = {} + neighbors = group.get("neighbor") + if isinstance(neighbors, dict): + self.parse_attrib( + neighbors_dict, + neighbors, + "neighbor", + ) + if neighbors_dict: + neighbors_lst.append(neighbors_dict) + else: + for neighbor in neighbors: + self.parse_attrib( + neighbors_dict, + neighbor, + "neighbor", + ) + if neighbors_dict: + neighbors_lst.append(neighbors_dict) + neighbors_dict = {} + + if neighbors_lst: + bgp_group["neighbors"] = neighbors_lst + + if bgp_group: + bgp_groups.append(bgp_group) + bgp_global["groups"] = bgp_groups + remove_empties(bgp_global) + return bgp_global + + def parse_attrib(self, cfg_dict, conf, type=None): + # Read accept-remote-nexthop value + if "accept-remote-nexthop" in conf.keys(): + cfg_dict["accept_remote_nexthop"] = True + + # Read add-path-display-ipv4-address value + if "add-path-display-ipv4-address" in conf.keys(): + cfg_dict["add_path_display_ipv4_address"] = True + + # Parse advertise-bgp-static dictionary + if "advertise-bgp-static" in conf.keys(): + cfg = {} + if conf.get("advertise-bgp-static") and "advertise-bgp-static" in conf.keys(): + if "policy" in conf["advertise-bgp-static"]: + cfg["policy"] = conf["advertise-bgp-static"].get("policy") + else: + cfg["set"] = True + cfg_dict["advertise_bgp_static"] = cfg + + # Parse advertise-external dictionary + if "advertise-external" in conf.keys(): + cfg = {} + if ( + isinstance(conf.get("advertise-external"), dict) + and "conditional" in conf["advertise-external"].keys() + ): + cfg["conditional"] = True + else: + cfg["set"] = True + cfg_dict["advertise_external"] = cfg + + # Read advertise-from-main-vpn-tables value + if "advertise-from-main-vpn-tables" in conf.keys(): + cfg_dict["advertise_from_main_vpn_tables"] = True + + # Read advertise-inactive value + if "advertise-inactive" in conf.keys(): + cfg_dict["advertise_inactive"] = True + + # Read advertise-peer-as value + if "advertise-peer-as" in conf.keys(): + cfg_dict["advertise_peer_as"] = True + + # Read authentication-algorithm value + if "authentication-algorithm" in conf.keys(): + cfg_dict["authentication_algorithm"] = conf["authentication-algorithm"] + + # Read authentication-key value + if "authentication-key" in conf.keys(): + cfg_dict["authentication_key"] = conf["authentication-key"] + + # Read authentication-key-chain value + if "authentication-key-chain" in conf.keys(): + cfg_dict["authentication_key_chain"] = conf["authentication-key-chain"] + + # Parse bfd-liveness-detection dictionary + if "bfd-liveness-detection" in conf.keys(): + cfg = {} + bld = conf["bfd-liveness-detection"] + # Parse authentication dictionary + if "authentication" in bld.keys(): + a_dict = {} + authentication = bld["authentication"] + if "algorithm" in authentication.keys(): + a_dict["algorithm"] = authentication["algorithm"] + if "key-chain" in authentication.keys(): + a_dict["key_chain"] = authentication["key-chain"] + if "loose-check" in authentication.keys(): + a_dict["loose_check"] = True + cfg["authentication"] = a_dict + + # Parse detection-time dictionary + if "detection-time" in bld.keys(): + dt_dict = {} + d_time = bld["detection-time"] + if "threshold" in d_time.keys(): + dt_dict["threshold"] = d_time["threshold"] + cfg["detection_time"] = dt_dict + # Parse transmit-interval dictionary + if "transmit-interval" in bld.keys(): + t_dict = {} + t_int = bld["transmit-interval"] + if "minimum-interval" in t_int.keys(): + t_dict["minimum_interval"] = t_int["minimum-interval"] + cfg["transmit_interval"] = t_dict + + # Read holddown-interval value + if "holddown-interval" in bld.keys(): + cfg["holddown_interval"] = bld["holddown-interval"] + + # Read minimum-receive-interval value + if "minimum-receive-interval" in bld.keys(): + cfg["minimum_receive_interval"] = bld["minimum-receive-interval"] + + # Read minimum-interval value + if "minimum-interval" in bld.keys(): + cfg["minimum_interval"] = bld["minimum-interval"] + + # Read multiplier value + if "multiplier" in bld.keys(): + cfg["multiplier"] = bld["multiplier"] + + # Read no-adaptation value + if "no-adaptation" in bld.keys(): + cfg["no_adaptation"] = True + + # Read session-mode value + if "session-mode" in bld.keys(): + cfg["session_mode"] = bld["session-mode"] + + # Read version value + if "version" in bld.keys(): + cfg["version"] = bld["version"] + + # write the bfd_liveness_detection to bgp global config dictionary + cfg_dict["bfd_liveness_detection"] = cfg + + # Parse bgp-error-tolerance dictionary + if "bgp-error-tolerance" in conf.keys(): + cfg = {} + bet = conf["bgp-error-tolerance"] + # Parse authentication dictionary + if "malformed-route-limit" in bet.keys(): + cfg["malformed_route_limit"] = bet["malformed-route-limit"] + if "malformed-update-log-interval" in bet.keys(): + cfg["malformed_update_log_interval"] = bet["malformed-update-log-interval"] + if "no-malformed-route-limit" in bet.keys(): + cfg["no_malformed_route_limit"] = True + # write the bfd_liveness_detection to bgp global config dictionary + cfg_dict["bgp_error_tolerance"] = cfg + + # Parse bmp dictionary + if "bmp" in conf.keys(): + cfg = {} + bmp = conf["bmp"] + # Parse authentication dictionary + if "route-monitoring" in bmp.keys(): + rm_dict = {} + r_monitoring = bmp["route-monitoring"] + + # Read none attribute value + if "none" in r_monitoring.keys(): + rm_dict["none"] = True + + # Read post-policy attribute value + if "post-policy" in r_monitoring.keys(): + if r_monitoring["post-policy"].get("exclude-non-eligible"): + rm_dict["post_policy_exclude_non_eligible"] = True + else: + rm_dict["post_policy"] = True + + # Read pre-policy attribute value + if "pre-policy" in r_monitoring.keys(): + if r_monitoring["pre-policy"].get("exclude-non-feasible"): + rm_dict["pre_policy_exclude_non_feasible"] = True + else: + rm_dict["pre_policy"] = True + cfg["route_monitoring"] = rm_dict + # Read monitor value + if "monitor" in bmp.keys(): + if bmp["monitor"] == "disable": + cfg["monitor"] = False + else: + cfg["monitor"] = True + + # write the bmp to bgp global config dictionary + cfg_dict["bmp"] = cfg + + # Read cluster value + if "cluster" in conf.keys(): + cfg_dict["cluster_id"] = conf["cluster"] + + # Read damping value + if "damping" in conf.keys(): + cfg_dict["damping"] = True + + # Read description value + if "description" in conf.keys(): + cfg_dict["description"] = conf["description"] + + # Read disable value + if "disable" in conf.keys(): + cfg_dict["disable"] = True + + # Read egress-te value + if "egress-te" in conf.keys(): + cfg = {} + if conf.get("egress-te") and "backup-path" in conf["egress-te"].keys(): + cfg["backup_path"] = conf["egress-te"].get("backup-path") + else: + cfg["set"] = True + cfg_dict["egress_te"] = cfg + + # Read egress-te-backup-paths + if "egress-te-backup-paths" in conf.keys(): + cfg = {} + templates_lst = [] + template_dict = {} + templates = conf["egress-te-backup-paths"].get("template") + if isinstance(templates, dict): + template_dict["path_name"] = templates["name"] + if "remote-nexthop" in templates.keys(): + template_dict["remote_nexthop"] = templates["remote-nexthop"].get( + "remote-nh-addr", + ) + if "peer" in templates.keys(): + peer_lst = [] + peers = templates.get("peer") + if isinstance(peers, dict): + peer_lst.append(peers.get("name")) + else: + for peer in peers: + peer_lst.append(peer.get("name")) + template_dict["peers"] = peer_lst + + if "ip-forward" in templates.keys(): + ipf_dict = {} + if templates.get("ip-forward") is None: + ipf_dict["set"] = True + else: + ipf_dict["rti_name"] = templates["ip-forward"].get( + "rti-name", + ) + template_dict["ip_forward"] = ipf_dict + if template_dict: + templates_lst.append(template_dict) + else: + # We have list of templates + for template in templates: + template_dict = {} + template_dict["path_name"] = template["name"] + if "remote-nexthop" in template.keys(): + template_dict["remote_nexthop"] = template["remote-nexthop"].get( + "remote-nh-addr", + ) + if "peer" in template.keys(): + peer_lst = [] + peers = template.get("peer") + if isinstance(peers, dict): + peer_lst.append(peers.get("name")) + else: + for peer in peers: + peer_lst.append(peer.get("name")) + template_dict["peers"] = peer_lst + if "ip-forward" in template.keys(): + ipf_dict = {} + if template.get("ip-forward") is None: + ipf_dict["set"] = True + else: + ipf_dict["rti_name"] = template["ip-forward"].get( + "rti-name", + ) + template_dict["ip_forward"] = ipf_dict + + if template_dict: + templates_lst.append(template_dict) + + if templates: + cfg["templates"] = templates_lst + cfg_dict["egress_te_backup_paths"] = cfg + + # Read egress-te-set-segment + if "egress-te-set-segment" in conf.keys(): + etss_lst = [] + etss_dict = {} + etsss = conf["egress-te-set-segment"] + if isinstance(etsss, dict): + etss_dict["name"] = etsss.get("name") + if "label" in etsss.keys(): + etss_dict["label"] = etsss["label"].get("label-value") + if "egress-te-backup-segment" in etsss.keys(): + etbs = etsss.get("egress-te-backup-segment") + etss_dict["egress_te_backup_segment_label"] = etbs["label"].get("label-value") + + if etss_dict: + etss_lst.append(etss_dict) + else: + for etss in etsss: + etss_dict["name"] = etss.get("name") + if "label" in etss.keys(): + etss_dict["label"] = etss["label"].get("label-value") + if "egress-te-backup-segment" in etss.keys(): + etbs = etss.get("egress-te-backup-segment") + etss_dict["egress_te_backup_segment_label"] = etbs["label"].get( + "label-value", + ) + + if etss_dict: + etss_lst.append(etss_dict) + etss_dict = {} + cfg_dict["egress_te_set_segment"] = etss_lst + + # Read egress-te-sid-stats value + if "egress-te-sid-stats" in conf.keys(): + cfg_dict["egress_te_sid_stats"] = True + + # Read enforce-first-as value + if "enforce-first-as" in conf.keys(): + cfg_dict["enforce_first_as"] = True + + # Read export value + if "export" in conf.keys(): + cfg_dict["export"] = conf["export"] + + # Read forwarding-context value + if "forwarding-context" in conf.keys(): + cfg_dict["forwarding_context"] = conf["forwarding-context"] + + # Read graceful-restart + if "graceful-restart" in conf.keys(): + cfg = {} + gr = conf.get("graceful-restart") + if gr is None: + cfg["set"] = True + else: + if "disable" in gr.keys(): + cfg["disable"] = True + if "dont-help-shared-fate-bfd-down" in gr.keys(): + cfg["dont_help_shared_fate_bfd_down"] = True + # read forwarding-state-bit + if "forwarding-state-bit" in gr.keys(): + fsb_dict = {} + if "as-rr-client" == gr.get("forwarding-state-bit"): + fsb_dict["as_rr_client"] = True + if "from-fib" == gr.get("forwarding-state-bit"): + fsb_dict["from_fib"] = True + cfg["forwarding_state_bit"] = fsb_dict + # read long-lived + if "long-lived" in gr.keys(): + ll_dict = {} + ll = gr.get("long-lived") + # read advertise_to_non_llgr_neighbor + if "advertise-to-non-llgr-neighbor" in ll.keys(): + atnln_dict = {} + atnln = ll.get("advertise-to-non-llgr-neighbor") + if atnln is None: + atnln_dict["set"] = True + else: + atnln_dict["omit_no_export"] = True + ll_dict["advertise_to_non_llgr_neighbor"] = atnln_dict + if "receiver" in ll.keys(): + ll_dict["receiver_disable"] = True + cfg["long_lived"] = ll_dict + # read restart-time + if "restart-time" in gr.keys(): + cfg["restart_time"] = gr.get("restart-time") + # read stale-routes-time + if "stale-routes-time" in gr.keys(): + cfg["stale_routes_time"] = gr.get("stale-routes-time") + + cfg_dict["graceful_restart"] = cfg + + # Read hold-time value + if "hold-time" in conf.keys(): + cfg_dict["hold_time"] = conf["hold-time"] + + # Read holddown-all-stale-labels value + if "holddown-all-stale-labels" in conf.keys(): + cfg_dict["holddown_all_stale_labels"] = True + + # Read idle-after-switch-over + if "idle-after-switch-over" in conf.keys(): + cfg = {} + iaso = conf.get("idle-after-switch-over") + if "forever" in iaso.keys(): + cfg["forever"] = True + else: + cfg["timeout"] = iaso.get("timeout") + cfg_dict["idle_after_switch_over"] = cfg + + # Read import value + if "import" in conf.keys(): + + imports = conf.get("import") + import_lst = [] + if isinstance(imports, dict): + import_lst.append(imports) + else: + for entry in imports: + import_lst.append(entry) + cfg_dict["import"] = import_lst + + # Read include-mp-next-hop value + if "include-mp-next-hop" in conf.keys(): + cfg_dict["include_mp_next_hop"] = True + + # Read ipsec-sa value + if "ipsec-sa" in conf.keys(): + cfg_dict["ipsec_sa"] = conf["ipsec-sa"] + + # Read keep value + if "keep" in conf.keys(): + cfg_dict["keep"] = conf["keep"] + + # Read local-address value + if "local-address" in conf.keys(): + cfg_dict["local_address"] = conf["local-address"] + + # Read local_as value + if "local-as" in conf.keys(): + cfg = {} + la = conf.get("local-as") + cfg["as_num"] = la.get("as-number") + if "alias" in la.keys(): + cfg["alias"] = True + if "private" in la.keys(): + cfg["private"] = True + if "loops" in la.keys(): + cfg["loops"] = la.get("loops") + if "no-prepend-global-as" in la.keys(): + cfg["no_prepend_global_as"] = True + cfg_dict["local_as"] = cfg + + # Read local-interface value + if "local-interface" in conf.keys(): + cfg_dict["local_interface"] = conf["local-interface"] + + # Read local-preference value + if "local-preference" in conf.keys(): + cfg_dict["local_preference"] = conf["local-preference"] + + # Read log-updown value + if "log-updown" in conf.keys(): + cfg_dict["log_updown"] = True + + # Read metric-out + if "metric-out" in conf.keys(): + cfg = {} + mo = conf.get("metric-out") + # metric value + if "metric-value" in mo.keys(): + cfg["metric_value"] = mo.get("metric-value") + # read igp + if "igp" in mo.keys(): + igp_dict = {} + igp = mo.get("igp") + if igp is None: + igp_dict["set"] = True + else: + if "metric-offset" in igp.keys(): + igp_dict["metric_offset"] = igp.get("metric-offset") + if "delay-med-update" in igp.keys(): + igp_dict["delay_med_update"] = True + cfg["igp"] = igp_dict + # read minimum-igp + if "minimum-igp" in mo.keys(): + minigp_dict = {} + minigp = mo.get("minimum-igp") + if minigp is None: + minigp_dict["set"] = True + else: + if "metric-offset" in minigp.keys(): + minigp_dict["metric_offset"] = minigp.get( + "metric-offset", + ) + cfg["minimum_igp"] = minigp_dict + cfg_dict["metric_out"] = cfg + + # Read mtu-discovery value + if "mtu-discovery" in conf.keys(): + cfg_dict["mtu_discovery"] = True + + # Read multihop value + if "multihop" in conf.keys(): + cfg = {} + multihop = conf.get("multihop") + if multihop is None: + cfg["set"] = True + else: + if "no-nexthop-change" in multihop.keys(): + cfg["no_nexthop_change"] = True + if "ttl" in multihop.keys(): + cfg["ttl"] = multihop.get("ttl") + cfg_dict["multihop"] = cfg + + # Read multipath + if "multipath" in conf.keys(): + cfg = {} + multipath = conf.get("multipath") + if multipath is None: + cfg["set"] = True + else: + if "disable" in multipath.keys(): + cfg["disable"] = True + if "multiple-as" in multipath.keys(): + mas = multipath.get("multiple-as") + if mas is None: + cfg["multiple_as"] = True + else: + cfg["multiple_as_disable"] = True + + cfg_dict["multipath"] = cfg + + # Read no-advertise-peer-as value + if "no-advertise-peer-as" in conf.keys(): + cfg_dict["no_advertise_peer_as"] = True + + # Read no-aggregator-id value + if "no-aggregator-id" in conf.keys(): + cfg_dict["no_aggregator_id"] = True + + # Read no-client-reflect value + if "no-client-reflect" in conf.keys(): + cfg_dict["no_client_reflect"] = True + + # Read no-precision-timers value + if "no-precision-timers" in conf.keys(): + cfg_dict["no_precision_timers"] = True + + # Read out-delay value + if "out-delay" in conf.keys(): + cfg_dict["out_delay"] = conf["out-delay"] + + # Read outbound-route-filter + if "outbound-route-filter" in conf.keys(): + cfg = {} + orf = conf.get("outbound-route-filter") + # read outbound-route-filter + if "bgp-orf-cisco-mode" in orf.keys(): + cfg["bgp_orf_cisco_mode"] = True + # read prefix-based + if "prefix-based" in orf.keys(): + pb = orf.get("prefix-based") + pb_dict = {} + if pb is None: + pb_dict["set"] = True + else: + if "accept" in pb.keys(): + # read accept node attributes + accept = pb.get("accept") + accept_dict = {} + if accept is None: + accept_dict["set"] = True + else: + if "inet" in accept.keys(): + accept_dict["inet"] = True + if "inet6" in accept.keys(): + accept_dict["inet6"] = True + pb_dict["accept"] = accept_dict + cfg["prefix_based"] = pb_dict + + cfg_dict["outbound_route_filter"] = cfg + + # Read output-queue-priority value + if "output-queue-priority" in conf.keys(): + cfg = {} + oqp_dict = {} + oqp = conf.get("output-queue-priority") + # read defaults + if "defaults" in oqp.keys(): + defaults = oqp.get("defaults") + defaults_dict = {} + # read high + if "high" in defaults.keys(): + high_dict = {} + high = defaults.get("high") + if "expedited" in high: + high_dict["expedited"] = True + + defaults_dict["high"] = high_dict + # read low + if "low" in defaults.keys(): + low_dict = {} + low = defaults.get("low") + if "expedited" in low: + low_dict["expedited"] = True + # read medium + if "medium" in defaults.keys(): + medium_dict = {} + medium = defaults.get("medium") + if "expedited" in medium: + medium_dict["expedited"] = True + oqp_dict["defaults"] = defaults_dict + + # read expedited + if "expedited" in oqp.keys(): + expedited = oqp.get("expedited") + if "update-tokens" in expedited: + oqp_dict["expedited_update_tokens"] = expedited.get( + "update-tokens", + ) + + # read priority + if "priority" in oqp.keys(): + priority_lst = [] + priority_dict = {} + priority = oqp.get("priority") + if isinstance(priority, dict): + priority_dict["priority"] = priority.get("name") + priority_dict["update_tokens"] = priority.get( + "update-tokens", + ) + priority_lst.append(priority_dict) + else: + for element in priority: + priority_dict["priority"] = element.get("name") + priority_dict["update_tokens"] = element.get( + "update-tokens", + ) + priority_lst.append(priority_dict) + priority_dict = {} + + oqp_dict["priority_update_tokens"] = priority_lst + + cfg_dict["output_queue_priority"] = oqp_dict + + # Read passive value + if "passive" in conf.keys(): + cfg_dict["passive"] = True + + # Read path-selection value + if "path-selection" in conf.keys(): + ps_dict = {} + ps = conf.get("path-selection") + if "always-compare-med" in ps.keys(): + ps_dict["always_compare_med"] = True + if "as-path-ignore" in ps.keys(): + ps_dict["as_path_ignore"] = True + if "external-router-id" in ps.keys(): + ps_dict["external_router_id"] = True + if "cisco-non-deterministic" in ps.keys(): + ps_dict["cisco_non_deterministic"] = True + if "l2vpn-use-bgp-rules" in ps.keys(): + ps_dict["l2vpn_use_bgp_rules"] = True + # read med-plus-igp + if "med-plus-igp" in ps.keys(): + mpi_dict = {} + mpi = ps.get("med-plus-igp") + if mpi is None: + mpi_dict["set"] = True + else: + if "igp-multiplier" in mpi.keys(): + mpi_dict["igp_multiplier"] = mpi.get("igp-multiplier") + if "med-multiplier" in mpi.keys(): + mpi_dict["med_multiplier"] = mpi.get("med-multiplier") + ps_dict["med_plus_igp"] = mpi_dict + + cfg_dict["path_selection"] = ps_dict + + # Read peer-as value + if "peer-as" in conf.keys(): + cfg_dict["peer_as"] = conf["peer-as"] + + # Read precision-timers value + if "precision-timers" in conf.keys(): + cfg_dict["precision_timers"] = True + + # Read preference value + if "preference" in conf.keys(): + cfg_dict["preference"] = conf["preference"] + + # Read remove-private value + if "remove-private" in conf.keys(): + rp_dict = {} + rp = conf.get("remove-private") + if rp is None: + rp_dict["set"] = True + else: + if "all" in rp.keys(): + all = rp.get("all") + if all is None: + rp_dict["all"] = True + else: + if "replace" in all.keys(): + replace = all.get("replace") + if replace is None: + rp_dict["all_replace"] = True + else: + rp_dict["all_replace_nearest"] = True + + cfg_dict["remove_private"] = rp_dict + + # Read rfc6514-compliant-safi129 value + if "rfc6514-compliant-safi129" in conf.keys(): + cfg_dict["rfc6514_compliant_safi129"] = True + + # Read route-server-client value + if "route-server-client" in conf.keys(): + cfg_dict["route_server_client"] = True + + # Read send-addpath-optimization value + if "send-addpath-optimization" in conf.keys(): + cfg_dict["send_addpath_optimization"] = True + + # Read snmp-options value + if "snmp-options" in conf.keys(): + cfg = {} + so = conf.get("snmp-options") + if "backward-traps-only-from-established" in so: + cfg["backward_traps_only_from_established"] = True + if "emit-inet-address-length-in-oid" in so: + cfg["emit_inet_address_length_in_oid"] = True + cfg_dict["snmp_options"] = cfg + + # Read sr-preference-override value + if "sr-preference-override" in conf.keys(): + cfg_dict["sr_preference_override"] = conf["sr-preference-override"] + + # Read stale-labels-holddown-period value + if "stale-labels-holddown-period" in conf.keys(): + cfg_dict["stale_labels_holddown_period"] = conf["stale-labels-holddown-period"] + + # Read tcp-aggressive-transmission value + if "tcp-aggressive-transmission" in conf.keys(): + cfg_dict["tcp_aggressive_transmission"] = True + + # Read tcp-mss value + if "tcp-mss" in conf.keys(): + cfg_dict["tcp_mss"] = conf["tcp-mss"] + + # Read traceoptions value + if "traceoptions" in conf.keys(): + to_dict = {} + to = conf.get("traceoptions") + # read file + if "file" in to.keys(): + file_dict = {} + file = to.get("file") + if "filename" in file.keys(): + file_dict["filename"] = file.get("filename") + if "files" in file.keys(): + file_dict["files"] = file.get("files") + if "no-world-readable" in file.keys(): + file_dict["no_world_readable"] = True + if "world-readable" in file.keys(): + file_dict["world_readable"] = True + if "size" in file.keys(): + file_dict["size"] = file.get("size") + to_dict["file"] = file_dict + # read flag + if "flag" in to.keys(): + flag_lst = [] + flag = to.get("flag") + for event in flag: + flag_dict = {} + if event is not None: + if "detail" in event.keys(): + flag_dict["detail"] = True + if "disable" in event.keys(): + flag_dict["disable"] = True + if "receive" in event.keys(): + flag_dict["receive"] = True + if "send" in event.keys(): + flag_dict["send"] = True + if "filter" in event.keys(): + filter_dict = {} + filter = event.get("filter") + if filter is None: + filter_dict["set"] = True + else: + if "match-on" in filter.keys(): + filter_dict["match_on_prefix"] = True + if "policy" in filter.keys(): + filter_dict["policy"] = filter.get( + "policy", + ) + flag_dict["filter"] = filter_dict + flag_dict["name"] = event.get("name") + flag_lst.append(flag_dict) + + to_dict["flag"] = flag_lst + cfg_dict["traceoptions"] = to_dict + # Read traffic-statistics-labeled-path + if "traffic-statistics-labeled-path" in conf.keys(): + tslp_dict = {} + tslp = conf.get("traffic-statistics-labeled-path") + # read file + if "file" in tslp.keys(): + file_dict = {} + file = tslp.get("file") + if "filename" in file.keys(): + file_dict["filename"] = file.get("filename") + if "files" in file.keys(): + file_dict["files"] = file.get("files") + if "no-world-readable" in file.keys(): + file_dict["no_world_readable"] = True + if "world-readable" in file.keys(): + file_dict["world_readable"] = True + if "size" in file.keys(): + file_dict["size"] = file.get("size") + tslp_dict["file"] = file_dict + # read interval + if "interval" in tslp.keys(): + tslp_dict["interval"] = tslp.get("interval") + + cfg_dict["traffic_statistics_labeled_path"] = tslp_dict + # Read ttl value + if "ttl" in conf.keys(): + cfg_dict["ttl"] = conf["ttl"] + + # Read unconfigured-peer-graceful-restart value + if "unconfigured-peer-graceful-restart" in conf.keys(): + cfg_dict["unconfigured_peer_graceful_restart"] = True + + # Read vpn-apply-export value + if "vpn-apply-export" in conf.keys(): + cfg_dict["vpn_apply_export"] = True + + # Read group name value + if "name" in conf.keys(): + if type == "neighbor": + cfg_dict["neighbor_address"] = conf["name"] + else: + cfg_dict["name"] = conf["name"] + + # Read as-override value + if "as-override" in conf.keys(): + cfg_dict["as_override"] = True + + # Read allow + if "allow" in conf.keys(): + allow_lst = [] + allow = conf["allow"] + if isinstance(allow, list): + for item in allow: + allow_lst.append(item) + else: + allow_lst.append(allow) + if allow_lst: + cfg_dict["allow"] = allow_lst + + # Read optimal-route-reflection + if "optimal-route-reflection" in conf.keys(): + orr_dict = {} + orr = conf["optimal-route-reflection"] + + if "igp-backup" in orr.keys(): + orr_dict["igp_backup"] = orr.get("igp-backup") + + if "igp-primary" in orr.keys(): + orr_dict["igp_primary"] = orr.get("igp-primary") + cfg_dict["optimal_route_reflection"] = orr_dict + + # Read group type value + if "type" in conf.keys(): + cfg_dict["type"] = conf["type"] + + # Read unconfigured-peer-graceful-restart value + if "unconfigured-peer-graceful-restart" in conf.keys(): + cfg_dict["unconfigured_peer_graceful_restart"] = True + + # Read vpn-apply-export value + if "vpn-apply-export" in conf.keys(): + cfg_dict["vpn_apply_export"] = True diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/facts.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/facts.py new file mode 100644 index 000000000..bf596dcc4 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/facts.py @@ -0,0 +1,203 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The facts class for junos +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.junipernetworks.junos.plugins.module_utils.network.junos.facts.acl_interfaces.acl_interfaces import ( + Acl_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.acls.acls import ( + AclsFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.bgp_address_family.bgp_address_family import ( + Bgp_address_familyFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.bgp_global.bgp_global import ( + Bgp_globalFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.hostname.hostname import ( + HostnameFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.interfaces.interfaces import ( + InterfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.l2_interfaces.l2_interfaces import ( + L2_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.l3_interfaces.l3_interfaces import ( + L3_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.lacp.lacp import ( + LacpFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.lag_interfaces.lag_interfaces import ( + Lag_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.legacy.base import ( + Config, + Default, + Hardware, + Interfaces, + OFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.lldp_global.lldp_global import ( + Lldp_globalFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.logging_global.logging_global import ( + Logging_globalFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.ntp_global.ntp_global import ( + Ntp_globalFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.ospfv2.ospfv2 import ( + Ospfv2Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.ospfv3.ospfv3 import ( + Ospfv3Facts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.prefix_lists.prefix_lists import ( + Prefix_listsFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.routing_instances.routing_instances import ( + Routing_instancesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.routing_options.routing_options import ( + Routing_optionsFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.security_policies.security_policies import ( + Security_policiesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.security_policies_global.security_policies_global import ( + Security_policies_globalFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.security_zones.security_zones import ( + Security_zonesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.snmp_server.snmp_server import ( + Snmp_serverFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.static_routes.static_routes import ( + Static_routesFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.vlans.vlans import ( + VlansFacts, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + HAS_PYEZ, +) + + +FACT_LEGACY_SUBSETS = dict( + default=Default, + hardware=Hardware, + config=Config, + interfaces=Interfaces, +) +FACT_RESOURCE_SUBSETS = dict( + acls=AclsFacts, + acl_interfaces=Acl_interfacesFacts, + interfaces=InterfacesFacts, + lacp=LacpFacts, + lacp_interfaces=Lacp_interfacesFacts, + lag_interfaces=Lag_interfacesFacts, + l2_interfaces=L2_interfacesFacts, + l3_interfaces=L3_interfacesFacts, + lldp_global=Lldp_globalFacts, + lldp_interfaces=Lldp_interfacesFacts, + ospf=Ospfv2Facts, + ospfv3=Ospfv3Facts, + ospf_interfaces=Ospf_interfacesFacts, + vlans=VlansFacts, + static_routes=Static_routesFacts, + bgp_global=Bgp_globalFacts, + bgp_address_family=Bgp_address_familyFacts, + routing_instances=Routing_instancesFacts, + prefix_lists=Prefix_listsFacts, + logging_global=Logging_globalFacts, + ntp_global=Ntp_globalFacts, + security_policies=Security_policiesFacts, + security_policies_global=Security_policies_globalFacts, + security_zones=Security_zonesFacts, + snmp_server=Snmp_serverFacts, + routing_options=Routing_optionsFacts, + hostname=HostnameFacts, +) + + +class Facts(FactsBase): + """The fact class for junos""" + + 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 junos + :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 not legacy_facts_type: + legacy_facts_type = self._gather_subset + # fetch old style facts only when explicitly mentioned in gather_subset option + if "ofacts" in legacy_facts_type: + if HAS_PYEZ: + self.ansible_facts.update(OFacts(self._module).populate()) + else: + self._warnings.extend( + [ + "junos-eznc is required to gather old style facts but does not appear to be installed. " + "It can be installed using `pip install junos-eznc`", + ], + ) + self.ansible_facts["ansible_net_gather_subset"].append( + "ofacts", + ) + legacy_facts_type.remove("ofacts") + + 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/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/hostname.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/hostname.py new file mode 100644 index 000000000..9078ff456 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/hostname/hostname.py @@ -0,0 +1,137 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos hostname fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.hostname.hostname import ( + HostnameArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class HostnameFacts(object): + """The junos hostname fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = HostnameArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ntp_gloabl + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <system> + </system> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/system/host-name") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["hostname"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["hostname"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + hostname_config = {} + + # Parse facts for hostname node + + if "host-name" in conf.keys(): + hostname_config["hostname"] = conf.get("host-name") + return utils.remove_empties(hostname_config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/interfaces.py new file mode 100644 index 000000000..e83265603 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/interfaces/interfaces.py @@ -0,0 +1,179 @@ +# +# -*- 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 junos 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.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class InterfacesFacts(object): + """The junos interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = InterfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_config(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return get_resource_config(connection, config_filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for interfaces + + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = self.get_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/interfaces/interface") + + objs = [] + for resource in resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + 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 _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 ElementTree instance of configuration object + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config["name"] = utils.get_xml_conf_arg(conf, "name") + config["description"] = utils.get_xml_conf_arg(conf, "description") + mtu = utils.get_xml_conf_arg(conf, "mtu") + config["mtu"] = int(mtu) if mtu else None + config["speed"] = utils.get_xml_conf_arg(conf, "speed") + config["duplex"] = utils.get_xml_conf_arg(conf, "link-mode") + config["hold_time"]["down"] = utils.get_xml_conf_arg( + conf, + "hold-time/down", + ) + config["hold_time"]["up"] = utils.get_xml_conf_arg( + conf, + "hold-time/up", + ) + disable = utils.get_xml_conf_arg(conf, "disable", data="tag") + if disable: + config["enabled"] = False + else: + config["enabled"] = True + cfg = self._get_xml_dict(conf) + unit_cfg = cfg.get("interface") + if "unit" in unit_cfg.keys(): + units = unit_cfg.get("unit") + unit_lst = [] + unit_dict = {} + if isinstance(units, dict): + if "description" in units.keys(): + unit_dict["name"] = units["name"] + unit_dict["description"] = units["description"] + unit_lst.append(unit_dict) + else: + for unit in units: + if "description" in unit.keys(): + unit_dict["name"] = unit["name"] + unit_dict["description"] = unit["description"] + unit_lst.append(unit_dict) + unit_dict = {} + config["units"] = unit_lst + + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/l2_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..19d85e0d0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l2_interfaces/l2_interfaces.py @@ -0,0 +1,162 @@ +# +# -*- 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 junos 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 + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.l2_interfaces.l2_interfaces import ( + L2_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class L2_interfacesFacts(object): + """The junos l2_interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = L2_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_config(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return get_resource_config(connection, config_filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = self.get_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + self._resources = data.xpath("configuration/interfaces/interface") + + objs = [] + for resource in self._resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + 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 ElementTree instance of configuration object + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + + enhanced_layer = True + mode = utils.get_xml_conf_arg( + conf, + "unit/family/ethernet-switching/interface-mode", + ) + + if mode is None: + mode = utils.get_xml_conf_arg( + conf, + "unit/family/ethernet-switching/port-mode", + ) + enhanced_layer = False + + # Layer 2 is configured on interface + if mode: + config["name"] = utils.get_xml_conf_arg(conf, "name") + unit = utils.get_xml_conf_arg(conf, "unit/name") + config["unit"] = unit if unit else 0 + config["enhanced_layer"] = enhanced_layer + + if mode == "access": + config["access"] = {} + config["access"]["vlan"] = utils.get_xml_conf_arg( + conf, + "unit/family/ethernet-switching/vlan/members", + ) + elif mode == "trunk": + config["trunk"] = {} + vlan_members = conf.xpath( + "unit/family/ethernet-switching/vlan/members", + ) + if vlan_members: + config["trunk"]["allowed_vlans"] = [] + for vlan_member in vlan_members: + config["trunk"]["allowed_vlans"].append( + vlan_member.text, + ) + + config["trunk"]["native_vlan"] = utils.get_xml_conf_arg( + conf, + "native-vlan-id", + ) + + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/l3_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..010c3d77c --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/l3_interfaces/l3_interfaces.py @@ -0,0 +1,180 @@ +# +# -*- 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 junos 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 + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import iteritems, string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.l3_interfaces.l3_interfaces import ( + L3_interfacesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class L3_interfacesFacts(object): + """The junos 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 get_config(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for l3_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = self.get_config(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + resources = data.xpath("configuration/interfaces/interface") + config = [] + if resources: + config = self.parse_l3_if_resources(resources) + facts = {} + facts["l3_interfaces"] = config + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def parse_l3_if_resources(self, l3_if_resources): + l3_ifaces = [] + for iface in l3_if_resources: + int_have = self._get_xml_dict(iface) + int_dict = int_have["interface"] + if "unit" in int_dict.keys() and int_dict.get("unit") is not None: + unit_list = int_dict["unit"] + if isinstance(unit_list, list): + for item in unit_list: + fact_dict = self._render_l3_intf(item, int_dict) + if fact_dict: + l3_ifaces.append(fact_dict) + else: + fact_dict = self._render_l3_intf(unit_list, int_dict) + if fact_dict: + l3_ifaces.append(fact_dict) + return l3_ifaces + + def _render_l3_intf(self, unit, int_dict): + """ + + :param item: + :param int_dict: + :return: + """ + interface = {} + ipv4 = [] + ipv6 = [] + if "family" in unit.keys(): + if "inet" in unit["family"].keys(): + interface["name"] = int_dict["name"] + interface["unit"] = unit["name"] + inet = unit["family"].get("inet") + if inet is not None and "address" in inet.keys(): + if isinstance(inet["address"], dict): + for key, value in iteritems(inet["address"]): + addr = {} + addr["address"] = value + ipv4.append(addr) + else: + for ip in inet["address"]: + addr = {} + addr["address"] = ip["name"] + ipv4.append(addr) + if "inet" in unit["family"].keys(): + interface["name"] = int_dict["name"] + interface["unit"] = unit["name"] + inet = unit["family"].get("inet") + if inet is not None and "dhcp" in inet.keys(): + addr = {} + addr["address"] = "dhcp" + ipv4.append(addr) + if "inet6" in unit["family"].keys(): + interface["name"] = int_dict["name"] + interface["unit"] = unit["name"] + inet6 = unit["family"].get("inet6") + if inet6 is not None and "address" in inet6.keys(): + if isinstance(inet6["address"], dict): + for key, value in iteritems(inet6["address"]): + addr = {} + addr["address"] = value + ipv6.append(addr) + else: + for ip in inet6["address"]: + addr = {} + addr["address"] = ip["name"] + ipv4.append(addr) + interface["ipv4"] = ipv4 + interface["ipv6"] = ipv6 + return utils.remove_empties(interface) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/lacp.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/lacp.py new file mode 100644 index 000000000..6c3ecd904 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp/lacp.py @@ -0,0 +1,119 @@ +# +# -*- 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 junos 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.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lacp.lacp import ( + LacpArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class LacpFacts(object): + """The junos 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 interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <chassis> + <aggregated-devices> + <ethernet> + <lacp> + </lacp> + </ethernet> + </aggregated-devices> + </chassis> + </configuration> + """ + data = get_resource_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + facts = {} + config = deepcopy(self.generated_spec) + resources = data.xpath( + "configuration/chassis/aggregated-devices/ethernet/lacp", + ) + if resources: + + lacp_root = resources[0] + config["system_priority"] = utils.get_xml_conf_arg( + lacp_root, + "system-priority", + ) + + if utils.get_xml_conf_arg( + lacp_root, + "link-protection/non-revertive", + data="tag", + ): + config["link_protection"] = "non-revertive" + + elif utils.get_xml_conf_arg(lacp_root, "link-protection"): + config["link_protection"] = "revertive" + + params = utils.validate_config( + self.argument_spec, + {"config": utils.remove_empties(config)}, + ) + facts["lacp"] = {} + facts["lacp"].update(utils.remove_empties(params["config"])) + + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/lacp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..52b776a53 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,146 @@ +# +# -*- 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 junos 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 + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class Lacp_interfacesFacts(object): + """The junos 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 interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = get_resource_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + self._resources = data.xpath("configuration/interfaces/interface") + + objs = [] + for resource in self._resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + if obj: + objs.append(obj) + facts = {} + if objs: + facts["lacp_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["lacp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + :param spec: The facts tree, generated from the argspec + :param conf: The ElementTree instance of configuration object + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config["name"] = utils.get_xml_conf_arg(conf, "name") + config["period"] = utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/lacp/periodic", + ) + config["sync_reset"] = utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/lacp/sync-reset", + ) + force_up = utils.get_xml_conf_arg( + conf, + "ether-options/ieee-802.3ad/lacp/force-up", + data="tag", + ) + if force_up: + config["force_up"] = True + config["port_priority"] = utils.get_xml_conf_arg( + conf, + "ether-options/ieee-802.3ad/lacp/port-priority", + ) + config["system"]["priority"] = utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/lacp/system-priority", + ) + address = utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/lacp/system-id", + ) + if address: + config["system"].update({"mac": {"address": address}}) + + lacp_intf_cfg = utils.remove_empties(config) + # if lacp config is not present for interface return empty dict + if len(lacp_intf_cfg) == 1: + return {} + else: + return lacp_intf_cfg diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/lag_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..5066cac9f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lag_interfaces/lag_interfaces.py @@ -0,0 +1,160 @@ +# +# -*- 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 junos 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 + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class Lag_interfacesFacts(object): + """The junos lag_interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lag_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <interfaces/> + </configuration> + """ + data = get_resource_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + self._resources = data.xpath("configuration/interfaces/interface") + + objs = [] + for resource in self._resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + if obj: + objs.append(obj) + facts = {} + if objs: + facts["lag_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["lag_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, 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 ElementTree instance of configuration object + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + intf_name = utils.get_xml_conf_arg(conf, "name") + if intf_name.startswith("ae"): + config["name"] = intf_name + config["members"] = [] + for interface_obj in self._resources: + lag_interface_member = utils.get_xml_conf_arg( + interface_obj, + "ether-options/ieee-802.3ad[bundle='%s']/../../name" % intf_name, + ) + if lag_interface_member: + member_config = {} + member_config["member"] = lag_interface_member + if utils.get_xml_conf_arg( + interface_obj, + "ether-options/ieee-802.3ad/primary", + data="tag", + ): + member_config["link_type"] = "primary" + elif utils.get_xml_conf_arg( + interface_obj, + "ether-options/ieee-802.3ad/backup", + data="tag", + ): + member_config["link_type"] = "backup" + + if member_config: + config["members"].append(member_config) + + for m in ["active", "passive"]: + if utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/lacp/%s" % m, + data="tag", + ): + config["mode"] = m + break + + link_protection = utils.get_xml_conf_arg( + conf, + "aggregated-ether-options/link-protection", + data="tag", + ) + if link_protection: + config["link_protection"] = True + + lag_intf_cfg = utils.remove_empties(config) + # if lag interfaces config is not present return empty dict + if len(lag_intf_cfg) == 1: + return {} + else: + return lag_intf_cfg diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/base.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/base.py new file mode 100644 index 000000000..11f6544aa --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/legacy/base.py @@ -0,0 +1,232 @@ +# -*- 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 junos interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import platform + + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import missing_required_lib +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + exec_rpc, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_capabilities, + get_configuration, + get_device, + tostring, +) + + +try: + from lxml.etree import Element, SubElement +except ImportError: + from xml.etree.ElementTree import Element, SubElement + + +class FactsBase(object): + def __init__(self, module): + self.module = module + self.facts = dict() + self.warnings = [] + + def populate(self): + raise NotImplementedError + + def cli(self, command): + reply = command(self.module, command) + output = reply.find(".//output") + if not output: + self.module.fail_json( + msg="failed to retrieve facts for command %s" % command, + ) + return to_text(output.text).strip() + + def rpc(self, rpc): + return exec_rpc(self.module, tostring(Element(rpc))) + + def get_text(self, ele, tag): + try: + return to_text(ele.find(tag).text).strip() + except AttributeError: + pass + + +class Default(FactsBase): + def populate(self): + self.facts.update(self.platform_facts()) + + reply = self.rpc("get-chassis-inventory") + data = reply.find(".//chassis-inventory/chassis") + self.facts["serialnum"] = self.get_text(data, "serial-number") + + def platform_facts(self): + platform_facts = {} + + resp = get_capabilities(self.module) + device_info = resp["device_info"] + + platform_facts["system"] = device_info["network_os"] + + for item in ("model", "image", "version", "platform", "hostname"): + val = device_info.get("network_os_%s" % item) + if val: + platform_facts[item] = val + + platform_facts["api"] = resp["network_api"] + platform_facts["python_version"] = platform.python_version() + + return platform_facts + + +class Config(FactsBase): + def populate(self): + config_format = self.module.params["config_format"] + reply = get_configuration(self.module, format=config_format) + + if config_format == "xml": + config = tostring(reply.find("configuration")).strip() + + elif config_format == "text": + config = self.get_text(reply, "configuration-text") + + elif config_format == "json": + config = self.module.from_json(reply.text.strip()) + + elif config_format == "set": + config = self.get_text(reply, "configuration-set") + + self.facts["config"] = config + + +class Hardware(FactsBase): + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def populate(self): + + reply = self.rpc("get-system-memory-information") + data = reply.find( + ".//system-memory-information/system-memory-summary-information", + ) + + self.facts.update( + { + "memfree_mb": int(self.get_text(data, "system-memory-free")), + "memtotal_mb": int(self.get_text(data, "system-memory-total")), + }, + ) + + reply = self.rpc("get-system-storage") + data = reply.find(".//system-storage-information") + + filesystems = list() + for obj in data: + filesystems.append(self.get_text(obj, "filesystem-name")) + self.facts["filesystems"] = filesystems + + reply = self.rpc("get-route-engine-information") + data = reply.find(".//route-engine-information") + + routing_engines = dict() + for obj in data: + slot = self.get_text(obj, "slot") + routing_engines.update({slot: {}}) + routing_engines[slot].update({"slot": slot}) + for child in obj: + if child.text != "\n": + routing_engines[slot].update( + {child.tag.replace("-", "_"): child.text}, + ) + + self.facts["routing_engines"] = routing_engines + + if len(data) > 1: + self.facts["has_2RE"] = True + else: + self.facts["has_2RE"] = False + + reply = self.rpc("get-chassis-inventory") + data = reply.findall(".//chassis-module") + + modules = list() + for obj in data: + mod = dict() + for child in obj: + if child.text != "\n": + mod.update({child.tag.replace("-", "_"): child.text}) + if "chassis-sub-module" in child.tag: + mod["chassis_sub_module"] = self._get_xml_dict(obj)["chassis-module"][ + "chassis-sub-module" + ] + modules.append(mod) + + self.facts["modules"] = modules + + +class Interfaces(FactsBase): + def populate(self): + ele = Element("get-interface-information") + SubElement(ele, "detail") + reply = exec_rpc(self.module, tostring(ele)) + + interfaces = {} + + for item in reply[0]: + name = self.get_text(item, "name") + obj = { + "oper-status": self.get_text(item, "oper-status"), + "admin-status": self.get_text(item, "admin-status"), + "speed": self.get_text(item, "speed"), + "macaddress": self.get_text(item, "hardware-physical-address"), + "mtu": self.get_text(item, "mtu"), + "type": self.get_text(item, "if-type"), + } + + interfaces[name] = obj + + self.facts["interfaces"] = interfaces + + +class OFacts(FactsBase): + def populate(self): + + device = get_device(self.module) + facts = dict(device.facts) + + if "2RE" in facts: + facts["has_2RE"] = facts["2RE"] + del facts["2RE"] + + facts["version_info"] = dict(facts["version_info"]) + if "junos_info" in facts: + for key, value in facts["junos_info"].items(): + if "object" in value: + value["object"] = dict(value["object"]) + + return facts diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/lldp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/lldp_global.py new file mode 100644 index 000000000..df82a2695 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_global/lldp_global.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 junos 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.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class Lldp_globalFacts(object): + """The junos 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 interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <lldp> + </lldp> + </protocols> + </configuration> + """ + data = get_resource_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + facts = {} + config = deepcopy(self.generated_spec) + resources = data.xpath("configuration/protocols/lldp") + if resources: + lldp_root = resources[0] + config["address"] = utils.get_xml_conf_arg( + lldp_root, + "management-address", + ) + config["interval"] = utils.get_xml_conf_arg( + lldp_root, + "advertisement-interval", + ) + config["transmit_delay"] = utils.get_xml_conf_arg( + lldp_root, + "transmit-delay", + ) + config["hold_multiplier"] = utils.get_xml_conf_arg( + lldp_root, + "hold-multiplier", + ) + if utils.get_xml_conf_arg(lldp_root, "disable", data="tag"): + config["enable"] = False + + params = utils.validate_config( + self.argument_spec, + {"config": utils.remove_empties(config)}, + ) + + facts["lldp_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/lldp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..a5d1af467 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,115 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos lldp_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class Lldp_interfacesFacts(object): + """The junos 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 interfaces + :param connection: the device connection + :param data: previously collected configuration as lxml ElementTree root instance + or valid xml sting + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <lldp/> + </protocols> + </configuration> + """ + data = get_resource_config(connection, config_filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + self._resources = data.xpath("configuration/protocols/lldp/interface") + + objs = [] + for resource in self._resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + if obj: + objs.append(obj) + facts = {} + if objs: + facts["lldp_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["lldp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + :param spec: The facts tree, generated from the argspec + :param conf: The ElementTree instance of configuration object + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config["name"] = utils.get_xml_conf_arg(conf, "name") + if utils.get_xml_conf_arg(conf, "disable", data="tag"): + config["enabled"] = False + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/logging_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/logging_global.py new file mode 100644 index 000000000..2affbde19 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/logging_global/logging_global.py @@ -0,0 +1,428 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos logging_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.logging_global.logging_global import ( + Logging_globalArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Logging_globalFacts(object): + """The junos logging_global fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Logging_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for logging_gloabl + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <system> + <syslog> + </syslog> + </system> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/system/syslog") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["logging_global"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["logging_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + logging_gloabl_config = {} + + # Parse facts for BGP address-family global node + conf = conf.get("syslog") + + # Read allow-duplicates node + if "allow-duplicates" in conf.keys(): + logging_gloabl_config["allow_duplicates"] = True + + # Read archive node + if "archive" in conf.keys(): + archive_dict = self.parse_archive_node(conf.get("archive")) + logging_gloabl_config["archive"] = archive_dict + + # Read console node + if "console" in conf.keys(): + console_dict = self.parse_console_node(conf.get("console")) + logging_gloabl_config["console"] = console_dict + + # Read file node + if "file" in conf.keys(): + files_list = self.parse_file_node(conf.get("file")) + logging_gloabl_config["files"] = files_list + + # Read host node + if "host" in conf.keys(): + hosts_list = self.parse_host_node(conf.get("host")) + logging_gloabl_config["hosts"] = hosts_list + + # Read log-rotate-frequency node + if "log-rotate-frequency" in conf.keys(): + logging_gloabl_config["log_rotate_frequency"] = conf.get( + "log-rotate-frequency", + ) + + # Read routing-instance node + if "routing-instance" in conf.keys(): + logging_gloabl_config["routing_instance"] = conf.get( + "routing-instance", + ) + + # Read source-address node + if "source-address" in conf.keys(): + logging_gloabl_config["source_address"] = conf.get( + "source-address", + ) + + # Read user node + if "user" in conf.keys(): + users_list = self.parse_user_node(conf.get("user")) + logging_gloabl_config["users"] = users_list + + # Read time-format + if "time-format" in conf.keys(): + time_format_dict = {} + time_format = conf.get("time-format") + if time_format is None: + time_format_dict["set"] = True + else: + if "millisecond" in time_format.keys(): + time_format_dict["millisecond"] = True + if "year" in time_format.keys(): + time_format_dict["year"] = True + logging_gloabl_config["time_format"] = time_format_dict + + return utils.remove_empties(logging_gloabl_config) + + def parse_archive_node(self, conf): + archive_dict = {} + if conf is not None: + # Read child attributes + if "binary-data" in conf.keys(): + archive_dict["binary_data"] = True + if "files" in conf.keys(): + archive_dict["files"] = conf.get("files") + if "no-binary-data" in conf.keys(): + archive_dict["no_binary_data"] = True + if "no-world-readable" in conf.keys(): + archive_dict["no_world_readable"] = True + if "size" in conf.keys(): + archive_dict["file_size"] = conf.get("size") + if "world-readable" in conf.keys(): + archive_dict["world_readable"] = True + if "archive-sites" in conf.keys(): + archive_sites_list = [] + archive_sites = conf.get("archive-sites") + if isinstance(archive_sites, list): + for item in archive_sites: + archive_sites_list.append(item["name"]) + else: + archive_sites_list.append(archive_sites["name"]) + archive_dict["archive_sites"] = archive_sites_list + else: + archive_dict["set"] = True + + return archive_dict + + def parse_console_node(self, conf, console_dict=None): + console_loggings = [ + "any", + "authorization", + "change-log", + "conflict-log", + "daemon", + "dfc", + "external", + "firewall", + "ftp", + "interactive-commands", + "kernel", + "ntp", + "pfe", + "security", + "user", + ] + if console_dict is None: + console_dict = {} + # Read any node + if isinstance(conf, dict): + for item in console_loggings: + if item in conf.get("name"): + any_dict = {} + for k, v in conf.items(): + if k != "name": + any_dict["level"] = k + console_dict[item.replace("-", "_")] = any_dict + else: + for console in conf: + for item in console_loggings: + if item in console.get("name"): + any_dict = {} + for k, v in console.items(): + if k != "name": + any_dict["level"] = k + console_dict[item.replace("-", "_")] = any_dict + + return console_dict + + def parse_file_node(self, conf): + files_list = [] + files = [] + if isinstance(conf, dict): + files.append(conf) + else: + files = conf + for file in files: + file_dict = {} + # Read file name node + file_dict["name"] = file.get("name") + # Read allow-duplicates node + if "allow-duplicates" in file: + file_dict["allow_duplicates"] = True + # Read contents + if "contents" in file.keys(): + contents = file.get("contents") + if isinstance(contents, list): + for content in contents: + file_dict = self.parse_console_node(content, file_dict) + else: + file_dict = self.parse_console_node(contents, file_dict) + # Read archives + if "archive" in file.keys(): + archive_dict = self.parse_archive_node(file.get("archive")) + file_dict["archive"] = archive_dict + # Read explicit priority + if "explicit-priority" in file.keys(): + file_dict["explicit_priority"] = True + # Read match + if "match" in file.keys(): + file_dict["match"] = file.get("match") + # Read match-strings + if "match-strings" in file.keys(): + match_strings = file.get("match-strings") + match_strings_list = [] + if isinstance(match_strings, list): + for item in match_strings: + match_strings_list.append(item) + else: + match_strings_list.append(match_strings) + file_dict["match_strings"] = match_strings_list + # Read srtructured-data + if "structured-data" in file.keys(): + structured_data_dict = {} + if file.get("structured-data"): + structured_data_dict["brief"] = True + else: + structured_data_dict["set"] = True + file_dict["structured_data"] = structured_data_dict + files_list.append(file_dict) + return files_list + + def parse_host_node(self, conf): + hosts_list = [] + hosts = [] + if isinstance(conf, dict): + hosts.append(conf) + else: + hosts = conf + for host in hosts: + host_dict = {} + # Read file name node + host_dict["name"] = host.get("name") + # Read allow-duplicates node + if "allow-duplicates" in host: + host_dict["allow_duplicates"] = True + # Read contents + if "contents" in host.keys(): + contents = host.get("contents") + if isinstance(contents, list): + for content in contents: + host_dict = self.parse_console_node(content, host_dict) + else: + host_dict = self.parse_console_node(contents, host_dict) + # Read exclude-hostname node + if "exclude-hostname" in host.keys(): + host_dict["exclude_hostname"] = True + # Read facility-override node + if "facility-override" in host.keys(): + host_dict["facility_override"] = host.get("facility-override") + # Read log-prefix node + if "log-prefix" in host.keys(): + host_dict["log_prefix"] = host.get("log-prefix") + # Read match + if "match" in host.keys(): + host_dict["match"] = host.get("match") + # Read match-strings + if "match-strings" in host.keys(): + match_strings = host.get("match-strings") + match_strings_list = [] + if isinstance(match_strings, list): + for item in match_strings: + match_strings_list.append(item) + else: + match_strings_list.append(match_strings) + host_dict["match_strings"] = match_strings_list + # Read port node + if "port" in host.keys(): + host_dict["port"] = host.get("port") + # Read routing-instance node + if "routing-instance" in host.keys(): + host_dict["routing_instance"] = host.get("routing-instance") + # Read source-address node + if "source-address" in host.keys(): + host_dict["source_address"] = host.get("source-address") + # Read srtructured-data + if "structured-data" in host.keys(): + structured_data_dict = {} + if host.get("structured-data"): + structured_data_dict["brief"] = True + else: + structured_data_dict["set"] = True + host_dict["structured_data"] = structured_data_dict + + hosts_list.append(host_dict) + + return hosts_list + + def parse_user_node(self, conf): + users_list = [] + users = [] + if isinstance(conf, dict): + users.append(conf) + else: + users = conf + for user in users: + user_dict = {} + # Read file name node + user_dict["name"] = user.get("name") + # Read allow-duplicates node + if "allow-duplicates" in user: + user_dict["allow_duplicates"] = True + # Read contents + if "contents" in user.keys(): + contents = user.get("contents") + if isinstance(contents, list): + for content in contents: + user_dict = self.parse_console_node(content, user_dict) + else: + user_dict = self.parse_console_node(contents, user_dict) + # Read match + if "match" in user.keys(): + user_dict["match"] = user.get("match") + # Read match-strings + if "match-strings" in user.keys(): + match_strings = user.get("match-strings") + match_strings_list = [] + if isinstance(match_strings, list): + for item in match_strings: + match_strings_list.append(item) + else: + match_strings_list.append(match_strings) + user_dict["match_strings"] = match_strings_list + + users_list.append(user_dict) + + return users_list diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/ntp_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/ntp_global.py new file mode 100644 index 000000000..a7dc37b57 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ntp_global/ntp_global.py @@ -0,0 +1,320 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos ntp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ntp_global.ntp_global import ( + Ntp_globalArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Ntp_globalFacts(object): + """The junos ntp_global fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ntp_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ntp_gloabl + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <system> + <ntp> + </ntp> + </system> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/system/ntp") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["ntp_global"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["ntp_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + ntp_global_config = {} + + # Parse facts for BGP address-family global node + conf = conf.get("ntp") + + # Read allow-duplicates node + if "authentication-key" in conf.keys(): + auth_key_lst = [] + auth_keys = conf.get("authentication-key") + auth_key_dict = {} + if isinstance(auth_keys, dict): + auth_key_dict["id"] = auth_keys["name"] + auth_key_dict["algorithm"] = auth_keys["type"] + auth_key_dict["key"] = auth_keys["value"] + auth_key_lst.append(auth_key_dict) + + else: + for auth_key in auth_keys: + auth_key_dict["id"] = auth_key["name"] + auth_key_dict["algorithm"] = auth_key["type"] + auth_key_dict["key"] = auth_key["value"] + auth_key_lst.append(auth_key_dict) + auth_key_dict = {} + if auth_key_lst: + ntp_global_config["authentication_keys"] = auth_key_lst + + # Read boot-server node + if "boot-server" in conf.keys(): + ntp_global_config["boot_server"] = conf.get("boot-server") + + # Read broadcast node + if "broadcast" in conf.keys(): + broadcast_lst = [] + broadcasts = conf.get("broadcast") + broadcast_dict = {} + if isinstance(broadcasts, dict): + broadcast_dict["address"] = broadcasts["name"] + if "key" in broadcasts.keys(): + broadcast_dict["key"] = broadcasts["key"] + if "ttl" in broadcasts.keys(): + broadcast_dict["ttl"] = broadcasts["ttl"] + if "version" in broadcasts.keys(): + broadcast_dict["version"] = broadcasts["version"] + if "routing-instance-name" in broadcasts.keys(): + broadcast_dict["routing_instance_name"] = broadcasts["routing-instance-name"] + broadcast_lst.append(broadcast_dict) + + else: + for broadcast in broadcasts: + broadcast_dict["address"] = broadcast["name"] + if "key" in broadcast.keys(): + broadcast_dict["key"] = broadcast["key"] + if "ttl" in broadcast.keys(): + broadcast_dict["ttl"] = broadcast["ttl"] + if "version" in broadcast.keys(): + broadcast_dict["version"] = broadcast["version"] + if "routing-instance-name" in broadcast.keys(): + broadcast_dict["routing_instance_name"] = broadcast["routing-instance-name"] + broadcast_lst.append(broadcast_dict) + broadcast_dict = {} + if broadcast_lst: + ntp_global_config["broadcasts"] = broadcast_lst + + # Read broadcast-client node + if "broadcast-client" in conf.keys(): + ntp_global_config["broadcast_client"] = True + + # Read interval-range node + if "interval-range" in conf.keys(): + ntp_global_config["interval_range"] = conf["interval-range"].get( + "value", + ) + + # Read multicast-client node + if "multicast-client" in conf.keys(): + ntp_global_config["multicast_client"] = conf["multicast-client"].get("address") + + # Read peer node + if "peer" in conf.keys(): + peer_lst = [] + peers = conf.get("peer") + peer_dict = {} + if isinstance(peers, dict): + peer_dict["peer"] = peers["name"] + if "key" in peers.keys(): + peer_dict["key_id"] = peers["key"] + if "prefer" in peers.keys(): + peer_dict["prefer"] = True + if "version" in peers.keys(): + peer_dict["version"] = peers["version"] + peer_lst.append(peer_dict) + + else: + for peer in peers: + peer_dict["peer"] = peer["name"] + if "key" in peer.keys(): + peer_dict["key_id"] = peer["key"] + if "prefer" in peer.keys(): + peer_dict["prefer"] = True + if "version" in peer.keys(): + peer_dict["version"] = peer["version"] + peer_lst.append(peer_dict) + peer_dict = {} + if peer_lst: + ntp_global_config["peers"] = peer_lst + + # Read server node + if "server" in conf.keys(): + server_lst = [] + servers = conf.get("server") + server_dict = {} + if isinstance(servers, dict): + server_dict["server"] = servers["name"] + if "key" in servers.keys(): + server_dict["key_id"] = servers["key"] + if "prefer" in servers.keys(): + server_dict["prefer"] = True + if "version" in servers.keys(): + server_dict["version"] = servers["version"] + if "routing-instance" in servers.keys(): + server_dict["routing-instance"] = servers["routing-instance"] + server_lst.append(server_dict) + + else: + for server in servers: + server_dict["server"] = server["name"] + if "key" in server.keys(): + server_dict["key_id"] = server["key"] + if "prefer" in server.keys(): + server_dict["prefer"] = True + if "version" in server.keys(): + server_dict["version"] = server["version"] + if "routing-instance" in server.keys(): + server_dict["routing_instance"] = server["routing-instance"] + server_lst.append(server_dict) + server_dict = {} + if server_lst: + ntp_global_config["servers"] = server_lst + + # Read source-address node + if "source-address" in conf.keys(): + source_address_lst = [] + source_addresses = conf.get("source-address") + source_address_dict = {} + if isinstance(source_addresses, dict): + source_address_dict["source_address"] = source_addresses["name"] + if "routing-instance" in source_addresses.keys(): + source_address_dict["routing_instance"] = source_addresses["routing-instance"] + source_address_lst.append(source_address_dict) + + else: + for source_address in source_addresses: + source_address_dict["source_address"] = source_address["name"] + if "routing-instance" in source_address.keys(): + source_address_dict["routing_instance"] = source_address["routing-instance"] + source_address_lst.append(source_address_dict) + source_address_dict = {} + if source_address_lst: + ntp_global_config["source_addresses"] = source_address_lst + + # Read threshold node + if "threshold" in conf.keys(): + threshold = conf.get("threshold") + threshold_dict = {} + if "value" in threshold.keys(): + threshold_dict["value"] = threshold.get("value") + if "action" in threshold.keys(): + threshold_dict["action"] = threshold.get("action") + if threshold_dict: + ntp_global_config["threshold"] = threshold_dict + + # read trusted-keys node + if "trusted-key" in conf.keys(): + trusted_keys = conf.get("trusted-key") + trusted_keys_lst = [] + trusted_keys_dict = {} + if isinstance(trusted_keys, list): + trusted_keys.sort(key=int) + for key in trusted_keys: + trusted_keys_dict["key_id"] = key + trusted_keys_lst.append(trusted_keys_dict) + trusted_keys_dict = {} + ntp_global_config["trusted_keys"] = trusted_keys_lst + else: + trusted_keys_dict["key_id"] = trusted_keys + trusted_keys_lst.append(trusted_keys_dict) + ntp_global_config["trusted_keys"] = trusted_keys_lst + return utils.remove_empties(ntp_global_config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/ospf_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..15319fa32 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,267 @@ +# +# -*- 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 junos 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 __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + generate_dict, + remove_empties, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + _validate_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Ospf_interfacesFacts(object): + """The junos ospf_interfaces fact 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 = generate_dict(facts_argument_spec) + self.router_id = "" + + def get_connection(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ospf_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <ospf/> + </protocols> + <routing-options> + <router-id/> + </routing-options> + </configuration> + """ + data = self.get_connection(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/protocols/ospf") + router_id_path = data.xpath("configuration/routing-options/router-id") + + if router_id_path: + self.router_id = self._get_xml_dict(router_id_path.pop()) + else: + self.router_id = "" + + objs = [] + for resource in resources: + if resource: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["junos_ospf_interfaces"] = [] + params = _validate_config( + self._module, + self.argument_spec, + {"config": objs}, + redact=True, + ) + + for cfg in params["config"]: + facts["junos_ospf_interfaces"].append(remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + ospf_interfaces_config = [] + ospf = conf.get("ospf") + + if ospf.get("area"): + areas = ospf.get("area") + + if not isinstance(areas, list): + areas = [areas] + + for area in areas: + rendered_area = {} + rendered_area["area_id"] = area.get("name") + rendered_area["interfaces"] = [] + + interfaces = area["interface"] + if not isinstance(interfaces, list): + interfaces = [interfaces] + for interface in interfaces: + interface_dict = {} + interface_dict["priority"] = interface.get("priority") + interface_dict["metric"] = interface.get("metric") + interface_dict["mtu"] = interface.get("mtu") + interface_dict["te_metric"] = interface.get("te-metric") + interface_dict["ipsec_sa"] = interface.get("ipsec-sa") + interface_dict["hello_interval"] = interface.get( + "hello-interval", + ) + interface_dict["dead_interval"] = interface.get( + "dead-interval", + ) + interface_dict["retransmit_interval"] = interface.get( + "retransmit-interval", + ) + interface_dict["transit_delay"] = interface.get( + "transit-delay", + ) + interface_dict["poll_interval"] = interface.get( + "poll-interval", + ) + if "passive" in interface.keys(): + interface_dict["passive"] = True + if "flood-reduction" in interface.keys(): + interface_dict["flood_reduction"] = True + if "demand-circuit" in interface.keys(): + interface_dict["demand_circuit"] = True + if "no-advertise-adjacency-segment" in interface.keys(): + interface_dict["no_advertise_adjacency_segment"] = True + if "no-eligible-backup" in interface.keys(): + interface_dict["no_eligible_backup"] = True + if "no-eligible-remote-backup" in interface.keys(): + interface_dict["no_eligible_remote_backup"] = True + if "no-interface-state-traps" in interface.keys(): + interface_dict["no_interface_state_traps"] = True + if "no-neighbor-down-notification" in interface.keys(): + interface_dict["no_neighbor_down_notification"] = True + if "node-link-protection" in interface.keys(): + interface_dict["node_link_protection"] = True + if "bandwidth-based-metrics" in interface.keys(): + bandwidth_metrics = interface["bandwidth-based-metrics"].get("bandwidth") + if not isinstance(bandwidth_metrics, list): + bandwidth_metrics = [bandwidth_metrics] + interface_dict["bandwidth_based_metrics"] = [] + + for metric in bandwidth_metrics: + interface_dict["bandwidth_based_metrics"].append( + { + "metric": metric.get("metric"), + "bandwidth": metric.get("name"), + }, + ) + + if "authentication" in interface.keys(): + auth = interface["authentication"] + auth_dict = {} + if auth.get("simple-password"): + auth_dict["simple_password"] = auth.get( + "simple-password", + ) + elif auth.get("md5"): + auth_dict["type"] = {"md5": []} + md5_list = auth.get("md5") + + if not isinstance(md5_list, list): + md5_list = [md5_list] + + for md5_auth in md5_list: + auth_dict["type"]["md5"].append( + { + "key_id": md5_auth.get("name"), + "key": md5_auth.get("key"), + }, + ) + interface_dict["authentication"] = auth_dict + + rendered_area["interfaces"].append(interface_dict) + + af = {} + conf = {} + areas = {} + address_family = [] + af["afi"] = "ipv4" + areas["area_id"] = rendered_area["area_id"] + interface_dict["area"] = areas + af["processes"] = interface_dict + address_family.append(af) + conf["address_family"] = address_family + conf["name"] = interface.get("name") + if self.router_id: + conf["router_id"] = self.router_id["router-id"] + remove_empties(conf) + ospf_interfaces_config.append(conf) + + return ospf_interfaces_config diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/ospfv2.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/ospfv2.py new file mode 100644 index 000000000..24c8fa86d --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv2/ospfv2.py @@ -0,0 +1,286 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# + + +""" +The junos_ospfv2 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Ospfv2Facts(object): + """The junos ospf 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) + self.router_id = "" + + def get_connection(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ospf + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <ospf/> + </protocols> + <routing-options> + <router-id/> + </routing-options> + </configuration> + """ + data = self.get_connection(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/protocols/ospf") + router_id_path = data.xpath("configuration/routing-options/router-id") + if router_id_path: + self.router_id = self._get_xml_dict(router_id_path.pop()) + else: + self.router_id = "" + objs = [] + for resource in resources: + if resource: + xml = self._get_xml_dict(resource) + obj = self.render_config(self.generated_spec, xml) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["ospfv2"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + for cfg in params["config"]: + facts["ospfv2"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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) + ospf = conf.get("ospf") + + if ospf.get("area"): + rendered_areas = [] + areas = ospf.get("area") + + if not isinstance(areas, list): + areas = [areas] + + for area in areas: + rendered_area = {} + rendered_area["area_id"] = area.get("name") + rendered_area["interfaces"] = [] + + interfaces = area["interface"] + if not isinstance(interfaces, list): + interfaces = [interfaces] + + for interface in interfaces: + interface_dict = {} + interface_dict["name"] = interface.get("name") + interface_dict["priority"] = interface.get("priority") + interface_dict["metric"] = interface.get("metric") + interface_dict["timers"] = {} + interface_dict["timers"]["hello_interval"] = interface.get( + "hello-interval", + ) + interface_dict["timers"]["dead_interval"] = interface.get( + "dead-interval", + ) + interface_dict["timers"]["retransmit_interval"] = interface.get( + "retransmit-interval", + ) + interface_dict["timers"]["transit_delay"] = interface.get( + "transit-delay", + ) + interface_dict["timers"]["poll_interval"] = interface.get( + "poll-interval", + ) + if "passive" in interface.keys(): + interface_dict["passive"] = True + if "flood-reduction" in interface.keys(): + interface_dict["flood_reduction"] = True + if "bandwidth-based-metrics" in interface.keys(): + bandwidth_metrics = interface["bandwidth-based-metrics"].get("bandwidth") + if not isinstance(bandwidth_metrics, list): + bandwidth_metrics = [bandwidth_metrics] + interface_dict["bandwidth_based_metrics"] = [] + + for metric in bandwidth_metrics: + interface_dict["bandwidth_based_metrics"].append( + { + "metric": metric.get("metric"), + "bandwidth": metric.get("name"), + }, + ) + + if "authentication" in interface.keys(): + auth = interface["authentication"] + auth_dict = {} + if auth.get("simple-password"): + auth_dict["type"] = "simple_password" + auth_dict["password"] = auth.get("simple-password") + elif auth.get("md5"): + auth_dict["type"] = {"md5": []} + md5_list = auth.get("md5") + + if not isinstance(md5_list, list): + md5_list = [md5_list] + + for md5_auth in md5_list: + auth_dict["type"]["md5"].append( + { + "key_id": md5_auth.get("name"), + "key": md5_auth.get("key"), + }, + ) + interface_dict["authentication"] = auth_dict + + rendered_area["interfaces"].append(interface_dict) + + if area.get("area-range"): + area_range = area["area-range"] + if not isinstance(area_range, list): + area_range = [area_range] + rendered_area["area_range"] = [] + for a_range in area_range: + rendered_area["area_range"].append(a_range["name"]) + + if area.get("stub"): + rendered_area["stub"] = {"set": True} + if "no-summaries" in area.get("stub").keys(): + rendered_area["stub"]["no_summary"] = True + if "default-metric" in area.get("stub").keys(): + rendered_area["stub"]["default_metric"] = area["stub"].get("default-metric") + if area.get("nssa"): + rendered_area["nssa"] = {"set": True} + if "no-summaries" in area.get("nssa").keys(): + rendered_area["nssa"]["no_summary"] = True + elif "summaries" in area.get("nssa").keys(): + rendered_area["nssa"]["no_summary"] = False + if "default-lsa" in area.get("nssa").keys(): + rendered_area["nssa"]["default-lsa"] = True + rendered_areas.append(rendered_area) + + if "no-rfc-1583" in ospf.keys(): + config["rfc1583compatibility"] = False + if ospf.get("spf-options"): + config["spf_options"] = {} + config["spf_options"]["delay"] = ospf["spf-options"].get( + "delay", + ) + config["spf_options"]["holddown"] = ospf["spf-options"].get( + "holddown", + ) + config["spf_options"]["rapid_runs"] = ospf["spf-options"].get( + "rapid-runs", + ) + config["overload"] = ospf.get("overload") + config["preference"] = ospf.get("preference") + config["external_preference"] = ospf.get("external-preference") + config["prefix_export_limit"] = ospf.get("prefix-export-limit") + config["reference_bandwidth"] = ospf.get("reference-bandwidth") + config["areas"] = rendered_areas + if self.router_id != "": + config["router_id"] = self.router_id["router-id"] + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/ospfv3.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/ospfv3.py new file mode 100644 index 000000000..2f95e0e8d --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/ospfv3/ospfv3.py @@ -0,0 +1,285 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# + + +""" +The junos_ospfv3 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Ospfv3Facts(object): + """The junos ospfv3 fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospfv3Args.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + self.router_id = "" + + def get_connection(self, connection, config_filter): + """ + + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ospfv3 + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <protocols> + <ospf3/> + </protocols> + <routing-options> + <router-id/> + </routing-options> + </configuration> + """ + data = self.get_connection(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/protocols/ospf3") + router_id_path = data.xpath("configuration/routing-options/router-id") + if router_id_path: + self.router_id = self._get_xml_dict(router_id_path.pop()) + else: + self.router_id = "" + + objs = [] + for resource in resources: + if resource: + xml = self._get_xml_dict(resource) + obj = self.render_config(self.generated_spec, xml) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["junos_ospfv3"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + for cfg in params["config"]: + facts["junos_ospfv3"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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) + ospfv3 = conf.get("ospf3") + + if ospfv3.get("area"): + rendered_areas = [] + areas = ospfv3.get("area") + + if not isinstance(areas, list): + areas = [areas] + + for area in areas: + rendered_area = {} + rendered_area["area_id"] = area.get("name") + rendered_area["interfaces"] = [] + + interfaces = area["interface"] + if not isinstance(interfaces, list): + interfaces = [interfaces] + + for interface in interfaces: + interface_dict = {} + interface_dict["name"] = interface.get("name") + interface_dict["priority"] = interface.get("priority") + interface_dict["metric"] = interface.get("metric") + interface_dict["timers"] = {} + interface_dict["timers"]["hello_interval"] = interface.get( + "hello-interval", + ) + interface_dict["timers"]["dead_interval"] = interface.get( + "dead-interval", + ) + interface_dict["timers"]["retransmit_interval"] = interface.get( + "retransmit-interval", + ) + interface_dict["timers"]["transit_delay"] = interface.get( + "transit-delay", + ) + interface_dict["timers"]["poll_interval"] = interface.get( + "poll-interval", + ) + if "passive" in interface.keys(): + interface_dict["passive"] = True + if "flood-reduction" in interface.keys(): + interface_dict["flood_reduction"] = True + if "bandwidth-based-metrics" in interface.keys(): + bandwidth_metrics = interface["bandwidth-based-metrics"].get("bandwidth") + if not isinstance(bandwidth_metrics, list): + bandwidth_metrics = [bandwidth_metrics] + interface_dict["bandwidth_based_metrics"] = [] + + for metric in bandwidth_metrics: + interface_dict["bandwidth_based_metrics"].append( + { + "metric": metric.get("metric"), + "bandwidth": metric.get("name"), + }, + ) + + if "authentication" in interface.keys(): + auth = interface["authentication"] + auth_dict = {} + if auth.get("simple-password"): + auth_dict["type"] = "simple_password" + auth_dict["password"] = auth.get("simple-password") + elif auth.get("md5"): + auth_dict["type"] = {"md5": []} + md5_list = auth.get("md5") + + if not isinstance(md5_list, list): + md5_list = [md5_list] + + for md5_auth in md5_list: + auth_dict["type"]["md5"].append( + { + "key_id": md5_auth.get("name"), + "key": md5_auth.get("key"), + }, + ) + interface_dict["authentication"] = auth_dict + + rendered_area["interfaces"].append(interface_dict) + + if area.get("area-range"): + area_range = area["area-range"] + if not isinstance(area_range, list): + area_range = [area_range] + rendered_area["area_range"] = [] + for a_range in area_range: + rendered_area["area_range"].append(a_range["name"]) + + if area.get("stub"): + rendered_area["stub"] = {"set": True} + if "no-summaries" in area.get("stub").keys(): + rendered_area["stub"]["no_summary"] = True + if "default-metric" in area.get("stub").keys(): + rendered_area["stub"]["default_metric"] = area["stub"].get("default-metric") + if area.get("nssa"): + rendered_area["nssa"] = {"set": True} + if "no-summaries" in area.get("nssa").keys(): + rendered_area["nssa"]["no_summary"] = True + elif "summaries" in area.get("nssa").keys(): + rendered_area["nssa"]["no_summary"] = False + if "default-lsa" in area.get("nssa").keys(): + rendered_area["nssa"]["default-lsa"] = True + rendered_areas.append(rendered_area) + + if "no-rfc-1583" in ospfv3.keys(): + config["rfc1583compatibility"] = False + if ospfv3.get("spf-options"): + config["spf_options"] = {} + config["spf_options"]["delay"] = ospfv3["spf-options"].get( + "delay", + ) + config["spf_options"]["holddown"] = ospfv3["spf-options"].get( + "holddown", + ) + config["spf_options"]["rapid_runs"] = ospfv3["spf-options"].get("rapid-runs") + config["overload"] = ospfv3.get("overload") + config["preference"] = ospfv3.get("preference") + config["external_preference"] = ospfv3.get("external-preference") + config["prefix_export_limit"] = ospfv3.get("prefix-export-limit") + config["reference_bandwidth"] = ospfv3.get("reference-bandwidth") + config["areas"] = rendered_areas + if self.router_id: + config["router_id"] = self.router_id["router-id"] + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/prefix_lists.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..4d3ec81c0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/prefix_lists/prefix_lists.py @@ -0,0 +1,168 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos prefix_lists fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.prefix_lists.prefix_lists import ( + Prefix_listsArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Prefix_listsFacts(object): + """The junos prefix_lists fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Prefix_listsArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for bgp_address_family + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + if not HAS_XMLTODICT: + self._module.fail_json(msg="xmltodict is not installed.") + if not data: + config_filter = """ + <configuration> + <policy-options> + <prefix-list> + </prefix-list> + </policy-options> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/policy-options") + + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["prefix_lists"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + for cfg in params["config"]: + facts["prefix_lists"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + prefix_lists_config = [] + pl_dict = {} + pl_cfg = [] + # Parse facts for routing instances node + prefix_lists = conf["policy-options"].get("prefix-list") + if isinstance(prefix_lists, dict): + pl_cfg.append(prefix_lists) + else: + pl_cfg = prefix_lists + for cfg in pl_cfg: + # Parse attribute name + if cfg.get("name"): + pl_dict["name"] = cfg.get("name") + + # Parse attribute dynamic-db + if "dynamic-db" in cfg.keys(): + pl_dict["dynamic_db"] = True + + # Parse attribute address-prefix + if cfg.get("prefix-list-item"): + addr_prefix = cfg.get("prefix-list-item") + addr_pre_list = [] + if isinstance(addr_prefix, dict): + addr_pre_list.append(addr_prefix["name"]) + else: + for prefix in addr_prefix: + addr_pre_list.append(prefix["name"]) + pl_dict["address_prefixes"] = addr_pre_list + + if pl_dict: + prefix_lists_config.append(pl_dict) + pl_dict = {} + + return prefix_lists_config diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/routing_instances.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/routing_instances.py new file mode 100644 index 000000000..d571e4e53 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_instances/routing_instances.py @@ -0,0 +1,248 @@ +# +# -*- 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 junos routing_instances fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.routing_instances.routing_instances import ( + Routing_instancesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Routing_instancesFacts(object): + """The junos routing_instances fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Routing_instancesArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for bgp_address_family + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + if not HAS_XMLTODICT: + self._module.fail_json(msg="xmltodict is not installed.") + if not data: + config_filter = """ + <configuration> + <routing-instances/> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/routing-instances") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["routing_instances"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + for cfg in params["config"]: + facts["routing_instances"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + routing_instances_config = [] + + # Parse facts for routing instances node + conf = conf.get("routing-instances") + + # Parse routing instances + routing_instances = conf.get("instance") + if isinstance(routing_instances, list): + for instance in routing_instances: + instance_dict = self.parse_instance(instance) + routing_instances_config.append(instance_dict) + else: + instance_dict = self.parse_instance(routing_instances) + routing_instances_config.append(instance_dict) + + return routing_instances_config + + def parse_instance(self, instance): + """ + + :param instance: + :return: + """ + instance_dict = {} + # read instance name + instance_dict["name"] = instance["name"] + + # read connection-id-advertise + if "connector-id-advertise" in instance.keys(): + instance_dict["connector_id_advertise"] = True + + # read description + if instance.get("description"): + instance_dict["description"] = instance["description"] + + # read instance role + if instance.get("instance-role"): + instance_dict["instance_role"] = instance["instance-role"] + + # read instance type + if instance.get("instance-type"): + instance_dict["type"] = instance["instance-type"] + + # read interfaces + if instance.get("interface"): + interfaces = instance.get("interface") + interfaces_list = [] + if isinstance(interfaces, list): + for interface in interfaces: + interfaces_list.append(self.parse_interface(interface)) + else: + interfaces_list.append(self.parse_interface(interfaces)) + instance_dict["interfaces"] = interfaces_list + + # read l2vpn-id + if instance.get("l2vpn-id"): + instance_dict["l2vpn_id"] = instance["l2vpn-id"].get("community") + + # read no-irb-layer2-copy + if "no-irb-layer2-copy" in instance.keys(): + instance_dict["no_irb_layer_2_copy"] = True + + # read no_local_switching + if "no-local-switching" in instance.keys(): + instance_dict["no_local_switching"] = True + + # read no-vrf-advertise + if "no-vrf-advertise" in instance.keys(): + instance_dict["no_vrf_advertise"] = True + + # read no_vrf_propagate_ttl + if "no-vrf-propagate-ttl" in instance.keys(): + instance_dict["no_vrf_propagate_ttl"] = True + + # read qualified_bum_pruning_mode + if instance.get("qualified-bum-pruning-mode"): + instance_dict["qualified_bum_pruning_mode"] = True + + # read route-distinguisher + if instance.get("route-distinguisher"): + instance_dict["route_distinguisher"] = instance["route-distinguisher"].get("rd-type") + + # read vrf imports + if instance.get("vrf-import"): + vrf_imp_lst = [] + vrf_imp = instance.get("vrf-import") + + if isinstance(vrf_imp, list): + vrf_imp_lst = vrf_imp + else: + vrf_imp_lst.append(vrf_imp) + instance_dict["vrf_imports"] = vrf_imp_lst + + # read vrf exports + if instance.get("vrf-export"): + vrf_exp_lst = [] + vrf_exp = instance.get("vrf-export") + if isinstance(vrf_exp, list): + vrf_exp_lst = vrf_exp + else: + vrf_exp_lst.append(vrf_exp) + instance_dict["vrf_exports"] = vrf_exp_lst + + return utils.remove_empties(instance_dict) + + def parse_interface(self, interface): + """ + + :param instance: + :return: + """ + cfg_dict = {} + cfg_dict["name"] = interface["name"] + if interface.get("protect-interface"): + cfg_dict["protect_interface"] = interface.get("protect-interface") + + return cfg_dict diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/routing_options.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/routing_options.py new file mode 100644 index 000000000..78664fea8 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/routing_options/routing_options.py @@ -0,0 +1,150 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos routing_options fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.routing_options.routing_options import ( + Routing_optionsArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Routing_optionsFacts(object): + """The junos routing_options fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Routing_optionsArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for routing_options + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <routing-options> + </routing-options> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/routing-options") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["routing_options"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["routing_options"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + routing_config = {} + conf = conf.get("routing-options") + + # Read autonomous-system + if "autonomous-system" in conf.keys(): + as_num = conf.get("autonomous-system") + as_dict = {} + if "as-number" in as_num.keys(): + as_dict["as_number"] = as_num.get("as-number") + if as_num.get("loops"): + as_dict["loops"] = as_num.get("loops") + if "asdot-notation" in as_num.keys(): + as_dict["asdot_notation"] = True + routing_config["autonomous_system"] = as_dict + + # Read router-id + if "router-id" in conf.keys(): + routing_config["router_id"] = conf.get("router-id") + + return utils.remove_empties(routing_config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/security_policies.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/security_policies.py new file mode 100644 index 000000000..dc6a20878 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies/security_policies.py @@ -0,0 +1,529 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos security_policies fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_policies.security_policies import ( + Security_policiesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Security_policiesFacts(object): + """The junos security_policies fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Security_policiesArgs.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_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def _get_device_data(self, connection, config_filters): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filters) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for security_polices + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <security> + <policies> + </policies> + </security> + </configuration> + """ + data = self._get_device_data(connection, config_filter) + + # split the config into instances of the resource + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/security/policies") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["security_policies"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["security_policies"] = 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 + """ + security_policies_config = {} + + # Parse facts for security policies + conf = conf.get("policies") or {} + + if "policy" in conf: + security_policies_config["from_zones"] = [] + from_zone_dict = {} + zone_pairs = conf.get("policy") + if isinstance(zone_pairs, dict): + temp = zone_pairs + zone_pairs = [] + zone_pairs.append(temp) + for zone_pair_policies in zone_pairs: + + if zone_pair_policies["from-zone-name"] not in from_zone_dict: + from_zone_dict[zone_pair_policies["from-zone-name"]] = {} + from_zone_dict[zone_pair_policies["from-zone-name"]][ + "name" + ] = zone_pair_policies["from-zone-name"] + from_zone_dict[zone_pair_policies["from-zone-name"]]["to_zones"] = {} + + from_zone = from_zone_dict[zone_pair_policies["from-zone-name"]] + + from_zone["to_zones"][zone_pair_policies["to-zone-name"]] = {} + to_zone = from_zone["to_zones"][zone_pair_policies["to-zone-name"]] + + to_zone["name"] = zone_pair_policies["to-zone-name"] + to_zone["policies"] = self.parse_policies( + zone_pair_policies["policy"], + ) + + for from_zone in from_zone_dict.values(): + from_zone["to_zones"] = list(from_zone["to_zones"].values()) + security_policies_config["from_zones"].append(from_zone) + + if "global" in conf: + global_policies = conf.get("global") + global_policies = global_policies.get("policy") + security_policies_config["global"] = {} + security_policies_config["global"]["policies"] = self.parse_policies(global_policies) + + return security_policies_config + + def parse_policies(self, policies): + policy_list = [] + + if isinstance(policies, dict): + temp = policies + policies = [] + policies.append(temp) + + for policy in policies: + tmp_policy = {} + + # parse name of policy + tmp_policy["name"] = policy["name"] + + # parse match criteria of security policy + tmp_policy["match"] = {} + + match = tmp_policy["match"] + policy_match = policy["match"] + + match["source_address"] = {} + if isinstance(policy_match["source-address"], string_types): + policy_match["source-address"] = [ + policy_match["source-address"], + ] + for source_address in policy_match["source-address"]: + if source_address == "any-ipv6": + match["source_address"]["any_ipv6"] = True + elif source_address == "any-ipv4": + match["source_address"]["any_ipv4"] = True + elif source_address == "any": + match["source_address"]["any"] = True + else: + if "addresses" not in match["source_address"]: + match["source_address"]["addresses"] = [] + match["source_address"]["addresses"].append(source_address) + + if "source-address-excluded" in policy_match: + match["source_address_excluded"] = True + + match["destination_address"] = {} + if isinstance(policy_match["destination-address"], string_types): + policy_match["destination-address"] = [ + policy_match["destination-address"], + ] + for destination_address in policy_match["destination-address"]: + if destination_address == "any-ipv6": + match["destination_address"]["any_ipv6"] = True + elif destination_address == "any-ipv4": + match["destination_address"]["any_ipv4"] = True + elif destination_address == "any": + match["destination_address"]["any"] = True + else: + if "addresses" not in match["destination_address"]: + match["destination_address"]["addresses"] = [] + match["destination_address"]["addresses"].append( + destination_address, + ) + + if "destination-address-excluded" in policy_match: + match["destination_address_excluded"] = True + + match["application"] = {} + if policy_match["application"] == "any": + match["application"]["any"] = True + else: + if isinstance(policy_match["application"], string_types): + policy_match["application"] = [policy_match["application"]] + match["application"]["names"] = policy_match["application"] + + if "source-end-user-profile" in policy_match: + match["source_end_user_profile"] = policy_match["source-end-user-profile"][ + "source-end-user-profile-name" + ] + + if "source-identity" in policy_match: + if isinstance(policy_match["source-identity"], string_types): + policy_match["source-identity"] = [ + policy_match["source-identity"], + ] + match["source_identity"] = {} + for source_identity in policy_match["source-identity"]: + if source_identity == "any": + match["source_identity"]["any"] = True + elif "authenticated-user" == source_identity: + match["source_identity"]["authenticated_user"] = True + elif "unauthenticated-user" == source_identity: + match["source_identity"]["unauthenticated_user"] = True + elif "unknown-user" == source_identity: + match["source_identity"]["unknown_user"] = True + else: + if "names" not in match["source_identity"]: + match["source_identity"]["names"] = [] + match["source_identity"]["names"].append( + source_identity, + ) + + if "url-category" in policy_match: + if isinstance(policy_match["url-category"], string_types): + policy_match["url-category"] = [ + policy_match["url-category"], + ] + match["url_category"] = {} + for url_category in policy_match["url-category"]: + if url_category == "any": + match["url_category"]["any"] = True + elif url_category == "none": + match["url_category"]["none"] = True + else: + if "names" not in match["url_category"]: + match["url_category"]["names"] = [] + match["url_category"]["names"].append(url_category) + + if "dynamic-application" in policy_match: + if isinstance( + policy_match["dynamic-application"], + string_types, + ): + policy_match["dynamic-application"] = [ + policy_match["dynamic-application"], + ] + match["dynamic_application"] = {} + for dynamic_application in policy_match["dynamic-application"]: + if dynamic_application == "any": + match["dynamic_application"]["any"] = True + elif dynamic_application == "none": + match["dynamic_application"]["none"] = True + else: + if "names" not in match["dynamic_application"]: + match["dynamic_application"]["names"] = [] + match["dynamic_application"]["names"].append( + dynamic_application, + ) + # end of match criteria parsing + + # parse match action of security policy + tmp_policy["then"] = {} + action = tmp_policy["then"] + policy_action = policy["then"] + + if "count" in policy_action: + action["count"] = True + + if "log" in policy_action: + action["log"] = {} + if "session-close" in policy_action["log"]: + action["log"]["session_close"] = True + if "session-init" in policy_action["log"]: + action["log"]["session_init"] = True + + if "deny" in policy_action: + action["deny"] = True + + if "reject" in policy_action: + action["reject"] = {} + reject = action["reject"] + policy_reject = policy_action["reject"] or {} + reject["enable"] = True + + if "profile" in policy_reject: + reject["profile"] = policy_reject["profile"] + if "ssl-proxy" in policy_reject: + policy_reject["ssl-proxy"] = policy_reject["ssl-proxy"] or {} + reject["ssl_proxy"] = {} + reject["ssl_proxy"]["enable"] = True + if "profile-name" in policy_reject["ssl-proxy"]: + reject["ssl_proxy"]["profile_name"] = policy_reject["ssl-proxy"][ + "profile-name" + ] + + if "permit" in policy_action: + action["permit"] = {} + permit = action["permit"] + policy_permit = policy_action["permit"] or {} + + if "application-services" in policy_permit: + permit["application_services"] = {} + application_services = permit["application_services"] + policy_application_services = policy_permit["application-services"] or {} + + if "advanced-anti-malware-policy" in policy_application_services: + application_services[ + "advanced_anti_malware_policy" + ] = policy_application_services["advanced-anti-malware-policy"] + if "application-traffic-control" in policy_application_services: + application_services[ + "application_traffic_control_rule_set" + ] = policy_application_services["application-traffic-control"]["rule-set"] + if "gprs-gtp-profile" in policy_application_services: + application_services["gprs_gtp_profile"] = policy_application_services[ + "gprs-gtp-profile" + ] + if "gprs-sctp-profile" in policy_application_services: + application_services["gprs_sctp_profile"] = policy_application_services[ + "gprs-sctp-profile" + ] + if "icap-redirect" in policy_application_services: + application_services["icap_redirect"] = policy_application_services[ + "icap-redirect" + ] + if "idp" in policy_application_services: + application_services["idp"] = True + if "idp-policy" in policy_application_services: + application_services["idp_policy"] = policy_application_services[ + "idp-policy" + ] + if "redirect-wx" in policy_application_services: + application_services["redirect_wx"] = True + if "reverse-redirect-wx" in policy_application_services: + application_services["reverse_redirect_wx"] = True + if "security-intelligence-policy" in policy_application_services: + application_services[ + "security_intelligence_policy" + ] = policy_application_services["security-intelligence-policy"] + if "ssl-proxy" in policy_application_services: + application_services["ssl_proxy"] = {} + application_services["ssl_proxy"]["enable"] = True + if ( + policy_application_services["ssl-proxy"] + and "profile-name" in policy_application_services["ssl-proxy"] + ): + application_services["ssl_proxy"][ + "profile_name" + ] = policy_application_services["ssl-proxy"]["profile-name"] + if "uac-policy" in policy_application_services: + application_services["uac_policy"] = {} + application_services["uac_policy"]["enable"] = True + if ( + policy_application_services["uac-policy"] + and "captive-portal" in policy_application_services["uac-policy"] + ): + application_services["uac_policy"][ + "captive_portal" + ] = policy_application_services["uac-policy"]["captive-portal"] + if "utm-policy" in policy_application_services: + application_services["utm_policy"] = policy_application_services[ + "utm-policy" + ] + + if "destination-address" in policy_permit: + permit["destination_address"] = policy_permit["destination-address"] + + if "firewall-authentication" in policy_permit: + permit["firewall_authentication"] = {} + f_a = permit["firewall_authentication"] + policy_f_a = policy_permit["firewall-authentication"] or {} + + if "pass-through" in policy_f_a: + f_a["pass_through"] = {} + if "access-profile" in policy_f_a["pass-through"]: + f_a["pass_through"]["access_profile"] = policy_f_a["pass-through"][ + "access-profile" + ] + if "auth-only-browser" in policy_f_a["pass-through"]: + f_a["pass_through"]["auth_only_browser"] = True + if "auth-user-agent" in policy_f_a["pass-through"]: + f_a["pass_through"]["auth_user_agent"] = policy_f_a["pass-through"][ + "auth-user-agent" + ] + if "client-match" in policy_f_a["pass-through"]: + f_a["pass_through"]["client_match"] = policy_f_a["pass-through"][ + "client-match" + ] + if "ssl-termination-profile" in policy_f_a["pass-through"]: + f_a["pass_through"]["ssl_termination_profile"] = policy_f_a[ + "pass-through" + ]["ssl-termination-profile"] + if "web-redirect" in policy_f_a["pass-through"]: + f_a["pass_through"]["web_redirect"] = True + if "web-redirect-to-https" in policy_f_a["pass-through"]: + f_a["pass_through"]["web_redirect_to_https"] = True + + if "push-to-identity-management" in policy_f_a: + f_a["push_to_identity_management"] = True + + if "user-firewall" in policy_f_a: + f_a["user_firewall"] = {} + if "access-profile" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["access_profile"] = policy_f_a["user-firewall"][ + "access-profile" + ] + if "auth-only-browser" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["auth_only_browser"] = True + if "auth-user-agent" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["auth_user_agent"] = policy_f_a["user-firewall"][ + "auth-user-agent" + ] + if "domain" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["domain"] = policy_f_a["user-firewall"]["domain"] + if "ssl-termination-profile" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["ssl_termination_profile"] = policy_f_a[ + "user-firewall" + ]["ssl-termination-profile"] + if "web-redirect" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["web_redirect"] = True + if "web-redirect-to-https" in policy_f_a["user-firewall"]: + f_a["user_firewall"]["web_redirect_to_https"] = True + + if "web-authentication" in policy_f_a: + f_a["web_authentication"] = [] + if isinstance( + policy_f_a["web-authentication"]["client-match"], + str, + ): + temp = policy_f_a["web-authentication"]["client-match"] + policy_f_a["web-authentication"]["client-match"] = [] + policy_f_a["web-authentication"]["client-match"].append(temp) + f_a["web_authentication"] = policy_f_a["web-authentication"]["client-match"] + + if "tcp-options" in policy_permit: + permit["tcp_options"] = {} + tcp_options = permit["tcp_options"] + policy_tcp_options = policy_permit["tcp-options"] or {} + + if "initial-tcp-mss" in policy_tcp_options: + tcp_options["initial_tcp_mss"] = policy_permit["tcp-options"][ + "initial-tcp-mss" + ] + if "reverse-tcp-mss" in policy_tcp_options: + tcp_options["reverse_tcp_mss"] = policy_permit["tcp-options"][ + "reverse-tcp-mss" + ] + if "sequence-check-required" in policy_tcp_options: + tcp_options["sequence_check_required"] = True + if "syn-check-required" in policy_tcp_options: + tcp_options["syn_check_required"] = True + if "window-scale" in policy_tcp_options: + tcp_options["window_scale"] = True + + if "tunnel" in policy_permit: + permit["tunnel"] = {} + policy_permit["tunnel"] = policy_permit["tunnel"] or {} + if "ipsec-vpn" in policy_permit["tunnel"]: + permit["tunnel"]["ipsec_vpn"] = policy_permit["tunnel"]["ipsec-vpn"] + if "pair-policy" in policy_permit["tunnel"]: + permit["tunnel"]["pair_policy"] = policy_permit["tunnel"]["pair-policy"] + + # end of match action parsing + + # parse description of security policy + if "description" in policy: + tmp_policy["description"] = policy["description"] + + # parse scheduler name of security policy + if "scheduler-name" in policy: + tmp_policy["scheduler_name"] = policy["scheduler-name"] + + test = utils.remove_empties(tmp_policy) + policy_list.append(test) + + return policy_list diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/security_policies_global.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/security_policies_global.py new file mode 100644 index 000000000..692a07b60 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_policies_global/security_policies_global.py @@ -0,0 +1,245 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos security_policies_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_policies_global.security_policies_global import ( + Security_policies_globalArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Security_policies_globalFacts(object): + """The junos security_policies_global fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Security_policies_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def _get_device_data(self, connection, config_filters): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filters) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for security_polices + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <security> + <policies> + </policies> + </security> + </configuration> + """ + data = self._get_device_data(connection, config_filter) + + # split the config into instances of the resource + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/security/policies") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["security_policies_global"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["security_policies_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 + """ + security_policies_global_config = {} + + # Parse facts for security policies global settings + global_policies = conf.get("policies") or {} + + if "default-policy" in global_policies: + if "deny-all" in global_policies["default-policy"]: + security_policies_global_config["default_policy"] = "deny-all" + elif "permit-all" in global_policies["default-policy"]: + security_policies_global_config["default_policy"] = "permit-all" + + if "policy-rematch" in global_policies: + security_policies_global_config["policy_rematch"] = {} + security_policies_global_config["policy_rematch"]["enable"] = True + + global_policies["policy-rematch"] = global_policies["policy-rematch"] or {} + if "extensive" in global_policies["policy-rematch"]: + security_policies_global_config["policy_rematch"]["extensive"] = True + + if "policy-stats" in global_policies: + security_policies_global_config["policy_stats"] = {} + security_policies_global_config["policy_stats"]["enable"] = True + + global_policies["policy-stats"] = global_policies["policy-stats"] or {} + if "system-wide" in global_policies["policy-stats"]: + if global_policies["policy-stats"]["system-wide"] == "enable": + security_policies_global_config["policy_stats"]["system_wide"] = True + elif global_policies["policy-stats"]["system-wide"] == "disable": + security_policies_global_config["policy_stats"]["system_wide"] = False + + if "pre-id-default-policy" in global_policies: + security_policies_global_config["pre_id_default_policy_action"] = {} + pre_id_action = security_policies_global_config["pre_id_default_policy_action"] + policy_pre_id_action = global_policies["pre-id-default-policy"]["then"] + + if "log" in policy_pre_id_action: + pre_id_action["log"] = {} + if "session-close" in policy_pre_id_action["log"]: + pre_id_action["log"]["session_close"] = True + if "session-init" in policy_pre_id_action["log"]: + pre_id_action["log"]["session_init"] = True + + if "session-timeout" in policy_pre_id_action: + pre_id_action["session_timeout"] = {} + if "icmp" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["icmp"] = policy_pre_id_action[ + "session-timeout" + ]["icmp"] + if "icmp6" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["icmp6"] = policy_pre_id_action[ + "session-timeout" + ]["icmp6"] + if "ospf" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["ospf"] = policy_pre_id_action[ + "session-timeout" + ]["ospf"] + if "others" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["others"] = policy_pre_id_action[ + "session-timeout" + ]["others"] + if "tcp" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["tcp"] = policy_pre_id_action[ + "session-timeout" + ]["tcp"] + if "udp" in policy_pre_id_action["session-timeout"]: + pre_id_action["session_timeout"]["udp"] = policy_pre_id_action[ + "session-timeout" + ]["udp"] + + if "traceoptions" in global_policies: + security_policies_global_config["traceoptions"] = {} + traceoptions = security_policies_global_config["traceoptions"] + policy_traceoptions = global_policies["traceoptions"] + + if "file" in policy_traceoptions: + traceoptions["file"] = {} + if "files" in policy_traceoptions["file"]: + traceoptions["file"]["files"] = policy_traceoptions["file"]["files"] + if "match" in policy_traceoptions["file"]: + traceoptions["file"]["match"] = policy_traceoptions["file"]["match"] + if "size" in policy_traceoptions["file"]: + traceoptions["file"]["size"] = policy_traceoptions["file"]["size"] + if "world-readable" in policy_traceoptions["file"]: + traceoptions["file"]["world_readable"] = True + if "no-world-readable" in policy_traceoptions["file"]: + traceoptions["file"]["no_world_readable"] = True + + if "flag" in policy_traceoptions: + traceoptions["flag"] = {} + + if policy_traceoptions["flag"]["name"] == "all": + traceoptions["flag"] = "all" + elif policy_traceoptions["flag"]["name"] == "configuration": + traceoptions["flag"] = "configuration" + elif policy_traceoptions["flag"]["name"] == "compilation": + traceoptions["flag"] = "compilation" + elif policy_traceoptions["flag"]["name"] == "ipc": + traceoptions["flag"] = "ipc" + elif policy_traceoptions["flag"]["name"] == "lookup": + traceoptions["flag"] = "lookup" + elif policy_traceoptions["flag"]["name"] == "routing-socket": + traceoptions["flag"] = "routing-socket" + elif policy_traceoptions["flag"]["name"] == "rules": + traceoptions["flag"] = "rules" + + if "no-remote-trace" in policy_traceoptions: + traceoptions["no_remote_trace"] = True + + return security_policies_global_config diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/security_zones.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/security_zones.py new file mode 100644 index 000000000..5368bf77f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/security_zones/security_zones.py @@ -0,0 +1,337 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos security_zones fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_zones.security_zones import ( + Security_zonesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Security_zonesFacts(object): + """The junos security_zones fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Security_zonesArgs.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_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def _get_device_data(self, connection, config_filters): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filters) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for security_polices + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <security> + <zones> + </zones> + </security> + </configuration> + """ + data = self._get_device_data(connection, config_filter) + + # split the config into instances of the resource + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/security/zones") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["security_zones"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["security_zones"] = 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 + """ + security_zones_config = {} + + # Parse facts for security zones + conf = conf.get("zones") + + if "functional-zone" in conf: + security_zones_config["functional_zone_management"] = {} + functional_zone_management = conf.get("functional-zone").get("management") or {} + + if "description" in functional_zone_management: + security_zones_config["functional_zone_management"][ + "description" + ] = functional_zone_management["description"] + + if "host-inbound-traffic" in functional_zone_management: + security_zones_config["functional_zone_management"][ + "host_inbound_traffic" + ] = self.parse_host_inbound_traffic( + functional_zone_management["host-inbound-traffic"], + ) + + if "interfaces" in functional_zone_management: + if isinstance(functional_zone_management["interfaces"], dict): + functional_zone_management["interfaces"] = [ + functional_zone_management["interfaces"], + ] + security_zones_config["functional_zone_management"]["interfaces"] = [ + interface["name"] for interface in functional_zone_management["interfaces"] + ] + + if "screen" in functional_zone_management: + security_zones_config["functional_zone_management"][ + "screen" + ] = functional_zone_management["screen"] + + if "security-zone" in conf: + security_zones_list = conf.get("security-zone") + if isinstance(security_zones_list, dict): + security_zones_list = [security_zones_list] + security_zones_config["zones"] = [] + + for security_zone in security_zones_list: + temp_sec_zone = {} + temp_sec_zone["name"] = security_zone["name"] + + if "address-book" in security_zone: + temp_sec_zone["address_book"] = {} + + if "address" in security_zone["address-book"]: + temp_sec_zone["address_book"]["addresses"] = [] + if isinstance( + security_zone["address-book"]["address"], + dict, + ): + security_zone["address-book"]["address"] = [ + security_zone["address-book"]["address"], + ] + + for address in security_zone["address-book"]["address"]: + temp_address = {} + + temp_address["name"] = address["name"] + if "ip-prefix" in address: + temp_address["ip_prefix"] = address["ip-prefix"] + elif "dns-name" in address: + temp_address["dns_name"] = {} + temp_address["dns_name"]["name"] = address["dns-name"]["name"] + if "ipv4-only" in address["dns-name"]: + temp_address["dns_name"]["ipv4_only"] = True + if "ipv6-only" in address["dns-name"]: + temp_address["dns_name"]["ipv6_only"] = True + elif "range-address" in address: + temp_address["range_address"] = {} + temp_address["range_address"]["from"] = address["range-address"][ + "name" + ] + temp_address["range_address"]["to"] = address["range-address"][ + "to" + ]["range-high"] + elif "wildcard-address" in address: + temp_address["wildcard_address"] = address["wildcard-address"][ + "name" + ] + if "description" in address: + temp_address["description"] = address["description"] + + temp_sec_zone["address_book"]["addresses"].append( + temp_address, + ) + + if "address-set" in security_zone["address-book"]: + temp_sec_zone["address_book"]["address_sets"] = [] + + for address_set in security_zone["address-book"]["address-set"]: + + temp_address_set = {} + + temp_address_set["name"] = address_set["name"] + if "address" in address_set: + if isinstance(address_set["address"], dict): + address_set["address"] = [ + address_set["address"], + ] + temp_address_set["addresses"] = [ + address["name"] for address in address_set["address"] + ] + if "address-set" in address_set: + if isinstance( + address_set["address-set"], + dict, + ): + address_set["address-set"] = [ + address_set["address-set"], + ] + temp_address_set["address_sets"] = [ + addr_set["name"] for addr_set in address_set["address-set"] + ] + if "description" in address_set: + temp_address_set["description"] = address_set["description"] + + temp_sec_zone["address_book"]["address_sets"].append(temp_address_set) + + if "advance-policy-based-routing-profile" in security_zone: + temp_sec_zone["advance_policy_based_routing_profile"] = security_zone[ + "advance-policy-based-routing-profile" + ]["profile"] + if "advanced-connection-tracking" in security_zone: + temp_sec_zone["advanced_connection_tracking"] = {} + temp_act = temp_sec_zone["advanced_connection_tracking"] + if "mode" in security_zone["advanced-connection-tracking"]: + temp_act["mode"] = security_zone["advanced-connection-tracking"]["mode"] + if "timeout" in security_zone["advanced-connection-tracking"]: + temp_act["timeout"] = security_zone["advanced-connection-tracking"][ + "timeout" + ] + if ( + "track-all-policies-to-this-zone" + in security_zone["advanced-connection-tracking"] + ): + temp_act["track_all_policies_to_this_zone"] = True + if "application-tracking" in security_zone: + temp_sec_zone["application_tracking"] = True + if "description" in security_zone: + temp_sec_zone["description"] = security_zone["description"] + if "enable-reverse-reroute" in security_zone: + temp_sec_zone["enable_reverse_reroute"] = True + if "host-inbound-traffic" in security_zone: + temp_sec_zone["host_inbound_traffic"] = self.parse_host_inbound_traffic( + security_zone["host-inbound-traffic"], + ) + if "interfaces" in security_zone: + if isinstance(security_zone["interfaces"], string_types): + security_zone["interfaces"] = [ + security_zone["interfaces"], + ] + temp_sec_zone["interfaces"] = [ + interface["name"] for interface in security_zone["interfaces"] + ] + if "screen" in security_zone: + temp_sec_zone["screen"] = security_zone["screen"] + if "source-identity-log" in security_zone: + temp_sec_zone["source_identity_log"] = True + if "tcp-rst" in security_zone: + temp_sec_zone["tcp_rst"] = True + if "unidirectional-session-refreshing" in security_zone: + temp_sec_zone["unidirectional_session_refreshing"] = True + + security_zones_config["zones"].append(temp_sec_zone) + + return security_zones_config + + def parse_host_inbound_traffic(self, host_inbound_traffic): + temp_hit = {} + + if "protocols" in host_inbound_traffic: + temp_hit["protocols"] = [] + if isinstance(host_inbound_traffic["protocols"], dict): + host_inbound_traffic["protocols"] = [ + host_inbound_traffic["protocols"], + ] + for protocol in host_inbound_traffic["protocols"]: + temp_protocol = {} + temp_protocol["name"] = protocol["name"] + if "except" in protocol: + temp_protocol["except"] = True + + temp_hit["protocols"].append(temp_protocol) + + if "system-services" in host_inbound_traffic: + temp_hit["system_services"] = [] + if isinstance(host_inbound_traffic["system-services"], dict): + host_inbound_traffic["system-services"] = [ + host_inbound_traffic["system-services"], + ] + for system_services in host_inbound_traffic["system-services"]: + temp_system_services = {} + temp_system_services["name"] = system_services["name"] + if "except" in system_services: + temp_system_services["except"] = True + + temp_hit["system_services"].append(temp_system_services) + + return temp_hit diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/snmp_server.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/snmp_server.py new file mode 100644 index 000000000..b3c35bfec --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/snmp_server/snmp_server.py @@ -0,0 +1,851 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The junos snmp_server fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.snmp_server.snmp_server import ( + Snmp_serverArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Snmp_serverFacts(object): + """The junos snmp_server fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Snmp_serverArgs.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, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return connection.get_configuration(filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ntp_gloabl + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <snmp> + </snmp> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + objs = {} + resources = data.xpath("configuration/snmp") + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + objs = self.render_config(self.generated_spec, xml) + + facts = {} + if objs: + facts["snmp_server"] = {} + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + + facts["snmp_server"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + if not HAS_XMLTODICT: + self._module.fail_json(msg=missing_required_lib("xmltodict")) + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + 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 + """ + snmp_server_config = {} + + # Parse facts for BGP address-family global node + conf = conf.get("snmp") + + # Read arp node + if "arp" in conf.keys(): + arp = conf.get("arp") + arp_dict = {} + if arp is None: + arp_dict["set"] = True + elif "host-name-resolution" in arp.keys(): + arp_dict["host_name_resolution"] = True + snmp_server_config["arp"] = arp_dict + + # Read client_lists node + if "client-list" in conf.keys(): + snmp_server_config["client_lists"] = self.get_client_list( + conf.get("client-list"), + ) + + # Read communities node + if "community" in conf.keys(): + comm_lst = [] + comm_lists = conf.get("community") + + if isinstance(comm_lists, dict): + comm_lst.append(self.get_community(comm_lists)) + else: + for item in comm_lists: + comm_lst.append(self.get_community(item)) + if comm_lst: + snmp_server_config["communities"] = comm_lst + + # Read routing-instance-access + if "routing-instance-access" in conf.keys(): + rinst_access = conf.get("routing-instance-access") + rints_dict = {} + access_lst = [] + if rinst_access is None: + rints_dict["set"] = True + else: + access_lists = rinst_access.get("access-list") + if isinstance(access_lists, dict): + access_lst.append(access_lists["name"]) + else: + for item in access_lists: + access_lst.append(item["name"]) + rints_dict["access_lists"] = access_lst + snmp_server_config["routing_instance_access"] = rints_dict + + # Read contact + if "contact" in conf.keys(): + snmp_server_config["contact"] = conf.get("contact") + + # Read description + if "description" in conf.keys(): + snmp_server_config["description"] = conf.get("description") + + # Read customization + if "customization" in conf.keys(): + custom_dict = {} + if "ether-stats-ifd-only" in conf["customization"].keys(): + custom_dict["ether_stats_ifd_only"] = True + snmp_server_config["customization"] = custom_dict + + # Read engine-id + if "engine-id" in conf.keys(): + engine_id = conf.get("engine-id") + engine_id_dict = {} + if "local" in engine_id.keys(): + engine_id_dict["local"] = engine_id["local"] + elif "use-default-ip-address" in engine_id.keys(): + engine_id_dict["use_default_ip_address"] = True + else: + engine_id_dict["use_mac_address"] = True + + snmp_server_config["engine_id"] = engine_id_dict + + # Read filter-duplicates + if "filter-duplicates" in conf.keys(): + snmp_server_config["filter_duplicates"] = True + + # Read filter-interfaces + if "filter-interfaces" in conf.keys(): + filter_dict = {} + filter_interfaces = conf.get("filter-interfaces") + + if filter_interfaces is None: + filter_dict["set"] = True + else: + if "all-internal-interfaces" in filter_interfaces.keys(): + filter_dict["all_internal_interfaces"] = True + if "interfaces" in filter_interfaces.keys(): + int_lst = [] + interfaces = filter_interfaces.get("interfaces") + if isinstance(interfaces, dict): + int_lst.append(interfaces["name"]) + else: + for item in interfaces: + int_lst.append(item["name"]) + filter_dict["interfaces"] = int_lst + + snmp_server_config["filter_interfaces"] = filter_dict + + # Read health_monitor + if "health-monitor" in conf.keys(): + health_dict = {} + health_monitor = conf.get("health-monitor") + + if health_monitor is None: + health_dict["set"] = True + else: + if "idp" in health_monitor.keys(): + health_dict["idp"] = True + if "falling-threshold" in health_monitor.keys(): + health_dict["falling_threshold"] = health_monitor["falling-threshold"] + if "rising-threshold" in health_monitor.keys(): + health_dict["rising_threshold"] = health_monitor["rising-threshold"] + if "interval" in health_monitor.keys(): + health_dict["interval"] = health_monitor["interval"] + snmp_server_config["health_monitor"] = health_dict + + # Read if-count-with-filter-interfaces + if "if-count-with-filter-interfaces" in conf.keys(): + snmp_server_config["if_count_with_filter_interfaces"] = True + + # Read interfaces + if "interfaces" in conf.keys(): + int_lst = [] + interfaces = conf.get("interfaces") + if isinstance(interfaces, dict): + int_lst.append(interfaces["name"]) + else: + for item in interfaces: + int_lst.append(item["name"]) + + snmp_server_config["interfaces"] = int_lst + + # Read location + if "location" in conf.keys(): + snmp_server_config["location"] = conf.get("location") + + # Read logical-system-trap-filter + if "logical-system-trap-filter" in conf.keys(): + snmp_server_config["logical_system_trap_filter"] = True + + # Read name + if "system-name" in conf.keys(): + snmp_server_config["name"] = conf.get("system-name") + + # Read nonvolatile + if "nonvolatile" in conf.keys(): + cfg_dict = {} + cfg_dict["commit_delay"] = conf["nonvolatile"].get("commit-delay") + snmp_server_config["nonvolatile"] = cfg_dict + + # Read rmon + if "rmon" in conf.keys(): + cfg_dict = {} + events = [] + alarms = [] + rmon = conf.get("rmon") + + if rmon is None: + cfg_dict["set"] = True + else: + if "event" in rmon.keys(): + event = rmon.get("event") + if isinstance(event, dict): + events.append(self.get_events(event)) + else: + for item in event: + events.append(self.get_events(item)) + cfg_dict["events"] = events + if "alarm" in rmon.keys(): + alarm = rmon.get("alarm") + if isinstance(alarm, dict): + alarms.append(self.get_alarms(alarm)) + else: + for item in alarm: + alarms.append(self.get_alarms(item)) + cfg_dict["alarms"] = alarms + + snmp_server_config["rmon"] = cfg_dict + + # Read subagent node + if "subagent" in conf.keys(): + cfg_dict = {} + subagent = conf.get("subagent") + + if "tcp" in subagent.keys(): + tcp = subagent.get("tcp") + tcp_dict = {} + if tcp is None: + tcp_dict["set"] = True + else: + tcp_dict["routing_instances_default"] = True + cfg_dict["tcp"] = tcp_dict + snmp_server_config["subagent"] = cfg_dict + + # Read traceoptions node + if "traceoptions" in conf.keys(): + cfg_dict = {} + trace_options = conf.get("traceoptions") + + if "file" in trace_options.keys(): + cfg_dict["file"] = self.get_trace_file( + trace_options.get("file"), + ) + if "flag" in trace_options.keys(): + cfg_dict["flag"] = self.get_trace_flag( + trace_options.get("flag"), + ) + if "memory-trace" in trace_options.keys(): + mtrace = trace_options.get("memory-trace") + trace_dict = {} + if mtrace is None: + trace_dict["set"] = True + else: + trace_dict["size"] = mtrace.get("size") + cfg_dict["memory_trace"] = trace_dict + if "no-remote-trace" in conf.keys(): + cfg_dict["no_remote_trace"] = True + + snmp_server_config["traceoptions"] = cfg_dict + + # Read trap-group node + if "trap-group" in conf.keys(): + cfg_lst = [] + trap_groups = conf.get("trap-group") + + if isinstance(trap_groups, dict): + cfg_lst.append(self.get_trap_group(trap_groups)) + else: + for item in trap_groups: + cfg_lst.append(self.get_trap_group(item)) + + snmp_server_config["trap_groups"] = cfg_lst + + # Read trap-options node + if "trap-options" in conf.keys(): + cfg_dict = {} + trap_options = conf.get("trap-options") + if trap_options is None: + cfg_dict["set"] = True + else: + if "agent-address" in trap_options.keys(): + agent_dict = {} + agent_dict["outgoing_interface"] = True + cfg_dict["agent_address"] = agent_dict + if "context-oid" in trap_options.keys(): + cfg_dict["context_oid"] = True + if "enterprise-oid" in trap_options.keys(): + cfg_dict["enterprise_oid"] = True + if "source-address" in trap_options.keys(): + source_address = trap_options.get("source-address") + source_dict = {} + if "address" in source_address.keys(): + source_dict["address"] = source_address["address"] + if "lowest-loopback" in source_address.keys(): + source_dict["lowest_loopback"] = True + cfg_dict["source_address"] = source_dict + if "routing-instance" in trap_options.keys(): + cfg_dict["routing_instance"] = trap_options.get( + "routing-instance", + ) + + snmp_server_config["trap_options"] = cfg_dict + + # Read snmp-v3 + if "v3" in conf.keys(): + cfg_dict = {} + snmp_v3 = conf.get("v3") + + if "notify" in snmp_v3.keys(): + notify_lst = [] + notify = snmp_v3.get("notify") + if isinstance(notify, dict): + notify_lst.append(self.get_notify(notify)) + else: + for item in notify: + notify_lst.append(self.get_notify(item)) + cfg_dict["notify"] = notify_lst + if "notify-filter" in snmp_v3.keys(): + notify_filter = snmp_v3.get("notify-filter") + notify_lst = [] + if isinstance(notify_filter, dict): + notify_lst.append(self.get_notify_filter(notify_filter)) + else: + for item in notify_filter: + notify_lst.append(self.get_notify_filter(item)) + cfg_dict["notify_filter"] = notify_lst + + if "snmp-community" in snmp_v3.keys(): + comm_lst = [] + communities = snmp_v3.get("snmp-community") + if isinstance(communities, dict): + comm_lst.append(self.get_snmpv3_comm(communities)) + else: + for item in communities: + comm_lst.append(self.get_snmpv3_comm(item)) + cfg_dict["snmp_community"] = comm_lst + + if "target-address" in snmp_v3.keys(): + tar_addr_lst = [] + tar_addr = snmp_v3.get("target-address") + if isinstance(tar_addr, dict): + tar_addr_lst.append(self.get_snmpv3_target(tar_addr)) + else: + for item in tar_addr: + tar_addr_lst.append(self.get_snmpv3_target(item)) + cfg_dict["target_addresses"] = tar_addr_lst + + if "target-parameters" in snmp_v3.keys(): + tar_param_lst = [] + tar_params = snmp_v3.get("target-parameters") + if isinstance(tar_params, dict): + tar_param_lst.append(self.get_snmpv3_param(tar_params)) + else: + for item in tar_params: + tar_param_lst.append(self.get_snmpv3_param(item)) + cfg_dict["target_parameters"] = tar_param_lst + + if "usm" in snmp_v3.keys(): + usm_dict = {} + usm = snmp_v3.get("usm") + if "local-engine" in usm.keys(): + local_dict = {} + local_engine = usm.get("local-engine") + if "user" in local_engine.keys(): + user_lst = [] + users = local_engine.get("user") + if isinstance(users, dict): + user_lst.append(self.get_user(users)) + else: + for item in users: + user_lst.append(self.get_user(item)) + local_dict["users"] = user_lst + + usm_dict["local_engine"] = local_dict + + if "remote-engine" in usm.keys(): + remote_lst = [] + remote_dict = {} + remote_engine = usm.get("remote-engine") + if isinstance(remote_engine, dict): + remote_dict["id"] = remote_engine["name"] + if "user" in remote_engine.keys(): + user_lst = [] + users = remote_engine.get("user") + if isinstance(users, dict): + user_lst.append(self.get_user(users)) + else: + for item in users: + user_lst.append(self.get_user(item)) + remote_dict["users"] = user_lst + remote_lst.append(remote_dict) + else: + for remote_eng in remote_engine: + remote_dict["id"] = remote_eng["name"] + if "user" in remote_eng.keys(): + user_lst = [] + users = remote_eng.get("user") + if isinstance(users, dict): + user_lst.append(self.get_user(users)) + else: + for item in users: + user_lst.append(self.get_user(item)) + remote_dict["users"] = user_lst + remote_lst.append(remote_dict) + remote_dict = {} + usm_dict["remote_engine"] = remote_lst + cfg_dict["usm"] = usm_dict + + snmp_server_config["snmp_v3"] = cfg_dict + + # Read view + if "view" in conf.keys(): + cfg_lst = [] + views = conf.get("view") + if isinstance(views, dict): + cfg_lst.append(self.get_view(views)) + else: + for item in views: + cfg_lst.append(self.get_view(item)) + + snmp_server_config["views"] = cfg_lst + return utils.remove_empties(snmp_server_config) + + def get_view(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg["name"] + if "oid" in cfg.keys(): + oid_lst = [] + oid_dict = {} + oids = cfg.get("oid") + if isinstance(oids, list): + for item in oids: + oid_dict["oid"] = item["name"] + if "exclude" in item.keys(): + oid_dict["exclude"] = True + if "include" in item.keys(): + oid_dict["include"] = True + oid_lst.append(oid_dict) + oid_dict = {} + else: + oid_dict["oid"] = oids["name"] + if "exclude" in oids.keys(): + oid_dict["exclude"] = True + if "include" in oids.keys(): + oid_dict["include"] = True + oid_lst.append(oid_dict) + cfg_dict["oids"] = oid_lst + + return cfg_dict + + def get_user(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg.get("name") + if "authentication-md5" in cfg.keys(): + auth_dict = {} + auth_md5 = cfg.get("authentication-md5") + auth_dict["key"] = auth_md5["authentication-key"] + if "authentication-password" in auth_md5.keys(): + auth_dict["password"] = auth_md5["authentication-password"] + cfg_dict["authentication_md5"] = auth_dict + if "authentication-none" in cfg.keys(): + cfg_dict["authentication_none"] = True + if "authentication-sha" in cfg.keys(): + auth_dict = {} + auth_sha = cfg.get("authentication-sha") + auth_dict["key"] = auth_sha["authentication-key"] + if "authentication-password" in auth_sha.keys(): + auth_dict["password"] = auth_sha["authentication-password"] + cfg_dict["authentication_sha"] = auth_dict + if "privacy-3des" in cfg.keys(): + pri_dict = {} + pri_3des = cfg.get("privacy-3des") + pri_dict["key"] = pri_3des["privacy-key"] + if "privacy-password" in pri_3des.keys(): + pri_dict["password"] = pri_3des["privacy-password"] + cfg_dict["privacy_3des"] = pri_dict + if "privacy-aes128" in cfg.keys(): + pri_dict = {} + pri_aes = cfg.get("privacy-aes128") + pri_dict["key"] = pri_aes["privacy-key"] + if "privacy-password" in pri_aes.keys(): + pri_dict["password"] = pri_aes["privacy-password"] + cfg_dict["privacy_aes128"] = pri_dict + if "privacy-none" in cfg.keys(): + cfg_dict["privacy_none"] = True + return cfg_dict + + def get_snmpv3_param(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg.get("name") + if "notify-filter" in cfg.keys(): + cfg_dict["notify_filter"] = cfg.get("notify-filter") + if "parameters" in cfg.keys(): + param_dict = {} + parameters = cfg.get("parameters") + for key in parameters.keys(): + param_dict[key.replace("-", "_")] = parameters.get(key) + cfg_dict["parameters"] = param_dict + return cfg_dict + + def get_snmpv3_target(self, cfg): + cfg_dict = {} + for key in cfg.keys(): + cfg_dict[key.replace("-", "_")] = cfg.get(key) + return cfg_dict + + def get_snmpv3_comm(self, cfg): + cfg_dict = {} + cfg_dict["community_index"] = cfg["name"] + if "context" in cfg.keys(): + cfg_dict["context"] = cfg.get("context") + if "tag" in cfg.keys(): + cfg_dict["tag"] = cfg.get("tag") + if "security-name" in cfg.keys(): + cfg_dict["security_name"] = cfg.get("security-name") + if "community-name" in cfg.keys(): + cfg_dict["community_name"] = cfg.get("community-name") + return cfg_dict + + def get_notify_filter(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg["name"] + if "oid" in cfg.keys(): + oid_lst = [] + oid_dict = {} + oids = cfg.get("oid") + if isinstance(oids, list): + for item in oids: + oid_dict["oid"] = item["name"] + if "exclude" in item.keys(): + oid_dict["exclude"] = True + if "include" in item.keys(): + oid_dict["include"] = True + oid_lst.append(oid_dict) + oid_dict = {} + else: + oid_dict["oid"] = oids["name"] + if "exclude" in oids.keys(): + oid_dict["exclude"] = True + if "include" in oids.keys(): + oid_dict["include"] = True + oid_lst.append(oid_dict) + cfg_dict["oids"] = oid_lst + return cfg_dict + + def get_notify(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg["name"] + if "type" in cfg.keys(): + cfg_dict["type"] = cfg.get("type") + if "tag" in cfg.keys(): + cfg_dict["tag"] = cfg.get("tag") + + return cfg_dict + + def get_trap_group(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg.get("name") + if "categories" in cfg.keys(): + categories_dict = {} + categories = cfg.get("categories") + for item in categories.keys(): + if item == "otn-alarms": + otn_dict = {} + otn_alarms = categories.get("otn-alarms") + for key in otn_alarms.keys(): + otn_dict[key.replace("-", "_")] = True + categories_dict["otn_alarms"] = otn_dict + else: + categories_dict[item.replace("-", "_")] = True + cfg_dict["categories"] = categories_dict + if "destination-port" in cfg.keys(): + cfg_dict["destination_port"] = cfg.get("destination-port") + if "routing-instance" in cfg.keys(): + cfg_dict["routing_instance"] = cfg.get("routing-instance") + if "version" in cfg.keys(): + cfg_dict["version"] = cfg.get("version") + if "targets" in cfg.keys(): + targets_lst = [] + targets = cfg.get("targets") + if isinstance(targets, dict): + targets_lst.append(targets["name"]) + else: + for item in targets: + targets_lst.append(item["name"]) + cfg_dict["targets"] = targets_lst + + return cfg_dict + + def get_trace_flag(self, cfg): + cfg_dict = {} + if isinstance(cfg, dict): + cfg_dict[cfg["name"].replace("-", "_")] = True + else: + for item in cfg: + cfg_dict[item["name"].replace("-", "_")] = True + + return cfg_dict + + def get_trace_file(self, cfg): + cfg_dict = {} + if "match" in cfg.keys(): + cfg_dict["match"] = cfg.get("match") + if "files" in cfg.keys(): + cfg_dict["files"] = cfg.get("files") + if "no-world-readable" in cfg.keys(): + cfg_dict["no_world_readable"] = True + if "world-readable" in cfg.keys(): + cfg_dict["world_readable"] = True + if "size" in cfg.keys(): + cfg_dict["size"] = cfg.get("size") + + return cfg_dict + + def get_events(self, cfg): + cfg_dict = {} + cfg_dict["id"] = cfg["name"] + if "community" in cfg.keys(): + cfg_dict["community"] = cfg.get("community") + if "description" in cfg.keys(): + cfg_dict["description"] = cfg.get("description") + if "type" in cfg.keys(): + cfg_dict["type"] = cfg.get("type") + return cfg_dict + + def get_alarms(self, cfg): + cfg_dict = {} + cfg_dict["id"] = cfg["name"] + if "description" in cfg.keys(): + cfg_dict["description"] = cfg.get("description") + if "falling-event-index" in cfg.keys(): + cfg_dict["falling_event_index"] = cfg.get("falling-event-index") + if "falling-threshold" in cfg.keys(): + cfg_dict["falling_threshold"] = cfg.get("falling-threshold") + if "falling-threshold-interval" in cfg.keys(): + cfg_dict["falling_threshold_interval"] = cfg.get( + "falling-threshold-interval", + ) + if "interval" in cfg.keys(): + cfg_dict["interval"] = cfg.get("interval") + if "request-type" in cfg.keys(): + cfg_dict["request_type"] = cfg.get("request-type") + if "rising-event-index" in cfg.keys(): + cfg_dict["rising_event_index"] = cfg.get("rising-event-index") + if "rising-threshold" in cfg.keys(): + cfg_dict["rising_threshold"] = cfg.get("rising-threshold") + if "sample-type" in cfg.keys(): + cfg_dict["sample_type"] = cfg.get("sample-type") + if "startup-alarm" in cfg.keys(): + cfg_dict["startup_alarm"] = cfg.get("startup-alarm") + if "syslog-subtag" in cfg.keys(): + cfg_dict["syslog_subtag"] = cfg.get("syslog-subtag") + if "variable" in cfg.keys(): + cfg_dict["variable"] = cfg.get("variable") + + return cfg_dict + + def get_routing_instance(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg["name"] + if "client-list-name" in cfg.keys(): + cfg_dict["client_list_name"] = cfg.get("client-list-name") + if "clients" in cfg.keys(): + client_lst = [] + if isinstance(cfg.get("clients"), dict): + client_lst.append(self.get_client_address(cfg.get("clients"))) + else: + clients = cfg.get("clients") + for item in clients: + client_lst.append(self.get_client_address(item)) + cfg_dict["clients"] = client_lst + return cfg_dict + + def get_community(self, cfg): + cfg_dict = {} + cfg_dict["name"] = cfg["name"] + if "authorization" in cfg.keys(): + cfg_dict["authorization"] = cfg.get("authorization") + if "client-list-name" in cfg.keys(): + cfg_dict["client_list_name"] = cfg.get("client-list-name") + if "clients" in cfg.keys(): + client_lst = [] + if isinstance(cfg.get("clients"), dict): + client_lst.append(self.get_client_address(cfg.get("clients"))) + else: + clients = cfg.get("clients") + for item in clients: + client_lst.append(self.get_client_address(item)) + cfg_dict["clients"] = client_lst + if "routing-instance" in cfg.keys(): + rinst_lst = [] + rinst_lists = cfg.get("routing-instance") + + if isinstance(rinst_lists, dict): + rinst_lst.append(self.get_routing_instance(rinst_lists)) + else: + for item in rinst_lists: + rinst_lst.append(self.get_routing_instance(item)) + if rinst_lst: + cfg_dict["routing_instances"] = rinst_lst + if "view" in cfg.keys(): + cfg_dict["view"] = cfg.get("view") + + return cfg_dict + + def get_client_address(self, cfg): + cfg_dict = {} + cfg_dict["address"] = cfg["name"] + if "restrict" in cfg.keys(): + cfg_dict["restrict"] = True + return cfg_dict + + def get_client_list(self, cfg): + client_lst = [] + client_lists = cfg + client_dict = {} + if isinstance(client_lists, dict): + client_dict["name"] = client_lists["name"] + if "client-address-list" in client_lists.keys(): + client_addresses = client_lists["client-address-list"] + client_address_lst = [] + if isinstance(client_addresses, dict): + client_address_lst.append( + self.get_client_address(client_addresses), + ) + else: + for address in client_addresses: + client_address_lst.append( + self.get_client_address(address), + ) + if client_address_lst: + client_dict["addresses"] = client_address_lst + client_lst.append(client_dict) + + else: + for client in client_lists: + client_dict["name"] = client["name"] + if "client-address-list" in client.keys(): + client_addresses = client["client-address-list"] + client_address_lst = [] + if isinstance(client_addresses, dict): + client_address_lst.append( + self.get_client_address(client_addresses), + ) + else: + for address in client_addresses: + client_address_lst.append( + self.get_client_address(address), + ) + if client_address_lst: + client_dict["addresses"] = client_address_lst + client_lst.append(client_dict) + client_dict = {} + return client_lst diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/static_routes.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/static_routes.py new file mode 100644 index 000000000..0be6f032d --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/static_routes/static_routes.py @@ -0,0 +1,180 @@ +# +# -*- 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 junos static_routes fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.static_routes.static_routes import ( + Static_routesArgs, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False +try: + import xmltodict + + HAS_XMLTODICT = True +except ImportError: + HAS_XMLTODICT = False + + +class Static_routesFacts(object): + """The junos 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 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 HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + if not HAS_XMLTODICT: + self._module.fail_json(msg="xmltodict is not installed.") + + if not data: + config_filter = """ + <configuration> + <routing-instances/> + <routing-options/> + </configuration> + """ + data = connection.get_configuration(filter=config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/routing-options") + vrf_resources = data.xpath("configuration/routing-instances") + resources.extend(vrf_resources) + + objs = [] + for resource in resources: + if resource is not None: + xml = self._get_xml_dict(resource) + obj = self.render_config(self.generated_spec, xml) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["static_routes"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["static_routes"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def _get_xml_dict(self, xml_root): + xml_dict = xmltodict.parse( + etree.tostring(xml_root), + dict_constructor=dict, + ) + return xml_dict + + def _create_route_dict(self, afi, route_path): + routes_dict = {"afi": afi, "routes": []} + if isinstance(route_path, dict): + route_path = [route_path] + for route in route_path: + route_dict = {} + route_dict["dest"] = route["name"] + if route.get("metric"): + route_dict["metric"] = route["metric"]["metric-value"] + route_dict["next_hop"] = [] + route_dict["next_hop"].append( + {"forward_router_address": route["next-hop"]}, + ) + routes_dict["routes"].append(route_dict) + return routes_dict + + 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) + routes = [] + config["address_families"] = [] + + if conf.get("routing-options"): + if conf["routing-options"].get("rib"): + if conf["routing-options"].get("rib").get("name") == "inet6.0": + if conf["routing-options"].get("rib").get("static"): + route_path = conf["routing-options"]["rib"]["static"].get("route") + routes.append( + self._create_route_dict("ipv6", route_path), + ) + if conf["routing-options"].get("static"): + route_path = conf["routing-options"]["static"].get("route") + routes.append(self._create_route_dict("ipv4", route_path)) + + if conf.get("routing-instances"): + config["vrf"] = conf["routing-instances"]["instance"]["name"] + if ( + conf["routing-instances"] + .get("instance") + .get("routing-options") + .get("rib") + .get("name") + == config["vrf"] + ".inet6.0" + ): + if conf["routing-instances"]["instance"]["routing-options"]["rib"].get("static"): + route_path = conf["routing-instances"]["instance"]["routing-options"]["rib"][ + "static" + ].get("route") + routes.append(self._create_route_dict("ipv6", route_path)) + if conf["routing-instances"].get("instance").get("routing-options").get("static"): + route_path = conf["routing-instances"]["instance"]["routing-options"]["static"].get( + "route", + ) + routes.append(self._create_route_dict("ipv4", route_path)) + config["address_families"].extend(routes) + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/vlans.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/vlans.py new file mode 100644 index 000000000..f4d35e635 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/facts/vlans/vlans.py @@ -0,0 +1,124 @@ +# +# -*- 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 junos vlans fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.vlans.vlans import ( + VlansArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.utils.utils import ( + get_resource_config, +) + + +try: + from lxml import etree + + HAS_LXML = True +except ImportError: + HAS_LXML = False + + +class VlansFacts(object): + """The junos vlans fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = VlansArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection, config_filter): + """ + :param connection: + :param config_filter: + :return: + """ + return get_resource_config(connection, config_filter=config_filter) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for vlans + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not HAS_LXML: + self._module.fail_json(msg="lxml is not installed.") + + if not data: + config_filter = """ + <configuration> + <vlans> + </vlans> + </configuration> + """ + data = self.get_device_data(connection, config_filter) + + if isinstance(data, string_types): + data = etree.fromstring( + to_bytes(data, errors="surrogate_then_replace"), + ) + + resources = data.xpath("configuration/vlans/vlan") + + objs = [] + for resource in resources: + if resource is not None: + obj = self.render_config(self.generated_spec, resource) + if obj: + objs.append(obj) + facts = {} + if objs: + facts["vlans"] = [] + params = utils.validate_config( + self.argument_spec, + {"config": objs}, + ) + for cfg in params["config"]: + facts["vlans"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + 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["name"] = utils.get_xml_conf_arg(conf, "name") + config["vlan_id"] = utils.get_xml_conf_arg(conf, "vlan-id") + config["description"] = utils.get_xml_conf_arg(conf, "description") + config["l3_interface"] = utils.get_xml_conf_arg(conf, "l3-interface") + return utils.remove_empties(config) diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/junos.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/junos.py new file mode 100644 index 000000000..36452b123 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/junos.py @@ -0,0 +1,509 @@ +# +# (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 +import collections +import json + +from contextlib import contextmanager +from copy import deepcopy + +from ansible.module_utils._text import to_text +from ansible.module_utils.connection import Connection, ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + NetconfConnection, +) + + +try: + from lxml.etree import Element, SubElement + from lxml.etree import tostring as xml_to_string + + HAS_LXML = True +except ImportError: + from xml.etree.ElementTree import Element, SubElement + from xml.etree.ElementTree import tostring as xml_to_string + + HAS_LXML = False + +try: + from jnpr.junos import Device + from jnpr.junos.exception import ConnectError + + HAS_PYEZ = True +except ImportError: + HAS_PYEZ = False + +ACTIONS = frozenset(["merge", "override", "replace", "update", "set"]) +JSON_ACTIONS = frozenset(["merge", "override", "update"]) +FORMATS = frozenset(["xml", "text", "json"]) +CONFIG_FORMATS = frozenset(["xml", "text", "json", "set"]) + + +def tostring(element, encoding="UTF-8", pretty_print=False): + if HAS_LXML: + return xml_to_string( + element, + encoding="unicode", + pretty_print=pretty_print, + ) + else: + return to_text( + xml_to_string(element, encoding), + encoding=encoding, + ) + + +def get_connection(module): + if hasattr(module, "_junos_connection"): + return module._junos_connection + + capabilities = get_capabilities(module) + network_api = capabilities.get("network_api") + if network_api == "cliconf": + module._junos_connection = Connection(module._socket_path) + elif network_api == "netconf": + module._junos_connection = NetconfConnection(module._socket_path) + else: + module.fail_json(msg="Invalid connection type %s" % network_api) + + return module._junos_connection + + +def get_capabilities(module): + if hasattr(module, "_junos_capabilities"): + return module._junos_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._junos_capabilities = json.loads(capabilities) + return module._junos_capabilities + + +def get_device(module): + provider = module.params.get("provider") or {} + host = provider.get("host") + + kwargs = { + "port": provider.get("port") or 830, + "user": provider.get("username"), + } + + if "password" in provider: + kwargs["passwd"] = provider.get("password") + + if "ssh_keyfile" in provider: + kwargs["ssh_private_key_file"] = provider.get("ssh_keyfile") + + if module.params.get("ssh_config"): + kwargs["ssh_config"] = module.params["ssh_config"] + + if module.params.get("ssh_private_key_file"): + kwargs["ssh_private_key_file"] = module.params["ssh_private_key_file"] + + kwargs["gather_facts"] = False + + try: + device = Device(host, **kwargs) + device.open() + device.timeout = provider.get("timeout") or 10 + except ConnectError as exc: + module.fail_json("unable to connect to %s: %s" % (host, to_text(exc))) + + return device + + +def is_netconf(module): + capabilities = get_capabilities(module) + return True if capabilities.get("network_api") == "netconf" else False + + +def _validate_rollback_id(module, value): + try: + if not 0 <= int(value) <= 49: + raise ValueError + except ValueError: + module.fail_json(msg="rollback must be between 0 and 49") + + +def load_configuration( + module, + candidate=None, + action="merge", + rollback=None, + format="xml", +): + + if all((candidate is None, rollback is None)): + module.fail_json(msg="one of candidate or rollback must be specified") + + elif all((candidate is not None, rollback is not None)): + module.fail_json(msg="candidate and rollback are mutually exclusive") + + if format not in FORMATS: + module.fail_json(msg="invalid format specified") + + if format == "json" and action not in JSON_ACTIONS: + module.fail_json(msg="invalid action for format json") + elif format in ("text", "xml") and action not in ACTIONS: + module.fail_json(msg="invalid action format %s" % format) + if action == "set" and format != "text": + module.fail_json(msg="format must be text when action is set") + + conn = get_connection(module) + try: + if rollback is not None: + _validate_rollback_id(module, rollback) + obj = Element("load-configuration", {"rollback": str(rollback)}) + conn.execute_rpc(tostring(obj)) + else: + return conn.load_configuration( + config=candidate, + action=action, + format=format, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + +def get_configuration( + module, + compare=False, + format="xml", + rollback="0", + filter=None, +): + if format not in CONFIG_FORMATS: + module.fail_json(msg="invalid config format specified") + + conn = get_connection(module) + try: + if compare: + xattrs = {"format": format} + _validate_rollback_id(module, rollback) + xattrs["compare"] = "rollback" + xattrs["rollback"] = str(rollback) + reply = conn.execute_rpc( + tostring(Element("get-configuration", xattrs)), + ) + else: + reply = conn.get_configuration(format=format, filter=filter) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return reply + + +def commit_configuration( + module, + confirm=False, + check=False, + comment=None, + confirm_timeout=None, + synchronize=False, + at_time=None, +): + conn = get_connection(module) + try: + if check: + reply = conn.validate() + else: + if is_netconf(module): + reply = conn.commit( + confirmed=confirm, + timeout=confirm_timeout, + comment=comment, + synchronize=synchronize, + at_time=at_time, + ) + else: + reply = conn.commit( + comment=comment, + confirmed=confirm, + at_time=at_time, + synchronize=synchronize, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return reply + + +def command(module, cmd, format="text", rpc_only=False): + conn = get_connection(module) + if rpc_only: + cmd += " | display xml rpc" + try: + response = conn.command(command=cmd, format=format) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return response + + +def lock_configuration(module): + conn = get_connection(module) + try: + response = conn.lock() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return response + + +def unlock_configuration(module): + conn = get_connection(module) + try: + response = conn.unlock() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return response + + +@contextmanager +def locked_config(module): + try: + lock_configuration(module) + yield + finally: + unlock_configuration(module) + + +def discard_changes(module): + conn = get_connection(module) + try: + response = conn.discard_changes() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return response + + +def get_diff(module, rollback="0"): + reply = get_configuration( + module, + compare=True, + format="text", + rollback=rollback, + ) + # if warning is received from device diff is empty. + if isinstance(reply, list): + return None + + output = reply.find(".//configuration-output") + if output is not None: + return to_text(output.text, encoding="latin-1").strip() + + +def load_config(module, candidate, warnings, action="merge", format="xml"): + get_connection(module) + if not candidate: + return + + if isinstance(candidate, list): + candidate = "\n".join(candidate) + + reply = load_configuration(module, candidate, action=action, format=format) + if isinstance(reply, list): + warnings.extend(reply) + + module._junos_connection.validate() + return get_diff(module) + + +def map_params_to_obj(module, param_to_xpath_map, param=None): + """ + Creates a new dictionary with key as xpath corresponding + to param and value is a list of dict with metadata and values for + the xpath. + Acceptable metadata keys: + 'value': Value of param. + 'tag_only': Value is indicated by tag only in xml hierarchy. + 'leaf_only': If operation is to be added at leaf node only. + 'value_req': If value(text) is required for leaf node. + 'is_key': If the field is key or not. + eg: Output + { + 'name': [{'value': 'ge-0/0/1'}] + 'disable': [{'value': True, tag_only': True}] + } + + :param module: + :param param_to_xpath_map: Modules params to xpath map + :return: obj + """ + if not param: + param = module.params + + obj = collections.OrderedDict() + for key, attribute in param_to_xpath_map.items(): + if key in param: + is_attribute_dict = False + + value = param[key] + if not isinstance(value, (list, tuple)): + value = [value] + + if isinstance(attribute, dict): + xpath = attribute.get("xpath") + is_attribute_dict = True + else: + xpath = attribute + + if not obj.get(xpath): + obj[xpath] = list() + + for val in value: + if is_attribute_dict: + attr = deepcopy(attribute) + del attr["xpath"] + + attr.update({"value": val}) + obj[xpath].append(attr) + else: + obj[xpath].append({"value": val}) + return obj + + +def map_obj_to_ele(module, want, top, value_map=None, param=None): + if not HAS_LXML: + module.fail_json(msg="lxml is not installed.") + + if not param: + param = module.params + + root = Element("root") + top_ele = top.split("/") + ele = SubElement(root, top_ele[0]) + + if len(top_ele) > 1: + for item in top_ele[1:-1]: + ele = SubElement(ele, item) + container = ele + state = param.get("state") + active = param.get("active") + if active: + oper = "active" + else: + oper = "inactive" + + # build xml subtree + if container.tag != top_ele[-1]: + node = SubElement(container, top_ele[-1]) + else: + node = container + + for fxpath, attributes in want.items(): + for attr in attributes: + tag_only = attr.get("tag_only", False) + leaf_only = attr.get("leaf_only", False) + value_req = attr.get("value_req", False) + is_key = attr.get("is_key", False) + parent_attrib = attr.get("parent_attrib", True) + value = attr.get("value") + field_top = attr.get("top") + + # operation 'delete' is added as element attribute + # only if it is key or leaf only node + if state == "absent" and not (is_key or leaf_only): + continue + + # convert param value to device specific value + if value_map and fxpath in value_map: + value = value_map[fxpath].get(value) + + if (value is not None) or tag_only or leaf_only: + ele = node + if field_top: + # eg: top = 'system/syslog/file' + # field_top = 'system/syslog/file/contents' + # <file> + # <name>test</name> + # <contents> + # </contents> + # </file> + ele_list = root.xpath(top + "/" + field_top) + + if not len(ele_list): + fields = field_top.split("/") + ele = node + for item in fields: + inner_ele = root.xpath(top + "/" + item) + if len(inner_ele): + ele = inner_ele[0] + else: + ele = SubElement(ele, item) + else: + ele = ele_list[0] + + if value is not None and not isinstance(value, bool): + value = to_text(value, errors="surrogate_then_replace") + + if fxpath: + tags = fxpath.split("/") + for item in tags: + ele = SubElement(ele, item) + + if tag_only: + if state == "present": + if not value: + # if value of tag_only node is false, delete the node + ele.set("delete", "delete") + + elif leaf_only: + if state == "present": + ele.set(oper, oper) + ele.text = value + else: + ele.set("delete", "delete") + # Add value of leaf node if required while deleting. + # in some cases if value is present while deleting, it + # can result in error, hence the check + if value_req: + ele.text = value + if is_key: + par = ele.getparent() + par.set("delete", "delete") + else: + ele.text = value + par = ele.getparent() + + if parent_attrib: + if state == "present": + # set replace attribute at parent node + if not par.attrib.get("replace"): + par.set("replace", "replace") + + # set active/inactive at parent node + if not par.attrib.get(oper): + par.set(oper, oper) + else: + par.set("delete", "delete") + + return root.getchildren()[0] + + +def to_param_list(module): + aggregate = module.params.get("aggregate") + if aggregate: + if isinstance(aggregate, dict): + return [aggregate] + else: + return aggregate + else: + return [module.params] diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/__init__.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/utils.py b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/utils.py new file mode 100644 index 000000000..1f1250a34 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/module_utils/network/junos/utils/utils.py @@ -0,0 +1,52 @@ +# +# -*- 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_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + validate_config, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + tostring, +) + + +try: + from ncclient.xml_ import new_ele, to_ele + + HAS_NCCLIENT = True +except ImportError: + HAS_NCCLIENT = False + +try: + from ansible.module_utils.common.parameters import _list_no_log_values as list_no_log_values +except ImportError: + # TODO: Remove this import when we no longer support ansible < 2.11 + from ansible.module_utils.common.parameters import list_no_log_values + + +def get_resource_config(connection, config_filter=None, attrib=None): + + if attrib is None: + attrib = {"inherit": "inherit"} + + get_ele = new_ele("get-configuration", attrib) + if config_filter: + get_ele.append(to_ele(config_filter)) + + return connection.execute_rpc(tostring(get_ele)) + + +def _validate_config(_module, spec, data, redact=False): + validated_data = validate_config(spec, data) + if redact: + _module.no_log_values.update(list_no_log_values(spec, validated_data)) + return validated_data diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/__init__.py b/ansible_collections/junipernetworks/junos/plugins/modules/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_acl_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_acl_interfaces.py new file mode 100644 index 000000000..ccc604a11 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_acl_interfaces.py @@ -0,0 +1,366 @@ +#!/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 junos_acl_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_acl_interfaces +short_description: ACL interfaces resource module +description: +- This module manages adding and removing Access Control Lists (ACLs) from interfaces + on devices running Juniper JUNOS. +version_added: 1.0.0 +author: Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +- xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of ACL options for interfaces. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier for the interface. + type: str + access_groups: + type: list + elements: dict + description: + - Specifies ACLs attached to the interface. + suboptions: + afi: + description: + - Specifies the AFI for the ACL(s) to be configured on this interface. + type: str + choices: [ipv4, ipv6] + acls: + type: list + description: + - Specifies the ACLs for the provided AFI. + elements: dict + suboptions: + name: + description: + - Specifies the name of the IPv4/IPv4 ACL for the interface. + type: str + direction: + description: + - Specifies the direction of packets that the ACL will be applied + on. + type: str + choices: [in, out] + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the Junos device + by executing the command B(show interfaces). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface with filter"; +# unit 0 { +# family inet { +# filter { +# input inbound_acl; +# output outbound_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + +- name: Delete JUNOS L3 interface filter + junipernetworks.junos.junos_acl_interfaces: + config: + - name: ge-1/0/0 + access_groups: + - afi: ipv4 + acls: + - name: inbound_acl + direction: in + - name: outbound_acl + direction: out + state: deleted + +# After state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface with filter"; +# unit 0 { +# family inet { +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + + +# Using merged + +# Before state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface without filter"; +# unit 0 { +# family inet { +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + +- name: Merge JUNOS L3 interface filter + junipernetworks.junos.junos_acl_interfaces: + config: + - name: ge-1/0/0 + access_groups: + - afi: ipv4 + acls: + - name: inbound_acl + direction: in + - name: outbound_acl + direction: out + state: merged + +# After state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface with filter"; +# unit 0 { +# family inet { +# filter { +# input inbound_acl; +# output outbound_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + + +# Using overridden + +# Before state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface without filter"; +# unit 0 { +# family inet { +# filter { +# input foo_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + +- name: Override JUNOS L3 interface filter + junipernetworks.junos.junos_acl_interfaces: + config: + - name: ge-1/0/0 + access_groups: + - afi: ipv4 + acls: + - name: inbound_acl + direction: in + - name: outbound_acl + direction: out + state: overridden + +# After state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface with filter"; +# unit 0 { +# family inet { +# filter { +# input inbound_acl; +# output outbound_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + + +# Using replaced + +# Before state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface without filter"; +# unit 0 { +# family inet { +# filter { +# input foo_acl; +# output outbound_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + +- name: Replace JUNOS L3 interface filter + junipernetworks.junos.junos_acl_interfaces: + config: + - name: ge-1/0/0 + access_groups: + - afi: ipv4 + acls: + - name: inbound_acl + direction: in + state: replaced + +# After state: +# ------------- +# +# admin# show interfaces +# ge-1/0/0 { +# description "L3 interface with filter"; +# unit 0 { +# family inet { +# filter { +# input inbound_acl; +# output outbound_acl; +# } +# address 100.64.0.1/10; +# address 100.64.0.2/10; +# } +# family inet6; +# } + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Acl_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Acl_interfaces(module).execute_module() + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_acls.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_acls.py new file mode 100644 index 000000000..2b1bafc7a --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_acls.py @@ -0,0 +1,364 @@ +#!/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 junos_acls +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_acls +short_description: ACLs resource module +description: This module provides declarative management of acls/filters on Juniper + JUNOS devices +version_added: 1.0.0 +author: Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +- xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed +- This module works with connection C(netconf) +- See L(the Junos OS Platform Options,https://docsansiblecom/ansible/latest/network/user_guide/platform_junoshtml) +- Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of acls options + type: list + elements: dict + suboptions: + afi: + description: + - Protocol family to use by the acl filter + type: str + required: true + choices: + - ipv4 + - ipv6 + acls: + description: + - List of Access Control Lists (ACLs). + type: list + elements: dict + suboptions: + name: + description: + - Name to use for the acl filter + type: str + required: true + aces: + description: + - List of Access Control Entries (ACEs) for this Access Control List (ACL). + type: list + elements: dict + suboptions: + name: + description: + - Filter term name + type: str + required: true + grant: + description: + - Action to take after matching condition (allow, discard/reject) + type: str + choices: [permit, deny] + source: + type: dict + description: + - Specifies the source for the filter + suboptions: + address: + description: + - IP source address to use for the filter + type: raw + prefix_list: + description: + - IP source prefix list to use for the filter + type: list + elements: dict + suboptions: + name: + description: Name of the list + 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 + range: + description: + - Match only packets in the range of port numbers + type: dict + suboptions: + start: + description: + - Specify the start of the port range + type: int + end: + description: + - Specify the end of the port range + type: int + destination: + type: dict + description: + - Specifies the destination for the filter + suboptions: + address: + description: + - Match IP destination address + type: raw + prefix_list: + description: + - Match IP destination prefixes in named list + type: list + elements: dict + suboptions: + name: + description: Name of the list + type: str + port_protocol: + description: + - Specify the destination port or protocol. + type: dict + suboptions: + eq: + description: + - Match only packets 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: int + end: + description: + - Specify the end of the port range + type: int + protocol: + description: + - Specify the protocol to match. + - Refer to vendor documentation for valid values. + type: str + protocol_options: + description: All possible suboptions for the protocol chosen. + type: dict + suboptions: + icmp: + description: ICMP protocol options. + type: dict + suboptions: + 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 + 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 + net_redirect: + description: Network redirect + type: bool + net_tos_redirect: + description: Net redirect for TOS + type: bool + network_unknown: + description: Network unknown + type: bool + port_unreachable: + description: Port unreachable + 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_route_failed: + description: Source route failed + type: bool + time_exceeded: + description: All time exceeded. + type: bool + ttl_exceeded: + description: TTL exceeded + 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 Junos device + by executing the command B(show firewall). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state the configuration should be left in + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# admin# show firewall + +- name: Merge JUNOS acl + junipernetworks.junos.junos_acls: + config: + - afi: ipv4 + acls: + - name: allow_ssh_acl + aces: + - name: ssh_rule + source: + port_protocol: + eq: ssh + protocol: tcp + state: merged + +# After state: +# ------------- +# admin# show firewall +# family inet { +# filter allow_ssh_acl { +# term ssh_rule { +# from { +# protocol tcp; +# source-port ssh; +# } +# } +# } +# } + +""" +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: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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", "rendered", ("config",)), + ("state", "overridden", ("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/ansible_collections/junipernetworks/junos/plugins/modules/junos_banner.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_banner.py new file mode 100644 index 000000000..d57f8f6aa --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_banner.py @@ -0,0 +1,194 @@ +#!/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: junos_banner +author: Ganesh Nalawade (@ganeshrn) +short_description: Manage multiline banners on Juniper JUNOS devices +description: +- This will configure both login and motd banners on network devices. It allows playbooks + to add or remote banner text from the active running configuration. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + banner: + description: + - Specifies which banner that should be configured on the remote device. Value + C(login) indicates system login message prior to authenticating, C(motd) is + login announcement after successful authentication. + type: str + required: true + choices: + - login + - motd + text: + description: + - The banner text that should be present in the remote device running configuration. This + argument accepts a multiline string, with no empty lines. Requires I(state=present). + type: str + state: + description: + - Specifies whether or not the configuration is present in the current devices + active running configuration. + type: str + default: present + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + type: bool + default: yes +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: configure the login banner + junipernetworks.junos.junos_banner: + banner: login + text: | + this is my login banner + that contains a multiline + string + state: present + +- name: remove the motd banner + junipernetworks.junos.junos_banner: + banner: motd + state: absent + +- name: deactivate the motd banner + junipernetworks.junos.junos_banner: + banner: motd + state: present + active: false + +- name: activate the motd banner + junipernetworks.junos.junos_banner: + banner: motd + state: present + active: true + +- name: Configure banner from file + junipernetworks.junos.junos_banner: + banner: motd + text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}" + state: present +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: str + sample: > + [edit system login] + + message \"this is my login banner\"; +""" +import collections + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + map_obj_to_ele, + map_params_to_obj, + tostring, +) + + +USE_PERSISTENT_CONNECTION = True + + +def validate_param_values(module, obj): + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(module.params.get(key), module) + + +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"]), + active=dict(default=True, type="bool"), + ) + + required_if = [("state", "present", ("text",))] + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + warnings = list() + result = {"changed": False} + + if warnings: + result["warnings"] = warnings + + top = "system/login" + + param_to_xpath_map = collections.OrderedDict() + + param_to_xpath_map.update( + [ + ( + "text", + { + "xpath": "message" if module.params["banner"] == "login" else "announcement", + "leaf_only": True, + }, + ), + ], + ) + + validate_param_values(module, param_to_xpath_map) + + want = map_params_to_obj(module, param_to_xpath_map) + ele = map_obj_to_ele(module, want, top) + + with locked_config(module): + diff = load_config(module, tostring(ele), warnings, action="merge") + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_address_family.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_address_family.py new file mode 100644 index 000000000..b40e5e816 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_address_family.py @@ -0,0 +1,1651 @@ +#!/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 junos_bgp_address_family +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_bgp_address_family +version_added: 1.3.0 +short_description: Manage BGP Address Family attributes of interfaces on Junos devices. +description: Manage BGP Address Family attributes of interfaces on Junos network devices. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show protocols bgp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + config: + description: The provided link BGP address family dictionary. + type: dict + suboptions: + address_family: &address_family + description: Enable address family and enter its config mode. + type: list + elements: dict + suboptions: + afi: + description: address family. + type: str + choices: + - evpn + - inet + - inet-mdt + - inet-mvpn + - inet-vpn + - inet6 + - inet6-mvpn + - inet6-vpn + - iso-vpn + - l2vpn + - route-target + - traffic-engineering + af_type: + description: Address family type for ipv4. + type: list + elements: dict + suboptions: + type: + description: Specify type of NLRI. + type: str + choices: + - any + - flow + - labeled-unicast + - multicast + - segment-routing-te + - unicast + - signaling + - auto-discovery-mspw + - auto-discovery-only + set: + description: Set NLRI. + type: bool + accepted_prefix_limit: + description: Specify limit for maximum number of prefixes accepted from a peer. + type: dict + suboptions: + maximum: + description: Specify maximum number of prefixes accepted from a peer. + type: int + teardown: + description: Clear peer connection on reaching limit. + type: bool + limit_threshold: + description: Specify teardown percentage of prefix-limit to start warnings. + type: int + idle_timeout: + description: Set idle timeout node. + type: bool + idle_timeout_value: + description: Specify timeout before attempting to restart peer. + type: int + forever: + description: Idle the peer until the user intervenes. + type: bool + add_path: + description: Advertise multiple paths to peer. + type: dict + suboptions: + receive: + description: Receive multiple paths from peer. + type: bool + send: + description: Send multiple paths to peer. + type: dict + suboptions: + include_backup_path: + description: Specify number of backup paths to advertise. + type: int + multipath: + description: Include only multipath contributor routes. + type: bool + path_count: + description: Include only multipath contributor routes. + type: int + required: true + path_selection_mode: + description: Configure how to select add-path routes. + type: dict + suboptions: + all_paths: + description: Advertise all paths allowed by path count. + type: bool + equal_cost_paths: + description: Advertise equal cost paths. + type: bool + prefix_policy: + description: Perform add-path only for prefixes that match policy. + type: str + aggregate_label: + description: Aggregate labels of incoming routes with the same FEC. + type: dict + suboptions: + set: + description: Set Aggregate labels of incoming routes with the same FEC + type: bool + community: + description: Community to identify the FEC of incoming routesC. + type: str + aigp: + description: Allow sending and receiving of AIGP attribute. + type: dict + suboptions: + set: + description: Set AIGP. + type: bool + disable: + description: Dn not allow sending and receiving of AIGP attribute. + type: bool + damping: + description: Enable route flap damping. + type: bool + defer_initial_multipath_build: + description: Defer initial multipath build until EOR is received. + type: dict + suboptions: + set: + description: Set defer initial multipath build. + type: bool + maximum_delay: + description: Max delay(sec) multipath build after peer is up. + type: int + delay_route_advertisements: + description: Delay route updates for this family until FIB-sync. + type: dict + suboptions: + set: + description: Set delay route advertisements. + type: bool + max_delay_route_age: + description: Set max delay advertisement route age. + type: int + max_delay_routing_uptime: + description: Set max delay advertisement route age. + type: int + min_delay_inbound_convergence: + description: Set min delayadvertisement after source-peer sent all routes. + type: int + min_delay_routing_uptime: + description: Set min delay advertisement route age. + type: int + entropy_label: + description: Use entropy label for entropy label capable BGP LSPs. + type: dict + suboptions: + set: + description: Set entropy-label attribute. + type: bool + import: + description: Policy to select BGP LSPs to use entropy label. + type: str + no_next_hop_validation: + description: Don't validate next hop field against route next hop. + type: bool + explicit_null: + description: Advertise explicit null. + type: dict + suboptions: + set: + description: Set explicit-null attribute. + type: bool + connected_only: + description: Advertise explicit null only for connected routes. + type: bool + extended_nexthop: + description: Enable extended nexthop encoding. + type: bool + extended_nexthop_color: + description: Resolve using extended color nexthop. + type: bool + graceful_restart_forwarding_state_bit: + description: Specify BGP graceful restart options. + type: str + choices: ['from-fib', 'set'] + local_ipv4_address: + description: Specify local IPv4 address. + type: str + legacy_redirect_ip_action: + description: Configure legacy redirect to IP support. + type: dict + suboptions: + set: + description: Set the legacy-redirect-ip-action. + type: bool + send: + description: Advertise Redirect action as legacy redirect attribute. + type: bool + receive: + description: Accept legacy encoded redirect-to-ip action attribute + type: bool + loops: + description: Allow local AS in received AS paths. + type: int + no_install: + description: Dont install received routes in forwarding. + type: bool + no_validate: + description: Bypass validation procedure for routes that match policy. + type: str + output_queue_priority_expedited: + description: Expedited queue; highest priority. + type: bool + output_queue_priority_priority: + description: Output queue priority; higher is better. + type: int + per_group_label: + description: Advertise prefixes with unique labels per group. + type: bool + per_prefix_label: + description: Allocate a unique label to each advertised prefix. + type: bool + prefix_limit: + description: Limit maximum number of prefixes from a peer. + type: dict + suboptions: + maximum: + description: Specify maximum number of prefixes from a peer. + type: int + teardown: + description: Clear peer connection on reaching limit. + type: bool + limit_threshold: + description: Percentage of prefix-limit to start warnings. + type: int + idle_timeout: + description: Set idle timeout node. + type: bool + idle_timeout_value: + description: Specify timeout before attempting to restart peer. + type: int + forever: + description: Idle the peer until the user intervenes. + type: bool + resolve_vpn: + description: Install received NLRI in inet.3 also. + type: bool + rib: + description: Select table used by labeled unicast routes. + type: str + choices: ['inet.3'] + ribgroup_name: + description: Name of the routing table group. + type: str + route_refresh_priority_expedited: + description: Expedited queue; highest priority. + type: bool + route_refresh_priority_priority: + description: Output queue priority; higher is better. + type: int + secondary_independent_resolution: + description: Resolve FLOW routes in VRF table independent of VPN FLOW route. + type: bool + topology: + description: Multi topology routing tables. + type: list + elements: dict + suboptions: + name: + description: Specify topology name. + type: str + community: + description: Community to identify multi topology routes. + type: list + elements: str + withdraw_priority_expedited: + description: Expedited queue; highest priority. + type: bool + withdraw_priority_priority: + description: Output queue priority; higher is better. + type: int + strip_nexthop: + description: Strip the next-hop from the outgoing flow update. + type: bool + traffic_statistics: + description: Collect statistics for BGP label-switched paths + type: dict + suboptions: + set: + description: Set traffic-statistics. + type: bool + interval: + description: Time to collect statistics (seconds). + type: int + labeled_path: + description: Enable ingress labeled path statistics. + type: bool + file: + description: Statistics file options. + type: dict + suboptions: + filename: + description: Name of file in which to write trace information. + type: str + files: + description: Maximum number of trace files. + type: int + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + size: + description: Maximum trace file size. + type: int + world_readable: + description: Don't allow any user to read the log file. + type: bool + groups: + description: Specify address family config for groups. + type: list + elements: dict + suboptions: + name: + description: Specify name of the group + type: str + address_family: *address_family + neighbors: + description: Specify address family config per neighbor. + type: list + elements: dict + suboptions: + neighbor_address: + description: Specify neighbor address. + type: str + address_family: *address_family + state: + description: + - The state the configuration should be left in. + - State I(deleted) only removes BGP address family attributes that this modules + manages and does not negate the BGP neighbor address family completely. Thereby, preserving + address-family related configurations under BGP group neighbor context. + - To delete the address family associated to neighbor use M(junipernetworks.junos.junos_bgp_neighbor_address_family) + modules for prior cleanup. + - Refer to examples for more details. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show protocols bgp +# +# [edit] + +- name: Merge Junos BGP address family configuration + junipernetworks.junos.junos_bgp_address_family: + config: + address_family: + - afi: 'evpn' + af_type: + - type: 'signaling' + accepted_prefix_limit: + maximum: 20 + limit_threshold: 98 + idle_timeout_value: 2001 + damping: true + defer_initial_multipath_build: + maximum_delay: 2 + - afi: 'inet' + af_type: + - type: 'flow' + legacy_redirect_ip_action: + send: true + receive: true + loops: 4 + no_install: true + output_queue_priority_expedited: true + secondary_independent_resolution: true + + - type: 'unicast' + extended_nexthop: true + extended_nexthop_color: true + local_ipv4_address: '9.9.9.9' + + - type: 'labeled-unicast' + entropy_label: + no_next_hop_validation: true + explicit_null: + connected_only: true + per_prefix_label: true + per_group_label: true + prefix_limit: + maximum: 20 + limit_threshold: 99 + forever: true + resolve_vpn: true + rib: 'inet.3' + route_refresh_priority_expedited: true + route_refresh_priority_priority: 3 + + - type: 'any' + accepted_prefix_limit: + maximum: 20 + limit_threshold: 99 + idle_timeout_value: 2000 + damping: true + defer_initial_multipath_build: + maximum_delay: 2 + delay_route_advertisements: + max_delay_route_age: 20 + max_delay_routing_uptime: 32000 + min_delay_inbound_convergence: 32000 + min_delay_routing_uptime: 23000 + graceful_restart_forwarding_state_bit: 'from-fib' + state: merged + +# After state +# ----------- +# +# admin# show protocols bgp +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } +# Using replaced +# +# Before state +# ------------ +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# bgp-error-tolerance { +# malformed-route-limit 40000000; +# } +# authentication-algorithm md5; +# advertise-bgp-static { +# policy static-to-bgp; +# } +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +- name: Replace existing Junos BGP address family config with provided config + junipernetworks.junos.junos_bgp_address_family: + config: + address_family: + - afi: 'evpn' + af_type: + - type: 'signaling' + accepted_prefix_limit: + maximum: 21 + limit_threshold: 99 + idle_timeout_value: 2002 + delay_route_advertisements: + max_delay_route_age: 20 + max_delay_routing_uptime: 32000 + min_delay_inbound_convergence: 32000 + min_delay_routing_uptime: 23000 + damping: true + state: replaced + +# After state +# ----------- +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# bgp-error-tolerance { +# malformed-route-limit 40000000; +# } +# authentication-algorithm md5; +# advertise-bgp-static { +# policy static-to-bgp; +# } +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 21; +# teardown 99 idle-timeout 2002; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# } +# } +# Using overridden +# +# Before state +# ------------ +# +# admin# show protocols bgp +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +- name: Override Junos BGP address family config + junipernetworks.junos.junos_bgp_address_family: + config: + address_family: + - afi: 'evpn' + af_type: + - type: 'signaling' + accepted_prefix_limit: + maximum: 21 + limit_threshold: 99 + idle_timeout_value: 2002 + delay_route_advertisements: + max_delay_route_age: 20 + max_delay_routing_uptime: 32000 + min_delay_inbound_convergence: 32000 + min_delay_routing_uptime: 23000 + damping: true + state: overridden + +# After state +# ----------- +# +# admin# show protocols bgp +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 21; +# teardown 99 idle-timeout 2002; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# } +# } + +# Using deleted +# +# Before state +# ------------ +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +- name: Delete Junos BGP address family config based on the afi + junipernetworks.junos.junos_bgp_address_family: + config: + address_family: + - afi: 'inet' + state: deleted + +# After state +# ----------- +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +# Using deleted +# +# Before state +# ------------ +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +- name: Delete complete Junos BGP address family config + junipernetworks.junos.junos_bgp_address_family: + config: + state: deleted + +# After state +# ----------- +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; + + +# Using gathered +# +# Before state +# ------------ +# +# admin# show protocols bgp +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# family inet { +# unicast { +# local-ipv4-address 9.9.9.9; +# extended-nexthop; +# extended-nexthop-color; +# } +# flow { +# loops 4; +# no-install; +# output-queue-priority expedited; +# legacy-redirect-ip-action { +# receive; +# send; +# } +# secondary-independent-resolution; +# } +# any { +# accepted-prefix-limit { +# maximum 20; +# teardown 99 idle-timeout 2000; +# } +# damping; +# delay-route-advertisements { +# minimum-delay { +# routing-uptime 23000; +# inbound-convergence 32000; +# } +# maximum-delay { +# route-age 20; +# routing-uptime 32000; +# } +# } +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# graceful-restart { +# forwarding-state-bit from-fib; +# } +# } +# labeled-unicast { +# prefix-limit { +# maximum 20; +# teardown 99 idle-timeout forever; +# } +# route-refresh-priority priority 3; +# per-prefix-label; +# per-group-label; +# rib { +# inet.3; +# } +# explicit-null connected-only; +# resolve-vpn; +# entropy-label { +# no-next-hop-validation; +# } +# } +# } +# family evpn { +# signaling { +# accepted-prefix-limit { +# maximum 20; +# teardown 98 idle-timeout 2001; +# } +# damping; +# defer-initial-multipath-build { +# maximum-delay 2; +# } +# } +# } + +- name: Gather Junos BGP address family config + junipernetworks.junos.junos_bgp_address_family: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "address_family": [ +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2001, +# "limit_threshold": 98, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "type": "signaling" +# } +# ], +# "afi": "evpn" +# }, +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2000, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "delay_route_advertisements": { +# "max_delay_route_age": 20, +# "max_delay_routing_uptime": 32000, +# "min_delay_inbound_convergence": 32000, +# "min_delay_routing_uptime": 23000 +# }, +# "graceful_restart_forwarding_state_bit": "from-fib", +# "type": "any" +# }, +# { +# "legacy_redirect_ip_action": { +# "receive": true, +# "send": true +# }, +# "loops": 4, +# "no_install": true, +# "output_queue_priority_expedited": true, +# "secondary_independent_resolution": true, +# "type": "flow" +# }, +# { +# "entropy_label": { +# "no_next_hop_validation": true +# }, +# "explicit_null": { +# "connected_only": true +# }, +# "per_group_label": true, +# "per_prefix_label": true, +# "prefix_limit": { +# "forever": true, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "resolve_vpn": true, +# "rib": "inet.3", +# "route_refresh_priority_priority": 3, +# "type": "labeled-unicast" +# }, +# { +# "extended_nexthop": true, +# "extended_nexthop_color": true, +# "local_ipv4_address": "9.9.9.9", +# "type": "unicast" +# } +# ], +# "afi": "inet" +# } +# ] +# } +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <protocols> +# <bgp> +# <preference>2</preference> +# <hold-time>5</hold-time> +# <advertise-inactive/> +# <out-delay>10</out-delay> +# <family> +# <inet> +# <unicast> +# <local-ipv4-address>9.9.9.9</local-ipv4-address> +# <extended-nexthop/> +# <extended-nexthop-color/> +# </unicast> +# <flow> +# <loops> +# <loops>4</loops> +# </loops> +# <no-install/> +# <output-queue-priority> +# <expedited/> +# </output-queue-priority> +# <legacy-redirect-ip-action> +# <receive/> +# <send/> +# </legacy-redirect-ip-action> +# <secondary-independent-resolution/> +# </flow> +# <any> +# <accepted-prefix-limit> +# <maximum>20</maximum> +# <teardown> +# <limit-threshold>99</limit-threshold> +# <idle-timeout> +# <timeout>2000</timeout> +# </idle-timeout> +# </teardown> +# </accepted-prefix-limit> +# <damping/> +# <delay-route-advertisements> +# <minimum-delay> +# <routing-uptime>23000</routing-uptime> +# <inbound-convergence>32000</inbound-convergence> +# </minimum-delay> +# <maximum-delay> +# <route-age>20</route-age> +# <routing-uptime>32000</routing-uptime> +# </maximum-delay> +# </delay-route-advertisements> +# <defer-initial-multipath-build> +# <maximum-delay>2</maximum-delay> +# </defer-initial-multipath-build> +# <graceful-restart> +# <forwarding-state-bit>from-fib</forwarding-state-bit> +# </graceful-restart> +# </any> +# <labeled-unicast> +# <prefix-limit> +# <maximum>20</maximum> +# <teardown> +# <limit-threshold>99</limit-threshold> +# <idle-timeout> +# <forever/> +# </idle-timeout> +# </teardown> +# </prefix-limit> +# <route-refresh-priority> +# <priority>3</priority> +# </route-refresh-priority> +# <per-prefix-label/> +# <per-group-label/> +# <rib> +# <inet.3/> +# </rib> +# <explicit-null> +# <connected-only/> +# </explicit-null> +# <resolve-vpn/> +# <entropy-label> +# <no-next-hop-validation/> +# </entropy-label> +# </labeled-unicast> +# </inet> +# <evpn> +# <signaling> +# <accepted-prefix-limit> +# <maximum>20</maximum> +# <teardown> +# <limit-threshold>98</limit-threshold> +# <idle-timeout> +# <timeout>2001</timeout> +# </idle-timeout> +# </teardown> +# </accepted-prefix-limit> +# <damping/> +# <defer-initial-multipath-build> +# <maximum-delay>2</maximum-delay> +# </defer-initial-multipath-build> +# </signaling> +# </evpn> +# </family> +# </bgp> +# <ospf3> +# <area> +# <name>0.0.0.100</name> +# <stub> +# <default-metric>200</default-metric> +# </stub> +# <interface> +# <name>so-0/0/0.0</name> +# <metric>5</metric> +# <priority>3</priority> +# </interface> +# </area> +# </ospf3> +# </protocols> +# <routing-options> +# <static> +# <route> +# <name>172.16.17.0/24</name> +# <discard /> +# </route> +# </static> +# <router-id>10.200.16.75</router-id> +# <autonomous-system> +# <as-number>65432</as-number> +# </autonomous-system> +# </routing-options> +# </configuration> +# </rpc-reply> + + +- name: Parsed the bgp address family running config to get the facts + junipernetworks.junos.junos_bgp_address_family: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "address_family": [ +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2001, +# "limit_threshold": 98, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "type": "signaling" +# } +# ], +# "afi": "evpn" +# }, +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2000, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "delay_route_advertisements": { +# "max_delay_route_age": 20, +# "max_delay_routing_uptime": 32000, +# "min_delay_inbound_convergence": 32000, +# "min_delay_routing_uptime": 23000 +# }, +# "graceful_restart_forwarding_state_bit": "from-fib", +# "type": "any" +# }, +# { +# "legacy_redirect_ip_action": { +# "receive": true, +# "send": true +# }, +# "loops": 4, +# "no_install": true, +# "output_queue_priority_expedited": true, +# "secondary_independent_resolution": true, +# "type": "flow" +# }, +# { +# "entropy_label": { +# "no_next_hop_validation": true +# }, +# "explicit_null": { +# "connected_only": true +# }, +# "per_group_label": true, +# "per_prefix_label": true, +# "prefix_limit": { +# "forever": true, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "resolve_vpn": true, +# "rib": "inet.3", +# "route_refresh_priority_priority": 3, +# "type": "labeled-unicast" +# }, +# { +# "extended_nexthop": true, +# "extended_nexthop_color": true, +# "local_ipv4_address": "9.9.9.9", +# "type": "unicast" +# } +# ], +# "afi": "inet" +# } +# ] +# } +# Using rendered +# +# +- name: Render the commands for provided configuration + junipernetworks.junos.junos_bgp_address_family: + config: + address_family: + - afi: 'evpn' + af_type: + - type: 'signaling' + accepted_prefix_limit: + maximum: 20 + limit_threshold: 98 + idle_timeout_value: 2001 + damping: true + defer_initial_multipath_build: + maximum_delay: 2 + - afi: 'inet' + af_type: + - type: 'flow' + legacy_redirect_ip_action: + send: true + receive: true + loops: 4 + no_install: true + output_queue_priority_expedited: true + secondary_independent_resolution: true + + - type: 'unicast' + extended_nexthop: true + extended_nexthop_color: true + local_ipv4_address: '9.9.9.9' + + - type: 'labeled-unicast' + entropy_label: + no_next_hop_validation: true + explicit_null: + connected_only: true + per_prefix_label: true + per_group_label: true + prefix_limit: + maximum: 20 + limit_threshold: 99 + forever: true + resolve_vpn: true + rib: 'inet.3' + route_refresh_priority_expedited: true + route_refresh_priority_priority: 3 + + - type: 'any' + accepted_prefix_limit: + maximum: 20 + limit_threshold: 99 + idle_timeout_value: 2000 + damping: true + defer_initial_multipath_build: + maximum_delay: 2 + delay_route_advertisements: + max_delay_route_age: 20 + max_delay_routing_uptime: 32000 + min_delay_inbound_convergence: 32000 + min_delay_routing_uptime: 23000 + graceful_restart_forwarding_state_bit: 'from-fib' + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": "<nc:protocols xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:bgp><nc:family><nc:evpn><nc:signaling><nc:accepted-prefix-limit><nc:maximum>20</nc:maximum> +# <nc:teardown><nc:limit-threshold>98</nc:limit-threshold><nc:idle-timeout><nc:timeout>2001</nc:timeout> +# </nc:idle-timeout></nc:teardown></nc:accepted-prefix-limit><nc:damping/><nc:defer-initial-multipath-build> +# <nc:maximum-delay>2</nc:maximum-delay></nc:defer-initial-multipath-build></nc:signaling> +# </nc:evpn><nc:inet><nc:flow><nc:legacy-redirect-ip-action><nc:send/><nc:receive/> +# </nc:legacy-redirect-ip-action><nc:loops>4</nc:loops><nc:no-install/> +# <nc:output-queue-priority><nc:expedited/></nc:output-queue-priority> +# <nc:secondary-independent-resolution/></nc:flow><nc:unicast><nc:extended-nexthop/> +# <nc:extended-nexthop-color/><nc:local-ipv4-address>9.9.9.9</nc:local-ipv4-address> +# </nc:unicast><nc:labeled-unicast><nc:entropy-label><nc:no-next-hop-validation/> +# </nc:entropy-label><nc:explicit-null><nc:connected-only/></nc:explicit-null> +# <nc:per-prefix-label/><nc:per-group-label/><nc:prefix-limit><nc:maximum>20</nc:maximum> +# <nc:teardown>99<nc:idle-timeout><nc:forever/></nc:idle-timeout></nc:teardown> +# </nc:prefix-limit><nc:resolve-vpn/><nc:rib><nc:inet.3/></nc:rib><nc:route-refresh-priority> +# <nc:expedited/><nc:priority>3</nc:priority></nc:route-refresh-priority></nc:labeled-unicast> +# <nc:any><nc:accepted-prefix-limit><nc:maximum>20</nc:maximum><nc:teardown> +# <nc:limit-threshold>99</nc:limit-threshold><nc:idle-timeout><nc:timeout>2000</nc:timeout> +# </nc:idle-timeout></nc:teardown></nc:accepted-prefix-limit><nc:damping/> +# <nc:defer-initial-multipath-build><nc:maximum-delay>2</nc:maximum-delay> +# </nc:defer-initial-multipath-build><nc:delay-route-advertisements> +# <nc:maximum-delay><nc:route-age>20</nc:route-age><nc:routing-uptime>32000</nc:routing-uptime> +# </nc:maximum-delay><nc:minimum-delay><nc:inbound-convergence>32000</nc:inbound-convergence> +# <nc:routing-uptime>23000</nc:routing-uptime></nc:minimum-delay></nc:delay-route-advertisements> +# <nc:graceful-restart><nc:forwarding-state-bit>from-fib</nc:forwarding-state-bit> +# </nc:graceful-restart></nc:any></nc:inet></nc:family></nc:bgp></nc:protocols>" + +""" +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: ['<nc:protocols xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:bgp><nc:family><nc:evpn><nc:signaling><nc:accepted-prefix-limit> + <nc:maximum>21</nc:maximum><nc:teardown><nc:limit-threshold>99</nc:limit-threshold> + <nc:idle-timeout><nc:timeout>2002</nc:timeout></nc:idle-timeout> + </nc:teardown></nc:accepted-prefix-limit><nc:damping/> + <nc:delay-route-advertisements><nc:maximum-delay> + <nc:route-age>20</nc:route-age><nc:routing-uptime>32000</nc:routing-uptime> + </nc:maximum-delay><nc:minimum-delay><nc:inbound-convergence>32000</nc:inbound-convergence> + <nc:routing-uptime>23000</nc:routing-uptime></nc:minimum-delay></nc:delay-route-advertisements> + </nc:signaling></nc:evpn></nc:family></nc:bgp></nc:protocols>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.bgp_address_family.bgp_address_family import ( + Bgp_address_family, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Bgp_address_familyArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Bgp_address_family(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_global.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_global.py new file mode 100644 index 000000000..de4715ff0 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_bgp_global.py @@ -0,0 +1,1763 @@ +#!/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 junos_bgp_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_bgp_global +version_added: "1.3.0" +short_description: Manages BGP Global configuration on devices running Juniper JUNOS. +description: + - This module manages global bgp configuration on devices running Juniper JUNOS. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show protocols bgp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + config: + description: A list of BGP process configuration. + type: dict + suboptions: + as_number: + description: Specify Autonomous system number. + type: str + loops: + description: Specify maximum number of times this AS can be in an AS path. + type: int + asdot_notation: + description: Enable AS-Dot notation to display true 4 byte AS numbers. + type: bool + accept_remote_nexthop: &accept_remote_nexthop + description: Allow import policy to specify a non-directly connected next-hop. + type: bool + add_path_display_ipv4_address: &add_path_display_ipv4_address + description: Display add-path path-id in IPv4 address format. + type: bool + advertise_bgp_static: &advertise_bgp_static + description: Advertise bgp-static routes. + type: dict + suboptions: + set: + description: Set Advertise bgp-static routes. + type: bool + policy: + description: Specify static route advertisement policy. + type: str + advertise_external: &advertise_external + description: Advertise best external routes. + type: dict + suboptions: + set: + description: Set Advertise best external routes. + type: bool + conditional: + description: Route matches active route upto med-comparison rule. + type: bool + advertise_from_main_vpn_tables: + description: Advertise VPN routes from bgp.Xvpn.0 tables in master instance. + type: bool + advertise_inactive: &advertise_inactive + description: Advertise inactive routes. + type: bool + advertise_peer_as: &advertise_peer_as + description: Advertise routes received from the same autonomous system. + type: bool + authentication_algorithm: &authentication_algorithm + description: Specify authentication algorithm name. + type: str + choices: ["aes-128-cmac-96", "hmac-sha-1-96", "md5"] + authentication_key: &authentication_key + description: Specify MD5 authentication key. + type: str + authentication_key_chain: &authentication_key_chain + description: Specify authentication key chain name. + type: str + bfd_liveness_detection: &bfd_liveness_detection + description: Bidirectional Forwarding Detection (BFD) options. + type: dict + suboptions: + authentication: + description: Authentication options. + type: dict + suboptions: + algorithm: + description: Specify algorithm name. + type: str + choices: ["keyed-md5", "keyed-sha-1", "meticulous-keyed-md5", "meticulous-keyed-sha-1", "simple-password"] + key_chain: + description: Specify Key chain name. + type: str + loose_check: + description: Verify authentication only if authentication is negotiated. + type: bool + detection_time: + description: Specify Detection-time optionss. + type: dict + suboptions: + threshold: + description: Specify high detection-time triggering a trap (milliseconds). + type: int + holddown_interval: + description: Specify time to hold the session-UP notification to the client. + type: int + minimum_interval: + description: Specify minimum transmit and receive interval. + type: int + minimum_receive_interval: + description: Specify minimum receive interval. + type: int + multiplier: + description: Specify detection time multiplier. + type: int + no_adaptation: + description: Disable adaptation. + type: bool + session_mode: + description: BFD single-hop or multihop session-mode. + type: str + choices: ["automatic", "multihop", "single-hop"] + transmit_interval: + description: Transmit-interval options. + type: dict + suboptions: + minimum_interval: + description: Specify Minimum transmit interval. + type: int + threshold: + description: Specify high transmit interval triggering a trap. + type: int + version: + description: Specify BFD protocol version number. + type: str + choices: ["0", "1", "automatic"] + bgp_error_tolerance: &bgp_error_tolerance + description: Handle BGP malformed updates softly. + type: dict + suboptions: + set: + description: Set BGP malformed updates softly. + type: bool + malformed_route_limit: + description: Maximum number of malformed routes from a peer. + type: int + malformed_update_log_interval: + description: Time used when logging malformed update. + type: int + no_malformed_route_limit: + description: Specify no malformed route limit. + type: bool + bmp: &bmp + description: Specific settings to override the routing-options settings. + type: dict + suboptions: + monitor: + description: Enable/Disable monitoring. + type: bool + route_monitoring: + description: Control route monitoring settings. + type: dict + suboptions: + none: + description: Do not send route montoring messages. + type: bool + post_policy: + description: Send post policy route montoring messages. + type: bool + post_policy_exclude_non_eligible: + description: Send post policy route montoring messages and exclude unresolved routes, etc. + type: bool + pre_policy: + description: Send pre policy route montoring messages. + type: bool + post_policy_exclude_non_feasible: + description: Send pre policy route montoring messages and exclude looped routes, etc. + type: bool + cluster_id: &cluster_id + description: Specify cluster identifier. + type: str + damping: &damping + description: Enable route flap damping. + type: bool + description: &description + description: Specify text description. + type: str + disable: + description: Disable BGP. + type: bool + egress_te: &egress_te + description: Use Egress Peering traffic engineering. + type: dict + suboptions: + set: + description: Set the attribute. + type: bool + backup_path: + description: The 'egress-te-backup-paths template' to use for this peer. + type: str + egress_te_backup_paths: + description: Backup-path for Egress-TE peer interface failure. + type: dict + suboptions: + templates: + description: Specify Backup-path template. + type: list + elements: dict + suboptions: + path_name: + description: Name of Egress-TE backup path. + type: str + required: true + ip_forward: + description: Use IP-forward backup path for Egress TE. + type: dict + suboptions: + set: + description: Set use IP-forward backup path for Egress TE. + type: bool + rti_name: + description: Routing-instance to use as IP forward backup-path. + type: str + peers: + description: Specify address of BGP peer to use as backup next-hop. + type: list + elements: str + remote_nexthop: + description: Specify address of remote-nexthop to use as backup path. + type: str + egress_te_set_segment: + description: Configure BGP-Peer-Set segment. + type: list + elements: dict + suboptions: + name: + description: The BGP-Peer-Set segment name. + type: str + required: true + label: + description: Backup segment label value from static label pool. + type: int + egress_te_backup_segment_label: + description: BGP-Peer-Set SID label value from static label pool. + type: int + egress_te_sid_stats: + description: Create BGP-Peer-SID sensor. + type: bool + enforce_first_as: &enforce_first_as + description: Enforce neighbor AS is the first AS in AS-PATH attribute (EBGP). + type: bool + export: &export + description: Specify export policy. + type: str + forwarding_context: &forwarding_context + description: Specify routing-instance used for data-forwarding and transport-session. + type: str + graceful_restart: &graceful_restart + description: BGP graceful restart options. + type: dict + suboptions: + set: + description: Set BGP graceful restart options. + type: bool + disable: + description: Disable graceful restart. + type: bool + dont_help_shared_fate_bfd_down: + description: Honor BFD-Down(C=0) if GR-restart not in progress. + type: bool + forwarding_state_bit: + description: Control forwarding-state flag negotiation. + type: dict + suboptions: + as_rr_client: + description: As for a route reflector client. + type: bool + from_fib: + description: Always use state of associated FIB(s). + type: bool + long_lived: + description: Long-lived graceful restart options. + type: dict + suboptions: + advertise_to_non_llgr_neighbor: + description: Advertise stale routes to non-LLGR neighbors. + type: dict + suboptions: + set: + description: Set Advertise stale routes to non-LLGR neighbors. + type: bool + omit_no_export: + description: Set Advertise stale routes to non-LLGR neighbors. + type: bool + receiver_disable: + description: Disable receiver (helper) functionality. + type: bool + restart_time: + description: Restart time used when negotiating with a peer. + type: int + stale_routes_time: + description: Maximum time for which stale routes are kept. + type: int + hold_time: &hold_time + description: Specify hold time used when negotiating with a peer. + type: int + holddown_all_stale_labels: + description: Hold all BGP stale-labels, facilating make-before-break for new label advertisements. + type: bool + idle_after_switch_over: &idle_after_switch_over + description: Stop peer session from coming up after nonstop-routing switch-over. + type: dict + suboptions: + timeout: + description: Specify timeout value, in seconds, for starting peer after switch over. + type: int + forever: + description: Idle the peer until the user intervenes. + type: bool + import: &import + description: Specify import policy. + type: list + elements: str + include_mp_next_hop: &include_mp_next_hop + description: Include NEXT-HOP attribute in multiprotocol updates. + type: bool + ipsec_sa: &ipsec_sa + description: Specify IPSec SA name. + type: str + keep: &keep + description: Specify how to retain routes in the routing table. + type: str + choices: ["all", "none"] + local_address: &local_address + description: Specify Address of local end of BGP session. + type: str + local_as: &local_as + description: Local autonomous system number. + type: dict + suboptions: + as_num: + description: Autonomous system number in plain number or (asdot notation) format. + type: str + required: true + alias: + description: Treat this AS as an alias to the system AS. + type: bool + loops: + description: Maximum number of times this AS can be in an AS path. + type: int + no_prepend_global_as: + description: Maximum number of times this AS can be in an AS path. + type: bool + private: + description: Hide this local AS in paths learned from this peering. + type: bool + local_interface: &local_interface + description: Specify Local interface for IPv6 link local EBGP peering. + type: str + local_preference: &local_preference + description: Specify value of LOCAL_PREF path attribute. + type: str + log_updown: &log_updown + description: Enable log a message for peer state transitions. + type: bool + metric_out: &metric_out + description: Specify route metric sent in MED. + type: dict + suboptions: + metric_value: + description: Specify metric value. + type: int + igp: + description: Track the IGP metric. + type: dict + suboptions: + set: + description: Set track the IGP metric. + type: bool + metric_offset: + description: Specify metric offset for MED. + type: int + delay_med_update: + description: Delay updating MED when IGP metric increases. + type: bool + minimum_igp: + description: Track the minimum IGP metric. + type: dict + suboptions: + set: + description: Set track the minimum IGP metric. + type: bool + metric_offset: + description: Specify metric offset for MED. + type: int + mtu_discovery: &mtu_discovery + description: Enable TCP path MTU discovery. + type: bool + multihop: &multihop + description: Configure an EBGP multihop session. + type: dict + suboptions: + set: + description: Set an EBGP multihop session. + type: bool + no_nexthop_change: + description: Do not change next hop to self in advertisements. + type: bool + ttl: + description: TTL value for the session. + type: int + multipath: &multipath + description: Allow load sharing among multiple BGP paths. + type: dict + suboptions: + set: + description: Set allow load sharing among multiple BGP paths. + type: bool + disable: + description: Disable Multipath. + type: bool + multiple_as: + description: Use paths received from different ASs. + type: bool + multiple_as_disable: + description: Disable multipath. + type: bool + multipath_build_priority: + description: Configure the multipath build priority. + type: str + choices: ["low", "medium"] + no_advertise_peer_as: &no_advertise_peer_as + description: Allows to not advertise routes received from the same autonomous system. + type: bool + no_aggregator_id: &no_aggregator_id + description: Set router ID in aggregator path attribute to 0. + type: bool + no_client_reflect: &no_client_reflect + description: Disable intracluster route redistribution. + type: bool + no_precision_timers: &no_precision_timers + description: Specify not to use precision timers for scheduling keepalives. + type: bool + out_delay: &out_delay + description: Specify how long before exporting routes from routing table. + type: int + outbound_route_filter: &outbound_route_filter + description: Dynamically negotiated cooperative route filtering. + type: dict + suboptions: + bgp_orf_cisco_mode: + description: Using BGP ORF capability code 130 and Prefix ORF type 128. + type: bool + prefix_based: + description: Prefix-based outbound route filtering. + type: dict + suboptions: + set: + description: Set prefix-based outbound route filtering. + type: bool + accept: + description: Honor Prefix-based ORFs from remote peers. + type: dict + suboptions: + set: + description: Set honor Prefix-based ORFs from remote peers. + type: bool + inet: + description: Honor IPv4 prefix filters. + type: bool + inet6: + description: Honor IPv6 prefix filters. + type: bool + output_queue_priority: + description: BGP output queue priority scheduler for updates. + type: dict + suboptions: + defaults: + description: Map policy's priority class and BGP output-queue. + type: dict + suboptions: + high: + description: Assign the 'high' priority class to this output-queue. + type: dict + suboptions: + expedited: + description: Expedited queue; highest priority. + type: bool + priority: + description: Specify output queue priorit. + type: int + low: + description: Assign the 'low' priority class to this output-queue. + type: dict + suboptions: + expedited: + description: Expedited queue; highest priority. + type: bool + priority: + description: Specify output queue priorit. + type: int + medium: + description: Assign the 'medium' priority class to this output-queue. + type: dict + suboptions: + expedited: + description: Expedited queue; highest priority. + type: bool + priority: + description: Specify output queue priorit. + type: int + expedited_update_tokens: + description: Expedited queue; highest priority for number of tokens. + type: int + priority_update_tokens: + description: Output queue priority; higher is better. + type: list + elements: dict + suboptions: + priority: + description: Specify the priority. + type: int + required: true + update_tokens: + description: Specify update_tokens. + type: int + required: true + passive: &passive + description: Specify to not send open messages to a peer. + type: bool + path_selection: + description: Configure path selection strategy. + type: dict + suboptions: + always_compare_med: + description: Compare MED on paths from different AS. + type: bool + as_path_ignore: + description: Ignore AS path comparison during path selection. + type: bool + cisco_non_deterministic: + description: Use Cisco IOS nondeterministic path selection algorithm. + type: bool + external_router_id: + description: Compare router ID on BGP externals. + type: bool + l2vpn_use_bgp_rules: + description: Use standard BGP rules during L2VPN path selection. + type: bool + med_plus_igp: + description: Add IGP cost to next-hop to MED before comparing MED values. + type: dict + suboptions: + set: + description: Set med-plus-igp attribute. + type: bool + igp_multiplier: + description: Specify multiplier for IGP cost to next-hop. + type: int + med_multiplier: + description: Specify Multiplier for MED. + type: int + peer_as: &peer_as + description: Specify Autonomous system number in plain number or 'higher 16bits'.'Lower 16 bits' format. + type: str + precision_timers: + description: Use precision timers for scheduling keepalives. + type: bool + preference: &preference + description: Specify preference value. + type: str + remove_private: &remove_private + description: Remove well-known private AS numbers. + type: dict + suboptions: + set: + description: Remove well-known private AS numbers. + type: bool + all: + description: Remove all private AS numbers and do not stop at the first public AS number. + type: bool + all_replace: + description: Specify private AS replacement. + type: bool + all_replace_nearest: + description: Use closest public AS number to replace a private AS number. + type: bool + no_peer_loop_check: + description: Remove peer loop-check. + type: bool + rfc6514_compliant_safi129: &rfc6514_compliant_safi129 + description: Specify Compliance with RFC6514 SAFI129 format. + type: bool + route_server_client: &route_server_client + description: Enable route server client behavior. + type: bool + send_addpath_optimization: + description: Enable BGP addpath advertisement optimization. + type: bool + snmp_options: + description: Customize SNMP behaviors specifically for BGP MIBs. + type: dict + suboptions: + backward_traps_only_from_established: + description: Limit traps for backward transitions to only those moving from Established state. + type: bool + emit_inet_address_length_in_oid: + description: Emit Length in OID for InetAddress MIB type. + type: bool + sr_preference_override: + description: Replace received segment routing traffic engineering preference value with override value. + type: str + stale_labels_holddown_period: + description: Specify duration (sec) MPLS labels allocated by BGP are kept after they go stale. + type: int + tcp_aggressive_transmission: &tcp_aggressive_transmission + description: Enable aggressive transmission of pure TCP ACKs and retransmissions. + type: bool + tcp_mss: &tcp_mss + description: Specify maximum TCP segment size. + type: int + traceoptions: &traceoptions + description: Configure trace options for BGP. + type: dict + suboptions: + file: + description: Specify trace file options. + type: dict + suboptions: + filename: + description: Specify name of file in which to write trace information. + type: str + required: true + files: + description: Specify maximum number of trace files. + type: int + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + world_readable: + description: Allow any user to read the log file. + type: bool + size: + description: Specify maximum trace file size. + type: int + flag: + description: Specify tracing parameters. + type: list + elements: dict + suboptions: + name: + description: specify event name + type: str + choices: + - 4byte-as + - add-path + - all + - bfd + - damping + - egress-te + - general + - graceful-restart + - keepalive + - normal + - nsr-synchronization + - open + - packets + - policy + - refresh + - route + - state + - task + - thread-io + - thread-update-io + - timer + - update + required: true + detail: + description: Trace detailed information. + type: bool + disable: + description: Disable this trace flag. + type: bool + receive: + description: Trace received packets. + type: bool + send: + description: Trace transmitted packets. + type: bool + filter: + description: Filter to apply to this flag. + type: dict + suboptions: + set: + description: Set filter to apply to this flag. + type: bool + match_on_prefix: + description: Specify filter based on prefix. + type: bool + policy: + description: Specify filter policy. + type: str + traffic_statistics_labeled_path: + description: Collect periodic ingress labeled statistics for BGP label-switched paths. + type: dict + suboptions: + file: + description: Specify statistics file options. + type: dict + suboptions: + filename: + description: Specify name of file in which to write trace information. + type: str + files: + description: Specify maximum number of trace files. + type: int + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + world_readable: + description: Allow any user to read the log file. + type: bool + size: + description: Specify maximum trace file size. + type: int + interval: + description: Specify time interval to collect statistics. + type: int + ttl: &ttl + description: Specify TTL value for the single-hop peer. + type: int + unconfigured_peer_graceful_restart: + description: Specify BGP unconfigured peer graceful restart options. + type: bool + vpn_apply_export: + description: Apply BGP export policy when exporting VPN routes. + type: bool + groups: + description: Specify name of the group. + type: list + elements: dict + suboptions: + name: + description: Specify the name of the group + type: str + accept_remote_nexthop: *accept_remote_nexthop + add_path_display_ipv4_address: *add_path_display_ipv4_address + advertise_bgp_static: *advertise_bgp_static + advertise_external: *advertise_external + advertise_inactive: *advertise_inactive + advertise_peer_as: *advertise_peer_as + allow: + description: Configure peer connections for specific networks. + type: list + elements: str + as_override: &as_override + description: Replace neighbor AS number with our AS number + type: bool + authentication_algorithm: *authentication_algorithm + authentication_key: *authentication_key + authentication_key_chain: *authentication_key_chain + bfd_liveness_detection: *bfd_liveness_detection + bgp_error_tolerance: *bgp_error_tolerance + bmp: *bmp + cluster_id: *cluster_id + damping: *damping + description: *description + egress_te: *egress_te + enforce_first_as: *enforce_first_as + export: *export + forwarding_context: *forwarding_context + graceful_restart: *graceful_restart + hold_time: *hold_time + idle_after_switch_over: *idle_after_switch_over + import: *import + include_mp_next_hop: *include_mp_next_hop + ipsec_sa: *ipsec_sa + keep: *keep + local_address: *local_address + local_as: *local_as + local_interface: *local_interface + local_preference: *local_preference + log_updown: *log_updown + metric_out: *metric_out + mtu_discovery: *mtu_discovery + multihop: *multihop + multipath: *multipath + no_advertise_peer_as: *no_advertise_peer_as + no_aggregator_id: *no_aggregator_id + no_client_reflect: *no_client_reflect + optimal_route_reflection: + description: Enable optimal route reflection for this client group. + type: dict + suboptions: + igp_backup: + description: Backup node identifier for this client group. + type: str + igp_primary: + description: Primary node identifier for this client group. + type: str + out_delay: *out_delay + outbound_route_filter: *outbound_route_filter + passive: *passive + peer_as: *peer_as + preference: *preference + remove_private: *remove_private + rfc6514_compliant_safi129: *rfc6514_compliant_safi129 + route_server_client: *route_server_client + tcp_aggressive_transmission: *tcp_aggressive_transmission + tcp_mss: *tcp_mss + traceoptions: *traceoptions + ttl: *ttl + type: + description: Specify BGP group type. + type: str + choices: ['external', 'internal'] + unconfigured_peer_graceful_restart: &unconfigured_peer_graceful_restart + description: Specify BGP unconfigured peer graceful restart options. + type: bool + vpn_apply_export: &vpn_apply_export + description: Apply BGP export policy when exporting VPN routes. + type: bool + neighbors: + description: Specify list of neighbors. + type: list + elements: dict + suboptions: + neighbor_address: + description: Specify neighbor address. + type: str + accept_remote_nexthop: *accept_remote_nexthop + add_path_display_ipv4_address: *add_path_display_ipv4_address + advertise_bgp_static: *advertise_bgp_static + advertise_external: *advertise_external + advertise_inactive: *advertise_inactive + advertise_peer_as: *advertise_peer_as + as_override: *as_override + authentication_algorithm: *authentication_algorithm + authentication_key: *authentication_key + authentication_key_chain: *authentication_key_chain + bfd_liveness_detection: *bfd_liveness_detection + bgp_error_tolerance: *bgp_error_tolerance + bmp: *bmp + cluster_id: *cluster_id + damping: *damping + description: + description: Specify neighbor description. + type: str + egress_te: *egress_te + enforce_first_as: *enforce_first_as + export: *export + forwarding_context: *forwarding_context + graceful_restart: *graceful_restart + hold_time: *hold_time + idle_after_switch_over: *idle_after_switch_over + import: *import + include_mp_next_hop: *include_mp_next_hop + ipsec_sa: *ipsec_sa + keep: *keep + local_address: *local_address + local_as: *local_as + local_interface: *local_interface + local_preference: *local_preference + log_updown: *log_updown + metric_out: *metric_out + mtu_discovery: *mtu_discovery + multihop: *multihop + multipath: *multipath + no_advertise_peer_as: *no_advertise_peer_as + no_aggregator_id: *no_aggregator_id + no_client_reflect: *no_client_reflect + out_delay: *out_delay + outbound_route_filter: *outbound_route_filter + passive: *passive + peer_as: *peer_as + preference: *preference + remove_private: *remove_private + rfc6514_compliant_safi129: *rfc6514_compliant_safi129 + route_server_client: *route_server_client + tcp_aggressive_transmission: *tcp_aggressive_transmission + tcp_mss: *tcp_mss + traceoptions: *traceoptions + ttl: *ttl + unconfigured_peer_graceful_restart: *unconfigured_peer_graceful_restart + vpn_apply_export: *vpn_apply_export + + state: + description: + - The state the configuration should be left in. + - State I(purged) removes all (routing-options autonomous-system, bgp global, bgp groups, bgp neighbors, bgp family + and bgp group and neighbor family) the BGP configurations from the + target device. Use caution with this state. + - State I(deleted) only removes BGP attributes that this modules + manages and does not negate the BGP process completely. Thereby, preserving + address-family related configurations under BGP context. + - Running states I(deleted) and I(replaced) will result in an error if there + are address-family configuration lines present under a neighbor.Please use the + M(junipernetworks.junos.junos_bgp_address_family) + modules for prior cleanup. + - Refer to examples for more details. + type: str + choices: + - purged + - merged + - replaced + - deleted + - gathered + - parsed + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show protocols bgp +# [edit] + +# admin# show routing-options autonomous-system +# [edit] + +- name: Merge Junos BGP config + junipernetworks.junos.junos_bgp_global: + config: + as_number: "65534" + loops: 3 + asdot_notation: true + accept_remote_nexthop: true + add_path_display_ipv4_address: true + advertise_bgp_static: + policy: "static-to-bgp" + advertise_from_main_vpn_tables: true + advertise_inactive: true + authentication_algorithm: "md5" + bgp_error_tolerance: + malformed_route_limit: 20000000 + bmp: + monitor: true + damping: true + description: "This is configured with Junos_bgp resource module" + egress_te_sid_stats: true + hold_time: 5 + holddown_all_stale_labels: true + include_mp_next_hop: true + log_updown: true + no_advertise_peer_as: true + no_aggregator_id: true + no_client_reflect: true + out_delay: 10 + precision_timers: true + preference: 2 + state: merged + +# After state +# ----------- +# +# admin# show routing-options autonomous-system +# 65534 loops 3 asdot-notation; + +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# advertise-bgp-static { +# policy static-to-bgp; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; + + +# Using merged +# +# Before state +# ------------ +# +# admin# show routing-options autonomous-system +# 65534 loops 3 asdot-notation; + +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# advertise-bgp-static { +# policy static-to-bgp; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; + +- name: Update running Junos BGP config + junipernetworks.junos.junos_bgp_global: + config: + egress_te_backup_paths: + templates: + - path_name: customer1 + peers: + - '11.11.11.11' + - '11.11.11.12' + - '11.11.11.13' + remote_nexthop: '2.2.2.2' + groups: + - name: 'internal' + type: 'internal' + vpn_apply_export: true + out_delay: 30 + accept_remote_nexthop: true + add_path_display_ipv4_address: true + peer_as: '65534' + allow: + - 'all' + - '1.1.1.0/24' + neighbors: + - neighbor_address: '11.11.11.11' + peer_as: '65534' + out_delay: 11 + - neighbor_address: '11.11.11.12' + peer_as: '65534' + out_delay: 12 + + - name: 'external' + out_delay: 20 + peer_as: '65534' + accept_remote_nexthop: true + add_path_display_ipv4_address: true + neighbors: + - neighbor_address: '12.12.12.12' + peer_as: '65534' + out_delay: 21 + accept_remote_nexthop: true + add_path_display_ipv4_address: true + - neighbor_address: '11.11.11.13' + peer_as: '65534' + out_delay: 31 + accept_remote_nexthop: true + add_path_display_ipv4_address: true + state: merged + +# After state +# ----------- +# +# admin# show routing-options autonomous-system +# 65534 loops 3 asdot-notation; + +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# egress-te-backup-paths { +# template customer1 { +# peer 11.11.11.11; +# peer 11.11.11.12; +# peer 11.11.11.13; +# remote-nexthop { +# 2.2.2.2; +# } +# } +# } +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; +# group internal { +# type internal; +# accept-remote-nexthop; +# out-delay 30; +# vpn-apply-export; +# peer-as 65534; +# add-path-display-ipv4-address; +# allow [ 0.0.0.0/0 1.1.1.0/24 ]; +# neighbor 11.11.11.11 { +# out-delay 11; +# peer-as 65534; +# } +# neighbor 11.11.11.12 { +# out-delay 12; +# peer-as 65534; +# } +# } +# group external { +# accept-remote-nexthop; +# out-delay 20; +# peer-as 65534; +# add-path-display-ipv4-address; +# neighbor 12.12.12.12 { +# accept-remote-nexthop; +# out-delay 21; +# peer-as 65534; +# add-path-display-ipv4-address; +# } +# neighbor 11.11.11.13 { +# accept-remote-nexthop; +# out-delay 31; +# peer-as 65534; +# add-path-display-ipv4-address; +# } +# } + + +# Using replaced +# +# Before state +# ------------ +# +# admin# show routing-options autonomous-system +# [edit] +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# advertise-bgp-static { +# policy static-to-bgp; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; + +- name: Replace Junos BGP global config + junipernetworks.junos.junos_bgp_global: + config: + advertise_bgp_static: + policy: "static-to-bgp" + advertise_inactive: true + authentication_algorithm: "md5" + bfd_liveness_detection: + minimum_receive_interval: 8 + multiplier: 30 + no_adaptation: true + transmit_interval: + minimum_interval: 4 + version: "automatic" + bgp_error_tolerance: + malformed_route_limit: 40000000 + description: "This is configured with Junos_bgp resource module replace" + egress_te_sid_stats: true + hold_time: 5 + out_delay: 10 + preference: "2" + state: replaced + +# After state +# ----------- +# +# admin# show protocols bgp +# description "This is configured with Junos_bgp resource module replace"; +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# bgp-error-tolerance { +# malformed-route-limit 40000000; +# } +# authentication-algorithm md5; +# advertise-bgp-static { +# policy static-to-bgp; +# } +# bfd-liveness-detection { +# version automatic; +# minimum-receive-interval 8; +# multiplier 30; +# no-adaptation; +# transmit-interval { +# minimum-interval 4; +# } +# } +# egress-te-sid-stats; + +# admin# show routing-options autonomous-system +# [edit] + +# +# Using deleted +# +# Before state +# ------------ +# +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; +# group internal { +# out-delay 12; +# } +# admin# show routing-options autonomous-system +# 65534 loops 3 asdot-notation; + +- name: Delete Junos BGP global config + junipernetworks.junos.junos_bgp_global: + config: + state: deleted + +# After state +# ----------- +# admin# show protocols bgp +# group internal { +# out-delay 12; +# } + + + +# admin# show protocols bgp +# [edit] + +# admin# show routing-options autonomous-system +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# admin# show protocols bgp +# description "This is configured with Junos_bgp resource module replace"; +# preference 2; +# hold-time 5; +# advertise-inactive; +# out-delay 10; +# bgp-error-tolerance { +# malformed-route-limit 40000000; +# } +# authentication-algorithm md5; +# advertise-bgp-static { +# policy static-to-bgp; +# } +# bfd-liveness-detection { +# version automatic; +# minimum-receive-interval 8; +# multiplier 30; +# no-adaptation; +# transmit-interval { +# minimum-interval 4; +# } +# } +# egress-te-sid-stats; + +- name: Gather Junos BGP global config + junipernetworks.junos.junos_bgp_global: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "advertise_bgp_static": { +# "policy": "static-to-bgp" +# }, +# "advertise_inactive": true, +# "authentication_algorithm": "md5", +# "bfd_liveness_detection": { +# "minimum_receive_interval": 8, +# "multiplier": 30, +# "no_adaptation": true, +# "transmit_interval": { +# "minimum_interval": 4 +# }, +# "version": "automatic" +# }, +# "bgp_error_tolerance": { +# "malformed_route_limit": 40000000 +# }, +# "description": "This is configured with Junos_bgp resource module replace", +# "egress_te_sid_stats": true, +# "hold_time": 5, +# "out_delay": 10, +# "preference": "2" +# } +# +# +# Using purged +# +# Before state +# ------------ +# +# admin# show protocols bgp +# precision-timers; +# advertise-from-main-vpn-tables; +# holddown-all-stale-labels; +# description "This is configured with Junos_bgp resource module"; +# accept-remote-nexthop; +# preference 2; +# hold-time 5; +# advertise-inactive; +# no-advertise-peer-as; +# no-aggregator-id; +# out-delay 10; +# log-updown; +# damping; +# bgp-error-tolerance { +# malformed-route-limit 20000000; +# } +# authentication-algorithm md5; +# no-client-reflect; +# include-mp-next-hop; +# bmp { +# monitor enable; +# } +# add-path-display-ipv4-address; +# egress-te-sid-stats; +# group internal { +# out-delay 12; +# } +# admin# show routing-options autonomous-system +# 65534 loops 3 asdot-notation; + +- name: Purge Junos BGP global config + junipernetworks.junos.junos_bgp_global: + config: + state: purged + +# After state +# ---------- +# admin# show protocols bgp +# +# [edit] +# admin# show routing-options autonomous-system +# +#[edit] + + +# Using rendered +# +# +- name: Render the commands for provided configuration + junipernetworks.junos.junos_bgp_global: + config: + authentication_algorithm: "md5" + bfd_liveness_detection: + minimum_receive_interval: 4 + multiplier: 10 + no_adaptation: true + transmit_interval: + minimum_interval: 2 + version: "automatic" + bgp_error_tolerance: + malformed_route_limit: 20000000 + bmp: + monitor: true + damping: true + description: "This is configured with Junos_bgp resource module" + egress_te_sid_stats: true + hold_time: 5 + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": " +# <nc:protocols +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:bgp> +# <nc:damping/> +# <nc:egress-te-sid-stats/> +# <nc:authentication-algorithm>md5</nc:authentication-algorithm> +# <nc:description>This is configured with Junos_bgp resource module</nc:description> +# <nc:hold-time>5</nc:hold-time> +# <nc:bfd-liveness-detection> +# <nc:transmit-interval> +# <nc:minimum-interval>2</nc:minimum-interval> +# </nc:transmit-interval> +# <nc:minimum-receive-interval>4</nc:minimum-receive-interval> +# <nc:multiplier>10</nc:multiplier> +# <nc:no-adaptation/> +# <nc:version>automatic</nc:version> +# </nc:bfd-liveness-detection> +# <nc:bgp-error-tolerance> +# <nc:malformed-route-limit>20000000</nc:malformed-route-limit> +# </nc:bgp-error-tolerance> +# <nc:bmp> +# <nc:monitor>enable</nc:monitor> +# </nc:bmp> +# </nc:bgp> +# </nc:protocols>" +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <protocols> +# <bgp> +# <precision-timers /> +# <advertise-from-main-vpn-tables /> +# <holddown-all-stale-labels /> +# <description>This is configured with Junos_bgp resource module</description> +# <accept-remote-nexthop /> +# <preference>2</preference> +# <hold-time>5</hold-time> +# <advertise-inactive /> +# <no-advertise-peer-as /> +# <no-aggregator-id /> +# <out-delay>10</out-delay> +# <log-updown /> +# <damping /> +# <bgp-error-tolerance> +# <malformed-route-limit>20000000</malformed-route-limit> +# </bgp-error-tolerance> +# <authentication-algorithm>md5</authentication-algorithm> +# <remove-private /> +# <no-client-reflect /> +# <include-mp-next-hop /> +# <bmp> +# <monitor>disable</monitor> +# <route-monitoring> +# <none /> +# </route-monitoring> +# </bmp> +# <advertise-bgp-static> +# <policy>static-to-bgp</policy> +# </advertise-bgp-static> +# <add-path-display-ipv4-address /> +# <bfd-liveness-detection> +# <version>automatic</version> +# <minimum-receive-interval>4</minimum-receive-interval> +# <multiplier>10</multiplier> +# <no-adaptation /> +# <transmit-interval> +# <minimum-interval>2</minimum-interval> +# </transmit-interval> +# <detection-time> +# <threshold>300000</threshold> +# </detection-time> +# </bfd-liveness-detection> +# <egress-te-sid-stats /> +# <group> +# <name>internal</name> +# <out-delay>8</out-delay> +# </group> +# <group> +# <name>external</name> +# <out-delay>9</out-delay> +# </group> +# <group> +# <name>inboun</name> +# <type>internal</type> +# </group> +# <group> +# <name>ibgp</name> +# <type>internal</type> +# <local-address>10.2.2.2</local-address> +# <export>static-to-bgp</export> +# <neighbor> +# <name>10.1.1.1</name> +# </neighbor> +# </group> +# </bgp> +# <ospf3> +# <area> +# <name>0.0.0.100</name> +# <stub> +# <default-metric>200</default-metric> +# </stub> +# <interface> +# <name>so-0/0/0.0</name> +# <metric>5</metric> +# <priority>3</priority> +# </interface> +# </area> +# </ospf3> +# </protocols> +# <routing-options> +# <static> +# <route> +# <name>172.16.17.0/24</name> +# <discard /> +# </route> +# </static> +# <router-id>10.200.16.75</router-id> +# <autonomous-system> +# <as-number>65432</as-number> +# </autonomous-system> +# </routing-options> +# </configuration> +# </rpc-reply> + + +- name: Parsed the device configuration to get output commands + junipernetworks.junos.junos_bgp_global: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "accept_remote_nexthop": true, +# "add_path_display_ipv4_address": true, +# "advertise_bgp_static": { +# "policy": "static-to-bgp" +# }, +# "advertise_from_main_vpn_tables": true, +# "advertise_inactive": true, +# "as_number": "65432", +# "authentication_algorithm": "md5", +# "bfd_liveness_detection": { +# "detection_time": { +# "threshold": 300000 +# }, +# "minimum_receive_interval": 4, +# "multiplier": 10, +# "no_adaptation": true, +# "transmit_interval": { +# "minimum_interval": 2 +# }, +# "version": "automatic" +# }, +# "bgp_error_tolerance": { +# "malformed_route_limit": 20000000 +# }, +# "bmp": { +# "monitor": false, +# "route_monitoring": { +# "none": true +# } +# }, +# "damping": true, +# "description": "This is configured with Junos_bgp resource module", +# "egress_te_sid_stats": true, +# "hold_time": 5, +# "holddown_all_stale_labels": true, +# "include_mp_next_hop": true, +# "log_updown": true, +# "no_advertise_peer_as": true, +# "no_aggregator_id": true, +# "no_client_reflect": true, +# "out_delay": 10, +# "precision_timers": true, +# "preference": "2" +# } +# + +""" +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: [' + <nc:protocols + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:bgp> + <nc:damping/> + <nc:egress-te-sid-stats/> + <nc:authentication-algorithm>md5</nc:authentication-algorithm> + <nc:description>This is configured with Junos_bgp resource module</nc:description> + <nc:hold-time>5</nc:hold-time> + <nc:bfd-liveness-detection> + <nc:transmit-interval> + <nc:minimum-interval>2</nc:minimum-interval> + </nc:transmit-interval> + <nc:minimum-receive-interval>4</nc:minimum-receive-interval> + <nc:multiplier>10</nc:multiplier> + <nc:no-adaptation/> + <nc:version>automatic</nc:version> + </nc:bfd-liveness-detection> + <nc:bgp-error-tolerance> + <nc:malformed-route-limit>20000000</nc:malformed-route-limit> + </nc:bgp-error-tolerance> + <nc:bmp> + <nc:monitor>enable</nc:monitor> + </nc:bmp> + </nc:bgp> + </nc:protocols>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.bgp_global.bgp_global import ( + Bgp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=Bgp_globalArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Bgp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_command.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_command.py new file mode 100644 index 000000000..ec713f230 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_command.py @@ -0,0 +1,481 @@ +#!/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: junos_command +author: Peter Sprygada (@privateip) +short_description: Run arbitrary commands on an Juniper JUNOS device +description: +- Sends an arbitrary set of commands to an JUNOS 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. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + commands: + description: + - The commands to send to the remote junos device. 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 I(retries) has been exceeded. + type: list + elements: str + rpcs: + description: + - The C(rpcs) argument accepts a list of RPCs to be executed over a netconf session + and the results from the RPC execution is return to the playbook via the modules + results dictionary. + type: list + elements: str + wait_for: + description: + - Specifies what to evaluate from the output of the command and what conditionals + to apply. This argument will cause the task to wait for a particular conditional + to be true before moving forward. If the conditional is not true by the configured + retries, the task fails. See examples. + type: list + elements: str + aliases: + - waitfor + match: + description: + - The I(match) argument is used in conjunction with the I(wait_for) argument to + specify the match policy. Valid values are C(all) or C(any). If the value + is set to C(all) then all conditionals in the I(wait_for) must be satisfied. If + the value is set to C(any) then only one of the values must be satisfied. + type: str + default: all + choices: + - any + - all + retries: + description: + - Specifies the number of retries a command should be tried before it is considered + failed. The command is run on the target device every retry and evaluated against + the I(wait_for) conditionals. + type: int + default: 10 + interval: + description: + - Configures the interval in seconds to wait between retries of the command. If + the command does not pass the specified conditional, the interval indicates + how to long to wait before trying the command again. + type: int + default: 1 + display: + description: + - Encoding scheme to use when serializing output from the device. This handles + how to properly understand the output and apply the conditionals path to the + result set. For I(rpcs) argument default display is C(xml) and for I(commands) + argument default display is C(text). Value C(set) is applicable only for fetching + configuration from device. + type: str + aliases: + - format + - output + choices: + - text + - json + - xml + - set +requirements: +- jxmlease +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(network_cli) connections and with C(local) connections + for legacy playbooks. +""" + +EXAMPLES = """ +- name: run show version on remote devices + junipernetworks.junos.junos_command: + commands: show version + +- name: run show version and check to see if output contains Juniper + junipernetworks.junos.junos_command: + commands: show version + wait_for: result[0] contains Juniper + +- name: run multiple commands on remote nodes + junipernetworks.junos.junos_command: + commands: + - show version + - show interfaces + +- name: run multiple commands and evaluate the output + junipernetworks.junos.junos_command: + commands: + - show version + - show interfaces + wait_for: + - result[0] contains Juniper + - result[1] contains Loopback0 + +- name: run commands and specify the output format + junipernetworks.junos.junos_command: + commands: show version + display: json + +- name: run rpc on the remote device + junipernetworks.junos.junos_command: + commands: show configuration + display: set + +- name: run rpc on the remote device + junipernetworks.junos.junos_command: + rpcs: get-software-information +""" + +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: [['...', '...'], ['...'], ['...']] +output: + description: The set of transformed xml to json format from the commands responses + returned: If the I(display) is in C(xml) format. + type: list + sample: ['...', '...'] +failed_conditions: + description: The list of conditionals that have failed + returned: failed + type: list + sample: ['...', '...'] +""" +import re +import shlex +import time + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + exec_rpc, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import ( + Conditional, + FailedConditionalError, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_lines + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_capabilities, + get_configuration, + get_connection, + tostring, +) + + +try: + from lxml.etree import Element, SubElement +except ImportError: + from xml.etree.ElementTree import Element, SubElement + +try: + import jxmlease + + HAS_JXMLEASE = True +except ImportError: + HAS_JXMLEASE = False + +USE_PERSISTENT_CONNECTION = True + + +def rpc(module, items): + + responses = list() + for item in items: + name = item["name"] + xattrs = item["xattrs"] + fetch_config = False + + args = item.get("args") + text = item.get("text") + + name = str(name).replace("_", "-") + + if all((module.check_mode, not name.startswith("get"))): + module.fail_json(msg="invalid rpc for running in check_mode") + + if name == "command" and text == "show configuration" or name == "get-configuration": + fetch_config = True + + element = Element(name, xattrs) + + if text: + element.text = text + + elif args: + for key, value in iteritems(args): + key = str(key).replace("_", "-") + if isinstance(value, list): + for item in value: + child = SubElement(element, key) + if item is not True: + child.text = item + else: + child = SubElement(element, key) + if value is not True: + child.text = value + if fetch_config: + reply = get_configuration(module, format=xattrs["format"]) + else: + reply = exec_rpc(module, tostring(element), ignore_warning=False) + + if xattrs["format"] == "text": + if len(reply) >= 1: + if fetch_config: + data = reply.find(".//configuration-text") + else: + if text and text.startswith("show configuration"): + data = reply.find(".//configuration-output") + else: + data = reply.find(".//output") + + if data is None: + module.fail_json(msg=tostring(reply)) + + responses.append(data.text.strip()) + else: + responses.append(reply.text.strip()) + + elif xattrs["format"] == "json": + responses.append(module.from_json(reply.text.strip())) + + elif xattrs["format"] == "set": + data = reply.find(".//configuration-set") + if data is None: + module.fail_json( + msg="Display format 'set' is not supported by remote device.", + ) + responses.append(data.text.strip()) + + else: + responses.append(tostring(reply)) + + return responses + + +def split(value): + lex = shlex.shlex(value) + lex.quotes = '"' + lex.whitespace_split = True + lex.commenters = "" + return list(lex) + + +def parse_rpcs(module): + items = list() + + for rpc in module.params["rpcs"] or list(): + parts = shlex.split(rpc) + + name = parts.pop(0) + args = dict() + + for item in parts: + key, value = item.split("=") + if str(value).upper() in ["TRUE", "FALSE"]: + args[key] = bool(value) + elif re.match(r"^[0-9]+$", value): + args[key] = int(value) + else: + args[key] = str(value) + + display = module.params["display"] or "xml" + + if display == "set" and rpc != "get-configuration": + module.fail_json( + msg="Invalid display option '%s' given for rpc '%s'" % ("set", name), + ) + + xattrs = {"format": display} + items.append({"name": name, "args": args, "xattrs": xattrs}) + + return items + + +def parse_commands(module, warnings): + items = list() + + for command in module.params["commands"] or list(): + if module.check_mode and not command.startswith("show"): + warnings.append( + "Only show commands are supported when using check_mode, not " + "executing %s" % command, + ) + continue + + parts = command.split("|") + text = parts[0] + + display = module.params["display"] or "text" + + if "| display json" in command: + display = "json" + + elif "| display xml" in command: + display = "xml" + + if display == "set" or "| display set" in command: + if command.startswith("show configuration"): + display = "set" + else: + module.fail_json( + msg="Invalid display option '%s' given for command '%s'" % ("set", command), + ) + + xattrs = {"format": display} + items.append({"name": "command", "xattrs": xattrs, "text": text}) + + return items + + +def main(): + """entry point for module execution""" + argument_spec = dict( + commands=dict(type="list", elements="str"), + rpcs=dict(type="list", elements="str"), + display=dict( + choices=["text", "json", "xml", "set"], + aliases=["format", "output"], + ), + 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"), + ) + + required_one_of = [("commands", "rpcs")] + + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + supports_check_mode=True, + ) + + warnings = list() + conn = get_connection(module) + capabilities = get_capabilities(module) + + if capabilities.get("network_api") == "cliconf": + if any( + ( + module.params["wait_for"], + module.params["match"], + module.params["rpcs"], + ), + ): + module.warn( + "arguments wait_for, match, rpcs are not supported when using transport=cli", + ) + commands = module.params["commands"] + + output = list() + display = module.params["display"] + for cmd in commands: + # if display format is not mentioned in command, add the display format + # from the modules params + if ("display json" not in cmd) and ("display xml" not in cmd): + if display and display != "text": + cmd += " | display {0}".format(display) + try: + output.append(conn.get(command=cmd)) + except ConnectionError as exc: + module.fail_json( + msg=to_text(exc, errors="surrogate_then_replace"), + ) + + lines = [out.split("\n") for out in output] + result = {"changed": False, "stdout": output, "stdout_lines": lines} + module.exit_json(**result) + + items = list() + items.extend(parse_commands(module, warnings)) + items.extend(parse_rpcs(module)) + + wait_for = module.params["wait_for"] or list() + conditionals = [Conditional(c) for c in wait_for] + + retries = module.params["retries"] + interval = module.params["interval"] + match = module.params["match"] + while retries > 0: + responses = rpc(module, items) + transformed = list() + output = list() + for item, resp in zip(items, responses): + if item["xattrs"]["format"] == "xml": + if not HAS_JXMLEASE: + module.fail_json( + msg="jxmlease is required but does not appear to be installed. " + "It can be installed using `pip install jxmlease`", + ) + + try: + json_resp = jxmlease.parse(resp) + transformed.append(json_resp) + output.append(json_resp) + except Exception: + raise ValueError(resp) + else: + transformed.append(resp) + + for item in list(conditionals): + try: + if item(transformed): + if match == "any": + conditionals = list() + break + conditionals.remove(item) + except FailedConditionalError: + pass + + 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 = { + "changed": False, + "warnings": warnings, + "stdout": responses, + "stdout_lines": list(to_lines(responses)), + } + + if output: + result["output"] = output + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_config.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_config.py new file mode 100644 index 000000000..c8bfde10c --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_config.py @@ -0,0 +1,531 @@ +#!/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: junos_config +author: Peter Sprygada (@privateip) +short_description: Manage configuration on devices running Juniper JUNOS +description: +- This module provides an implementation for working with the active configuration + running on Juniper JUNOS devices. It provides a set of arguments for loading configuration, + performing rollback operations and zeroing the active configuration on the device. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + lines: + description: + - This argument takes a list of C(set) or C(delete) configuration lines to push + into the remote device. Each line must start with either C(set) or C(delete). This + argument is mutually exclusive with the I(src) argument. + type: list + aliases: + - commands + elements: str + src: + description: + - The I(src) argument provides a path to the configuration file to load into the + remote system. The path can either be a full system path to the configuration + file if the value starts with / or relative to the root of the implemented role + or playbook. This argument is mutually exclusive with the I(lines) argument. + type: path + src_format: + description: + - The I(src_format) argument specifies the format of the configuration found int + I(src). If the I(src_format) argument is not provided, the module will attempt + to determine the format of the configuration file specified in I(src). + type: str + choices: + - xml + - set + - text + - json + rollback: + description: + - The C(rollback) argument instructs the module to rollback the current configuration + to the identifier specified in the argument. If the specified rollback identifier + does not exist on the remote device, the module will fail. To rollback to the + most recent commit, set the C(rollback) argument to 0. + type: int + zeroize: + description: + - The C(zeroize) argument is used to completely sanitize the remote device configuration + back to initial defaults. This argument will effectively remove all current + configuration statements on the remote device. + type: bool + default: no + confirm: + description: + - The C(confirm) argument will configure a time out value in minutes for the commit + to be confirmed before it is automatically rolled back. If the C(confirm) argument + is set to False, this argument is silently ignored. If the value for this argument + is set to 0, the commit is confirmed immediately. + type: int + default: 0 + comment: + description: + - The C(comment) argument specifies a text string to be used when committing the + configuration. If the C(confirm) argument is set to False, this argument is + silently ignored. + default: configured by junos_config + type: str + replace: + description: + - The C(replace) argument will instruct the remote device to replace the current + configuration hierarchy with the one specified in the corresponding hierarchy + of the source configuration loaded from this module. + - Note this argument should be considered deprecated. To achieve the equivalent, + set the I(update) argument to C(replace). This argument will be removed in a + future release. The C(replace) and C(update) argument is mutually exclusive. + type: bool + 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 + update: + description: + - This argument will decide how to load the configuration data particularly when + the candidate configuration and loaded configuration contain conflicting statements. + Following are accepted values. C(merge) combines the data in the loaded configuration + with the candidate configuration. If statements in the loaded configuration + conflict with statements in the candidate configuration, the loaded statements + replace the candidate ones. C(override) discards the entire candidate configuration + and replaces it with the loaded configuration. C(replace) substitutes each hierarchy + level in the loaded configuration for the corresponding level. C(update) is + similar to the override option. The new configuration completely replaces the + existing configuration. The difference comes when the configuration is later + committed. This option performs a 'diff' between the new candidate configuration + and the existing committed configuration. It then only notifies system processes + responsible for the changed portions of the configuration, and only marks the + actual configuration changes as 'changed'. + type: str + default: merge + choices: + - merge + - override + - replace + - update + confirm_commit: + description: + - This argument will execute commit operation on remote device. It can be used + to confirm a previous commit. + type: bool + default: no + check_commit: + description: + - This argument will check correctness of syntax; do not apply changes. + - Note that this argument can be used to confirm verified configuration done via + commit confirmed operation + type: bool + default: no + 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 + backup_format: + description: + - This argument specifies the format of the configuration the backup file will + be stored as. If the argument is not specified, the module will use the 'set' + format. + type: str + default: set + choices: + - xml + - set + - text + - json + type: dict +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- 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). +- Loading JSON-formatted configuration I(json) is supported starting in Junos OS Release + 16.1 onwards. +- Update C(override) not currently compatible with C(set) notation. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: load configure file into device + junipernetworks.junos.junos_config: + src: srx.cfg + comment: update config + +- name: load configure lines into device + junipernetworks.junos.junos_config: + lines: + - set interfaces ge-0/0/1 unit 0 description "Test interface" + - set vlans vlan01 description "Test vlan" + comment: update config + +- name: Set routed VLAN interface (RVI) IPv4 address + junipernetworks.junos.junos_config: + lines: + - set vlans vlan01 vlan-id 1 + - set interfaces irb unit 10 family inet address 10.0.0.1/24 + - set vlans vlan01 l3-interface irb.10 + +- name: Check correctness of commit configuration + junipernetworks.junos.junos_config: + check_commit: yes + +- name: rollback the configuration to id 10 + junipernetworks.junos.junos_config: + rollback: 10 + +- name: zero out the current configuration + junipernetworks.junos.junos_config: + zeroize: yes + +- name: Set VLAN access and trunking + junipernetworks.junos.junos_config: + lines: + - set vlans vlan02 vlan-id 6 + - set interfaces ge-0/0/6.0 family ethernet-switching interface-mode access vlan + members vlan02 + - set interfaces ge-0/0/6.0 family ethernet-switching interface-mode trunk vlan + members vlan02 + +- name: confirm a previous commit + junipernetworks.junos.junos_config: + confirm_commit: yes + +- name: for idempotency, use full-form commands + junipernetworks.junos.junos_config: + lines: + # - set int ge-0/0/1 unit 0 desc "Test interface" + - set interfaces ge-0/0/1 unit 0 description "Test interface" + +- name: configurable backup path + junipernetworks.junos.junos_config: + src: srx.cfg + backup: yes + backup_options: + filename: backup.cfg + dir_path: /home/user +""" + +RETURN = """ +backup_path: + description: The full path to the backup file + returned: when backup is yes + type: str + sample: /playbooks/ansible/backup/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: junos01_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/junos01_config +date: + description: The date extracted from the backup file name + returned: when backup is yes + type: str + sample: "2016-07-16" +time: + description: The time extracted from the backup file name + returned: when backup is yes + type: str + sample: "22:28:34" +""" +import json +import re + +from ansible.module_utils._text import to_native, to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + exec_rpc, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + get_configuration, + get_diff, + load_config, + load_configuration, + locked_config, + tostring, +) + + +try: + from lxml.etree import Element, fromstring +except ImportError: + from xml.etree.ElementTree import Element, fromstring + +try: + from lxml.etree import ParseError +except ImportError: + try: + from xml.etree.ElementTree import ParseError + except ImportError: + # for Python < 2.7 + from xml.parsers.expat import ExpatError + + ParseError = ExpatError + +USE_PERSISTENT_CONNECTION = True +DEFAULT_COMMENT = "configured by junos_config" + + +def check_args(module, warnings): + if module.params["replace"] is not None: + module.fail_json(msg="argument replace is deprecated, use update") + + +def zeroize(module): + return exec_rpc( + module, + tostring(Element("request-system-zeroize")), + ignore_warning=False, + ) + + +def rollback(ele, id="0"): + return get_diff(ele, id) + + +def guess_format(config): + try: + json.loads(config) + return "json" + except ValueError: + pass + + try: + fromstring(config) + return "xml" + except ParseError: + pass + + if config.startswith("set") or config.startswith("delete"): + return "set" + + return "text" + + +def filter_delete_statements(module, candidate): + reply = get_configuration(module, format="set") + match = reply.find(".//configuration-set") + if match is None: + # Could not find configuration-set in reply, perhaps device does not support it? + return candidate + config = to_native(match.text, encoding="latin-1") + + modified_candidate = candidate[:] + for index, line in reversed(list(enumerate(candidate))): + if line.startswith("delete"): + newline = re.sub("^delete", "set", line) + if newline not in config: + del modified_candidate[index] + + return modified_candidate + + +def configure_device(module, warnings, candidate): + + kwargs = {} + config_format = None + + if module.params["src"]: + config_format = module.params["src_format"] or guess_format( + str(candidate), + ) + if config_format == "set": + kwargs.update({"format": "text", "action": "set"}) + else: + kwargs.update( + {"format": config_format, "action": module.params["update"]}, + ) + + if isinstance(candidate, string_types): + candidate = candidate.split("\n") + + # this is done to filter out `delete ...` statements which map to + # nothing in the config as that will cause an exception to be raised + if any((module.params["lines"], config_format == "set")): + candidate = filter_delete_statements(module, candidate) + kwargs["format"] = "text" + kwargs["action"] = "set" + + return load_config(module, candidate, warnings, **kwargs) + + +def main(): + """main entry point for module execution""" + backup_spec = dict( + filename=dict(), + dir_path=dict(type="path"), + backup_format=dict( + default="set", + choices=["xml", "text", "set", "json"], + ), + ) + argument_spec = dict( + lines=dict(aliases=["commands"], type="list", elements="str"), + src=dict(type="path"), + src_format=dict(choices=["xml", "text", "set", "json"]), + # update operations + update=dict( + default="merge", + choices=["merge", "override", "replace", "update"], + ), + # deprecated replace in Ansible 2.3 + replace=dict(type="bool"), + confirm=dict(default=0, type="int"), + comment=dict(default=DEFAULT_COMMENT), + confirm_commit=dict(type="bool", default=False), + check_commit=dict(type="bool", default=False), + # config operations + backup=dict(type="bool", default=False), + backup_options=dict(type="dict", options=backup_spec), + rollback=dict(type="int"), + zeroize=dict(default=False, type="bool"), + ) + + mutually_exclusive = [("lines", "src", "rollback", "zeroize")] + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + warnings = list() + check_args(module, warnings) + + candidate = module.params["lines"] or module.params["src"] + commit = not module.check_mode + + result = {"changed": False, "warnings": warnings} + + if module.params["backup"]: + if module.params["backup_options"] is not None: + conf_format = module.params["backup_options"]["backup_format"] + else: + conf_format = "set" + reply = get_configuration(module, format=conf_format) + if reply is None: + module.fail_json(msg="unable to retrieve device configuration") + else: + if conf_format in ["set", "text"]: + reply = reply.find( + ".//configuration-%s" % conf_format, + ).text.strip() + elif conf_format in "xml": + reply = str( + tostring(reply.find(".//configuration"), pretty_print=True), + ).strip() + elif conf_format in "json": + reply = str(reply.xpath("//rpc-reply/text()")[0]).strip() + if not isinstance(reply, str): + module.fail_json( + msg="unable to format retrieved device configuration", + ) + result["__backup__"] = reply + + rollback_id = module.params["rollback"] + if rollback_id: + diff = rollback(module, rollback_id) + if commit: + kwargs = {"comment": module.params["comment"]} + with locked_config(module): + load_configuration(module, rollback=rollback_id) + commit_configuration(module, **kwargs) + if module._diff: + result["diff"] = {"prepared": diff} + result["changed"] = True + + elif module.params["zeroize"]: + if commit: + zeroize(module) + result["changed"] = True + + else: + if candidate: + with locked_config(module): + diff = configure_device(module, warnings, candidate) + if diff: + if commit: + kwargs = { + "comment": module.params["comment"], + "check": module.params["check_commit"], + } + + confirm = module.params["confirm"] + if confirm > 0: + kwargs.update( + { + "confirm": True, + "confirm_timeout": to_text( + confirm, + errors="surrogate_then_replace", + ), + }, + ) + commit_configuration(module, **kwargs) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + elif module.params["check_commit"]: + commit_configuration(module, check=True) + + elif module.params["confirm_commit"]: + with locked_config(module): + # confirm a previous commit + commit_configuration(module) + + result["changed"] = True + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_facts.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_facts.py new file mode 100644 index 000000000..f44bdf820 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_facts.py @@ -0,0 +1,146 @@ +#!/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: junos_facts +author: Nathaniel Case (@Qalthos) +short_description: Collect facts from remote devices running Juniper Junos +description: +- Collects fact information from a remote device running the Junos operating system. By + default, the module will collect basic fact information from the device to be included + with the hostvars. Additional fact information can be collected based on the configured + set of arguments. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + gather_subset: + description: + - When supplied, this argument will restrict the facts collected to a given subset. Possible + values for this argument include C(all), C(hardware), C(config), C(interfaces) and C(min). Can + specify a list of values to include a larger subset. Values can also be used + with an initial C(!) to specify that a specific subset should not be collected. + To maintain backward compatibility old style facts can be retrieved by explicitly + adding C(ofacts) to value, this requires junos-eznc to be installed as a prerequisite. + Valid value of gather_subset are default, hardware, config, interfaces, ofacts. + If C(ofacts) is present in the list it fetches the old style facts (fact keys + without 'ansible_' prefix) and it requires junos-eznc library to be installed. + required: false + default: + - 'min' + type: list + elements: str + config_format: + description: + - The I(config_format) argument specifies the format of the configuration when + serializing output from the device. This argument is applicable only when C(config) + value is present in I(gather_subset). The I(config_format) should be supported + by the junos version running on device. This value is not applicable while fetching + old style facts that is when C(ofacts) value is present in value if I(gather_subset) + value. This option is valid only for C(gather_subset) values. + type: str + required: false + default: text + choices: + - xml + - text + - set + - json + gather_network_resources: + description: + - When supplied, this argument will restrict the facts collected to a given subset. + Possible values for this argument include all and the resources like interfaces, + vlans etc. Can specify a list of values to include a larger subset. Values can + also be used with an initial C(!) to specify that a specific subset should + not be collected. Valid subsets are 'all', 'interfaces', 'lacp', 'lacp_interfaces', + 'lag_interfaces', 'l2_interfaces', 'l3_interfaces', 'lldp_global', 'lldp_interfaces', + 'vlans'. + required: false + type: list + elements: str + available_network_resources: + description: When 'True' a list of network resources for which resource modules are available will be provided. + type: bool + default: false +requirements: +- ncclient (>=v0.5.2) +notes: +- Ensure I(config_format) used to retrieve configuration from device is supported + by junos version running on device. +- With I(config_format = json), configuration in the results will be a dictionary(and + not a JSON string) +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: collect default set of facts + junipernetworks.junos.junos_facts: + +- name: collect default set of facts and configuration + junipernetworks.junos.junos_facts: + gather_subset: config + +- name: Gather legacy and resource facts + junipernetworks.junos.junos_facts: + gather_subset: all + gather_network_resources: all +""" + +RETURN = """ +ansible_facts: + description: Returns the facts collect from the device + returned: always + type: dict +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.facts.facts import ( + FactsArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.facts.facts import ( + FACT_RESOURCE_SUBSETS, + Facts, +) + + +def main(): + """ + Main entry point for module execution + + :returns: ansible_facts + """ + argument_spec = FactsArgs.argument_spec + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + warnings = [] + ansible_facts = {} + if module.params.get("available_network_resources"): + ansible_facts["available_network_resources"] = sorted( + FACT_RESOURCE_SUBSETS.keys(), + ) + result = Facts(module).get_facts() + additional_facts, additional_warnings = result + ansible_facts.update(additional_facts) + warnings.extend(additional_warnings) + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_hostname.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_hostname.py new file mode 100644 index 000000000..093645a6f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_hostname.py @@ -0,0 +1,362 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_hostname +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_hostname +version_added: 2.9.0 +short_description: Manage Hostname server configuration on Junos devices. +description: This module manages hostname configuration on devices running Junos. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show system hostname). + - 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 system Hostname configuration. + type: dict + suboptions: + hostname: + description: Specify the hostname. + type: str + state: + description: + - The state the configuration should be left in. + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The states I(merged), I(replaced) and I(overridden) have identical + behaviour for this module. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show system hostname) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - deleted + - overridden + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show system hostname +# +# [edit] +- name: Merge provided HOSTNAME configuration into running configuration. + junipernetworks.junos.junos_hostname: + config: + hostname: 'vsrx-18.4R1' + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "hostname": "vsrx-18.4R1" +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:host-name>vsrx-18.4R1</nc:host-name></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-18.4R1; +# +# Using replaced +# +# Before state +# ------------ +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-18.4R1; +# +# [edit] +- name: Replaced target config with provided config. + junipernetworks.junos.junos_hostname: + config: + hostname: 'vsrx-12' + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "hostname": "vsrx-12" +# }, +# "before": { +# "hostname": "vsrx-18.4R1" +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:host-name>vsrx-12</nc:host-name></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-12; +# +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-18.4R1; +# +# [edit] +- name: Replaced target config with provided config. + junipernetworks.junos.junos_hostname: + config: + hostname: 'vsrx-12' + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "hostname": "vsrx-12" +# }, +# "before": { +# "hostname": "vsrx-18.4R1" +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:host-name>vsrx-12</nc:host-name></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-12; +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-12; +# +- name: Delete running HOSTNAME global configuration + junipernetworks.junos.junos_hostname: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "hostname": "vsrx-12" +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:host-name delete=\"delete\"/></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx-18.4R1# show system host-name +# host-name vsrx-12; +# +- name: Gather running HOSTNAME global configuration + junipernetworks.junos.junos_hostname: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "hostname": "vsrx-12", +# }, +# "changed": false, +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_hostname: + config: + boot_server: '78.46.194.186' + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:host-name>78.46.194.186</nc:host-name></nc:system>" +# ] +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <system xmlns="http://yang.juniper.net/junos-es/conf/system"> +# <host-name>vsrx-18.4R1</host-name> +# </system> +# </configuration> +# </rpc-reply> +# +- name: Parse HOSTNAME running config + junipernetworks.junos.junos_hostname: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "hostname": "vsrx-18.4R1" +# } +# + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['"<nc:host-name>78.46.194.186</nc:host-name></nc:system>"'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.hostname.hostname import ( + HostnameArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.hostname.hostname import ( + Hostname, +) + + +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=HostnameArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Hostname(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_interfaces.py new file mode 100644 index 000000000..f3b749d63 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_interfaces.py @@ -0,0 +1,587 @@ +#!/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 junos_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_interfaces +short_description: Junos Interfaces resource module +description: This module manages the interfaces on Juniper Junos OS network devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: The provided configuration + type: list + elements: dict + suboptions: + name: + description: + - Full name of interface, e.g. ge-0/0/0. + type: str + required: true + description: + description: + - Interface description. + type: str + duplex: + description: + - Interface link status. Applicable for Ethernet interfaces only, either in + half duplex, full duplex or in automatic state which negotiates the duplex + automatically. + type: str + choices: + - automatic + - full-duplex + - half-duplex + enabled: + default: true + description: + - Administrative state of the interface. + - Set the value to C(true) to administratively enabled the interface or C(false) + to disable it. + type: bool + hold_time: + description: + - The hold time for given interface name. + type: dict + suboptions: + down: + description: + - The link down hold time in milliseconds. + type: int + up: + description: + - The link up hold time in milliseconds. + type: int + mtu: + description: + - MTU for a specific interface. + - Applicable for Ethernet interfaces only. + type: int + speed: + description: + - Interface link speed. Applicable for Ethernet interfaces only. + type: str + units: + description: + - Specify Logical interfaces units. + type: list + elements: dict + suboptions: + name: + description: + - Specify interface unit number. + type: int + description: + description: Specify logical interface description. + 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 Junos device + by executing the command B(show interfaces). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + description: + - The state of the configuration after module completion + type: str +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 18.4R1. +- This module works with connection C(netconf). +- See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). +""" +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible-1"; +# speed 1g; +# mtu 1800 +# unit 0 { +# description "This is logical intf unit0"; +# } +# ge-0/0/2 { +# description "Configured by Ansible-2"; +# ether-options { +# auto-negotiation; +# } +# } + +- name: "Delete given options for the interface (Note: This won't delete the interface itself if any other values are configured for interface)" + junipernetworks.junos.junos_interfaces: + config: + - name: ge-0/0/1 + description: Configured by Ansible-1 + speed: 1g + mtu: 1800 + - name: ge-0/0/2 + description: Configured by Ansible -2 + state: deleted + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# auto-negotiation; +# } +# } + + +# Using merged + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "test interface"; +# speed 1g; +# } +# fe-0/0/2 { +# vlan-tagging; +# unit 10 { +# vlan-id 10; +# } +# unit 11 { +# vlan-id 11; +# } +# } + +- name: Merge provided configuration with device configuration (default operation + is merge) + junipernetworks.junos.junos_interfaces: + config: + - name: ge-0/0/1 + description: Configured by Ansible-1 + enabled: true + units: + - name: 0 + description: "This is logical intf unit0" + mtu: 1800 + - name: ge-0/0/2 + description: Configured by Ansible-2 + enabled: false + state: merged + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible-1"; +# speed 1g; +# mtu 1800 +# unit 0 { +# description "This is logical intf unit0"; +# } +# } +# ge-0/0/2 { +# disable; +# description "Configured by Ansible-2"; +# } + + +# Using overridden + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible-1"; +# speed 1g; +# mtu 1800 +# } +# ge-0/0/2 { +# disable; +# description "Configured by Ansible-2"; +# ether-options { +# auto-negotiation; +# } +# } +# ge-0/0/11 { +# description "Configured by Ansible-11"; +# } + +- name: Override device configuration of all interfaces with provided configuration + junipernetworks.junos.junos_interfaces: + config: + - name: ge-0/0/2 + description: Configured by Ansible-2 + enabled: false + mtu: 2800 + - name: ge-0/0/3 + description: Configured by Ansible-3 + state: overridden + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/2 { +# disable; +# description "Configured by Ansible-2"; +# mtu 2800 +# } +# ge-0/0/3 { +# description "Configured by Ansible-3"; +# } + + +# Using replaced + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible-1"; +# speed 1g; +# mtu 1800 +# } +# ge-0/0/2 { +# disable; +# mtu 1800; +# speed 1g; +# description "Configured by Ansible-2"; +# ether-options { +# auto-negotiation; +# } +# } +# ge-0/0/11 { +# description "Configured by Ansible-11"; +# } + +- name: Replaces device configuration of listed interfaces with provided configuration + junipernetworks.junos.junos_interfaces: + config: + - name: ge-0/0/2 + description: Configured by Ansible-2 + enabled: false + mtu: 2800 + - name: ge-0/0/3 + description: Configured by Ansible-3 + state: replaced + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible-1"; +# speed 1g; +# mtu 1800 +# } +# ge-0/0/2 { +# disable; +# description "Configured by Ansible-2"; +# mtu 2800 +# } +# ge-0/0/3 { +# description "Configured by Ansible-3"; +# } +# ge-0/0/11 { +# description "Configured by Ansible-11"; +# } +# Using gathered +# Before state: +# ------------ +# +# vagrant@vsrx# show interfaces +# fe-0/0/2 { +# description "This is interface DESCRIPTION"; +# vlan-tagging; +# unit 10 { +# description "UNIT 10 DESCRIPTION"; +# vlan-id 10; +# } +# unit 11 { +# description "UNIT 11 DESCRIPTION"; +# vlan-id 11; +# } +# } +# fxp0 { +# description OUTER; +# unit 0 { +# description "Sample config"; +# family inet { +# dhcp; +# } +# } +# } +# +- name: Gather junos interfaces as in given arguments + junipernetworks.junos.junos_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "description": "This is interface DESCRIPTION", +# "enabled": true, +# "name": "fe-0/0/2", +# "units": [ +# { +# "description": "UNIT 10 DESCRIPTION", +# "name": 10 +# }, +# { +# "description": "UNIT 11 DESCRIPTION", +# "name": 11 +# } +# ] +# }, +# { +# "description": "OUTER", +# "enabled": true, +# "name": "fxp0", +# "units": [ +# { +# "description": "Sample config", +# "name": 0 +# } +# ] +# } +# ] +# After state: +# ------------ +# +# vagrant@vsrx# show interfaces +# fe-0/0/2 { +# description "This is interface DESCRIPTION"; +# vlan-tagging; +# unit 10 { +# description "UNIT 10 DESCRIPTION"; +# vlan-id 10; +# } +# unit 11 { +# description "UNIT 11 DESCRIPTION"; +# vlan-id 11; +# } +# } +# fxp0 { +# description OUTER; +# unit 0 { +# description "Sample config"; +# family inet { +# dhcp; +# } +# } +# } +# +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <interfaces> +# <interface> +# <name>ge-0/0/1</name> +# <description>Configured by Ansible</description> +# <disable/> +# <speed>100m</speed> +# <mtu>1024</mtu> +# <hold-time> +# <up>2000</up> +# <down>2200</down> +# </hold-time> +# <link-mode>full-duplex</link-mode> +# <unit> +# <name>0</name> +# <family> +# <ethernet-switching> +# <interface-mode>access</interface-mode> +# <vlan> +# <members>vlan100</members> +# </vlan> +# </ethernet-switching> +# </family> +# </unit> +# </interface> +# </interfaces> +# </configuration> +# </rpc-reply> +# - name: Convert interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "description": "Configured by Ansible", +# "duplex": "full-duplex", +# "enabled": false, +# "hold_time": { +# "down": 2200, +# "up": 2000 +# }, +# "mtu": 1024, +# "name": "ge-0/0/1", +# "speed": "100m" +# } +# ] +# +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_interfaces: + config: + - name: ge-0/0/2 + description: Configured by Ansibull + mtu: 2048 + speed: 20m + hold_time: + up: 3200 + down: 3200 + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": <nc:interfaces +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:interface> +# <nc:name>ge-0/0/2</nc:name> +# <nc:description>Configured by Ansibull</nc:description> +# <nc:speed>20m</nc:speed> +# <nc:mtu>2048</nc:mtu> +# <nc:hold-time> +# <nc:up>3200</nc:up> +# <nc:down>3200</nc:down> +# </nc:hold-time> +# </nc:interface> +# </nc:interfaces>" + +""" +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. +xml: + description: The set of xml rpc payload pushed to the remote device. + returned: always + type: list + sample: ['<?xml version="1.0" encoding="UTF-8"?> +<rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> + <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> + <interfaces> + <interface> + <name>ge-0/0/1</name> + <description>Configured by Ansible</description> + <disable/> + <speed>100m</speed> + <mtu>1024</mtu> + <hold-time> + <up>2000</up> + <down>2200</down> + </hold-time> + <link-mode>full-duplex</link-mode> + <unit> + <name>0</name> + <family> + <ethernet-switching> + <interface-mode>access</interface-mode> + <vlan> + <members>vlan100</members> + </vlan> + </ethernet-switching> + </family> + </unit> + </interface> + </interfaces> + </configuration> +</rpc-reply>', 'xml 2', 'xml 3'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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",)), + ] + + module = AnsibleModule( + argument_spec=InterfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_l2_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_l2_interfaces.py new file mode 100644 index 000000000..d5def410b --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_l2_interfaces.py @@ -0,0 +1,697 @@ +#!/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 junos_l2_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_l2_interfaces +short_description: L2 interfaces resource module +description: This module provides declarative management of a Layer-2 interface on + Juniper JUNOS devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: A dictionary of Layer-2 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of interface, e.g. ge-0/0/1. + type: str + required: true + unit: + description: + - Logical interface number. Value of C(unit) should be of type integer. + type: int + access: + description: + - Configure the interface as a Layer 2 access mode. + type: dict + suboptions: + vlan: + description: + - Configure the access VLAN ID. + type: str + trunk: + description: + - Configure the interface as a Layer 2 trunk mode. + type: dict + suboptions: + allowed_vlans: + description: + - List of VLANs to be configured in trunk port. It's used as the VLAN + range to ADD or REMOVE from the trunk. + type: list + elements: str + native_vlan: + description: + - Native VLAN to be configured in trunk port. It is used as the trunk + native VLAN ID. + type: str + enhanced_layer: + description: + - True if your device has Enhanced Layer 2 Software (ELS). If the l2 configuration + is under C(interface-mode) the value is True else if the l2 configuration + is under C(port-mode) value is False + 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 Junos device + by executing the command B(show interfaces). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + description: + - The state of the configuration after module completion + type: str +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 18.4R1. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +""" +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# ansible@junos01# show interfaces +# ge-0/0/1 { +# description "L2 interface"; +# speed 1g; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members vlan30; +# } +# } +# } +#} +#ge-0/0/2 { +# description "non L2 interface"; +# unit 0 { +# family inet { +# address 192.168.56.14/24; +# } +# } + +- name: "Delete L2 attributes of given interfaces (Note: This won't delete the + interface itself)." + junipernetworks.junos.junos_l2_interfaces: + config: + - name: ge-0/0/1 + - name: ge-0/0/2 + state: deleted + +# After state: +# ------------ +# +# ansible@junos01# show interfaces +# ge-0/0/1 { +# description "L2 interface"; +# speed 1g; +# } +#ge-0/0/2 { +# description "non L2 interface"; +# unit 0 { +# family inet { +# address 192.168.56.14/24; +# } +# } + + +# Using merged + +# Before state: +# ------------- +# ansible@junos01# show interfaces +# ge-0/0/3 { +# description "test interface"; +# speed 1g; +#} +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 100; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan40 ]; +# } +# } +# } +# } + +- name: Merge provided configuration with device configuration (default operation + is merge) + junipernetworks.junos.junos_l2_interfaces: + config: + - name: ge-0/0/3 + access: + vlan: v101 + - name: ge-0/0/4 + trunk: + allowed_vlans: + - vlan30 + native_vlan: 50 + state: merged + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/3 { +# description "test interface"; +# speed 1g; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members v101; +# } +# } +# } +# } +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 50; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan40 vlan30 ]; +# } +# } +# } +# } + + +# Using overridden + +# Before state: +# ------------- +# ansible@junos01# show interfaces +# ge-0/0/3 { +# description "test interface"; +# speed 1g; +#} +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 100; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan40 ]; +# } +# } +# } +# } +# ge-0/0/5 { +# description "Configured by Ansible-11"; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members v101; +# } +# } +# } +# } + +- name: Override provided configuration with device configuration + junipernetworks.junos.junos_l2_interfaces: + config: + - name: ge-0/0/3 + access: + vlan: v101 + - name: ge-0/0/4 + trunk: + allowed_vlans: + - vlan30 + native_vlan: 50 + state: overridden + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/3 { +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members v101; +# } +# } +# } +# } +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 50; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan30 ]; +# } +# } +# } +# } + + +# Using replaced + +# Before state: +# ------------- +# ansible@junos01# show interfaces +# ge-0/0/3 { +# description "test interface"; +# speed 1g; +#} +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 100; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan40 ]; +# } +# } +# } +# } + +- name: Replace provided configuration with device configuration + junipernetworks.junos.junos_l2_interfaces: + config: + - name: ge-0/0/3 + access: + vlan: v101 + - name: ge-0/0/4 + trunk: + allowed_vlans: + - vlan30 + native_vlan: 50 + state: replaced + +# After state: +# ------------ +# user@junos01# show interfaces +# ge-0/0/3 { +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members v101; +# } +# } +# } +# } +# ge-0/0/4 { +# description interface-trunk; +# native-vlan-id 50; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan30 ]; +# } +# } +# } +# } +# Using gathered +# Before state: +# ------------ +# +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible"; +# disable; +# speed 100m; +# mtu 1024; +# hold-time up 2000 down 2200; +# link-mode full-duplex; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members vlan100; +# } +# } +# } +# } +# ge-0/0/2 { +# description "Configured by Ansible"; +# native-vlan-id 400; +# speed 10m; +# mtu 2048; +# hold-time up 3000 down 3200; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan200 vlan300 ]; +# } +# } +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +- name: Gather junos layer 2 interfaces as in given arguments + junipernetworks.junos.junos_l2_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "access": { +# "vlan": "vlan100" +# }, +# "enhanced_layer": true, +# "name": "ge-0/0/1", +# "unit": 0 +# }, +# { +# "enhanced_layer": true, +# "name": "ge-0/0/2", +# "trunk": { +# "allowed_vlans": [ +# "vlan200", +# "vlan300" +# ], +# "native_vlan": "400" +# }, +# "unit": 0 +# } +# ] +# After state: +# ------------ +# +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible"; +# disable; +# speed 100m; +# mtu 1024; +# hold-time up 2000 down 2200; +# link-mode full-duplex; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members vlan100; +# } +# } +# } +# } +# ge-0/0/2 { +# description "Configured by Ansible"; +# native-vlan-id 400; +# speed 10m; +# mtu 2048; +# hold-time up 3000 down 3200; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan200 vlan300 ]; +# } +# } +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <interfaces> +# <interface> +# <name>ge-0/0/1</name> +# <description>Configured by Ansible</description> +# <disable/> +# <speed>100m</speed> +# <mtu>1024</mtu> +# <hold-time> +# <up>2000</up> +# <down>2200</down> +# </hold-time> +# <link-mode>full-duplex</link-mode> +# <unit> +# <name>0</name> +# <family> +# <ethernet-switching> +# <interface-mode>access</interface-mode> +# <vlan> +# <members>vlan100</members> +# </vlan> +# </ethernet-switching> +# </family> +# </unit> +# </interface> +# </interfaces> +# </configuration> +# </rpc-reply> +# - name: Convert interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_l2_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "access": { +# "vlan": "vlan100" +# }, +# "enhanced_layer": true, +# "name": "ge-0/0/1", +# "unit": 0 +# }, +# { +# "enhanced_layer": true, +# "name": "ge-0/0/2", +# "trunk": { +# "allowed_vlans": [ +# "vlan200", +# "vlan300" +# ], +# "native_vlan": "400" +# }, +# "unit": 0 +# } +# ] +# +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_l2_interfaces: + config: + - name: ge-0/0/1 + access: + vlan: vlan100 + - name: ge-0/0/2 + trunk: + allowed_vlans: + - vlan200 + - vlan300 + native_vlan: '400' + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:interfaces +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:interface> +# <nc:name>ge-0/0/1</nc:name> +# <nc:unit> +# <nc:name>0</nc:name> +# <nc:family> +# <nc:ethernet-switching> +# <nc:interface-mode>access</nc:interface-mode> +# <nc:vlan> +# <nc:members>vlan100</nc:members> +# </nc:vlan> +# </nc:ethernet-switching> +# </nc:family> +# </nc:unit> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/2</nc:name> +# <nc:unit> +# <nc:name>0</nc:name> +# <nc:family> +# <nc:ethernet-switching> +# <nc:interface-mode>trunk</nc:interface-mode> +# <nc:vlan> +# <nc:members>vlan200</nc:members> +# <nc:members>vlan300</nc:members> +# </nc:vlan> +# </nc:ethernet-switching> +# </nc:family> +# </nc:unit> +# <nc:native-vlan-id>400</nc:native-vlan-id> +# </nc:interface> +# </nc:interfaces>" + +""" +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: ['<nc:interfaces + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:interface> + <nc:name>ge-0/0/1</nc:name> + <nc:unit> + <nc:name>0</nc:name> + <nc:family> + <nc:ethernet-switching> + <nc:interface-mode>access</nc:interface-mode> + <nc:vlan> + <nc:members>vlan100</nc:members> + </nc:vlan> + </nc:ethernet-switching> + </nc:family> + </nc:unit> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/2</nc:name> + <nc:unit> + <nc:name>0</nc:name> + <nc:family> + <nc:ethernet-switching> + <nc:interface-mode>trunk</nc:interface-mode> + <nc:vlan> + <nc:members>vlan200</nc:members> + <nc:members>vlan300</nc:members> + </nc:vlan> + </nc:ethernet-switching> + </nc:family> + </nc:unit> + <nc:native-vlan-id>400</nc:native-vlan-id> + </nc:interface> + </nc:interfaces>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.l2_interfaces.l2_interfaces import ( + L2_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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",)), + ] + + module = AnsibleModule( + argument_spec=L2_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = L2_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_l3_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_l3_interfaces.py new file mode 100644 index 000000000..3e59e0844 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_l3_interfaces.py @@ -0,0 +1,777 @@ +#!/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 junos_l3_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_l3_interfaces +short_description: L3 interfaces resource module +description: This module provides declarative management of a Layer 3 interface on + Juniper JUNOS devices +version_added: 1.0.0 +author: Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of Layer 3 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of interface, e.g. ge-0/0/1 + type: str + required: true + unit: + description: + - Logical interface number. Value of C(unit) should be of type integer + default: 0 + type: int + ipv4: + description: + - IPv4 addresses to be set for the Layer 3 logical interface mentioned in + I(name) option. The address format is <ipv4 address>/<mask>. The mask is + number in range 0-32 for example, 192.0.2.1/24, or C(dhcp) to query DHCP + for an IP address + type: list + elements: dict + suboptions: + address: + description: + - IPv4 address to be set for the specific interface + type: str + ipv6: + description: + - IPv6 addresses to be set for the Layer 3 logical interface mentioned in + I(name) option. The address format is <ipv6 address>/<mask>, the mask is + number in range 0-128 for example, 2001:db8:2201:1::1/64 or C(auto-config) + to use SLAAC + type: list + elements: dict + suboptions: + address: + description: + - IPv6 address to be set for the specific 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 Junos device + by executing the command B(show interfaces). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state of the configuration after module completion + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 10.200.16.10/24; +# } +# } +# } +# ge-0/0/2 { +# description "non L3 interface"; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members 2; +# } +# } +# } +# } + +- name: Delete JUNOS L3 logical interface + junipernetworks.junos.junos_l3_interfaces: + config: + - name: ge-0/0/1 + - name: ge-0/0/2 + state: deleted + +# After state: +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "deleted L3 interface"; +# } +# ge-0/0/2 { +# description "non L3 interface"; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members 2; +# } +# } +# } +# } +# Using merged +# Before state +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 10.200.16.10/24; +# } +# } +# } +# ge-0/0/2 { +# description "non configured interface"; +# unit 0; +# } +- name: Merge provided configuration with device configuration (default operation is merge) + junipernetworks.junos.junos_l3_interfaces: + config: + - name: ge-0/0/1 + ipv4: + - address: 192.168.1.10/24 + ipv6: + - address: 8d8d:8d01::1/64 + - name: ge-0/0/2 + ipv4: + - address: dhcp + state: merged + +# After state: +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 10.200.16.10/24; +# address 192.168.1.10/24; +# } +# family inet6 { +# address 8d8d:8d01::1/64; +# } +# } +# } +# ge-0/0/2 { +# description "L3 interface with dhcp"; +# unit 0 { +# family inet { +# dhcp; +# } +# } +# } + + +# Using overridden + +# Before state +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 10.200.16.10/24; +# } +# } +# } +# ge-0/0/2 { +# description "L3 interface with dhcp"; +# unit 0 { +# family inet { +# dhcp; +# } +# } +# } +# ge-0/0/3 { +# description "another L3 interface"; +# unit 0 { +# family inet { +# address 192.168.1.10/24; +# } +# } +# } + +- name: Override provided configuration with device configuration + junipernetworks.junos.junos_l3_interfaces: + config: + - name: ge-0/0/1 + ipv4: + - address: 192.168.1.10/24 + ipv6: + - address: 8d8d:8d01::1/64 + - name: ge-0/0/2 + ipv6: + - address: 2001:db8:3000::/64 + state: overridden + +# After state: +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 192.168.1.10/24; +# } +# family inet6 { +# address 8d8d:8d01::1/64; +# } +# } +# } +# ge-0/0/2 { +# description "L3 interface with ipv6"; +# unit 0 { +# family inet6 { +# address 2001:db8:3000::/64; +# } +# } +# } +# ge-0/0/3 { +# description "overridden L3 interface"; +# unit 0; +# } + + +# Using replaced + +# Before state +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 10.200.16.10/24; +# } +# } +# } +# ge-0/0/2 { +# description "non configured interface"; +# unit 0; +# } +# ge-0/0/3 { +# description "another L3 interface"; +# unit 0 { +# family inet { +# address 192.168.1.10/24; +# } +# } +# } + +- name: Replace provided configuration with device configuration + junipernetworks.junos.junos_l3_interfaces: + config: + - name: ge-0/0/1 + ipv4: + - address: 192.168.1.10/24 + ipv6: + - address: 8d8d:8d01::1/64 + - name: ge-0/0/2 + ipv4: + - address: dhcp + state: replaced + +# After state: +# ------------ +# +# admin# show interfaces +# ge-0/0/1 { +# description "L3 interface"; +# unit 0 { +# family inet { +# address 192.168.1.10/24; +# } +# family inet6 { +# address 8d8d:8d01::1/64; +# } +# } +# } +# ge-0/0/2 { +# description "L3 interface with dhcp"; +# unit 0 { +# family inet { +# dhcp; +# } +# } +# } +# ge-0/0/3 { +# description "another L3 interface"; +# unit 0 { +# family inet { +# address 192.168.1.10/24; +# } +# } +# } +# Using gathered +# Before state: +# ------------ +# +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible"; +# disable; +# speed 100m; +# mtu 1024; +# hold-time up 2000 down 2200; +# link-mode full-duplex; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members vlan100; +# } +# } +# } +# } +# ge-0/0/2 { +# description "Configured by Ansible"; +# native-vlan-id 400; +# speed 10m; +# mtu 2048; +# hold-time up 3000 down 3200; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan200 vlan300 ]; +# } +# } +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +- name: Gather junos layer3 interfaces as in given arguments + junipernetworks.junos.junos_l3_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "ipv4": [ +# { +# "address": "192.168.100.1/24" +# }, +# { +# "address": "10.200.16.20/24" +# } +# ], +# "name": "ge-1/0/0", +# "unit": "0" +# }, +# { +# "ipv4": [ +# { +# "address": "192.168.100.2/24" +# }, +# { +# "address": "10.200.16.21/24" +# } +# ], +# "name": "ge-2/0/0", +# "unit": "0" +# }, +# { +# "ipv4": [ +# { +# "address": "192.168.100.3/24" +# }, +# { +# "address": "10.200.16.22/24" +# } +# ], +# "name": "ge-3/0/0", +# "unit": "0" +# }, +# { +# "ipv4": [ +# { +# "address": "10.8.38.38/24" +# } +# ], +# "name": "fxp0", +# "unit": "0" +# } +# ] +# After state: +# ------------ +# +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Configured by Ansible"; +# disable; +# speed 100m; +# mtu 1024; +# hold-time up 2000 down 2200; +# link-mode full-duplex; +# unit 0 { +# family ethernet-switching { +# interface-mode access; +# vlan { +# members vlan100; +# } +# } +# } +# } +# ge-0/0/2 { +# description "Configured by Ansible"; +# native-vlan-id 400; +# speed 10m; +# mtu 2048; +# hold-time up 3000 down 3200; +# unit 0 { +# family ethernet-switching { +# interface-mode trunk; +# vlan { +# members [ vlan200 vlan300 ]; +# } +# } +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <interfaces> +# <interface> +# <name>ge-1/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.1/24</name> +# </address> +# <address> +# <name>10.200.16.20/24</name> +# </address> +# </inet> +# <inet6></inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-2/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.2/24</name> +# </address> +# <address> +# <name>10.200.16.21/24</name> +# </address> +# </inet> +# <inet6></inet6> +# </family> +# </unit> +# </interface> +# </interfaces> +# </configuration> +# </rpc-reply> +# - name: Convert interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_l3_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "ipv4": [ +# { +# "address": "192.168.100.1/24" +# }, +# { +# "address": "10.200.16.20/24" +# } +# ], +# "name": "ge-1/0/0", +# "unit": "0" +# }, +# { +# "ipv4": [ +# { +# "address": "192.168.100.2/24" +# }, +# { +# "address": "10.200.16.21/24" +# } +# ], +# "name": "ge-2/0/0", +# "unit": "0" +# } +# ] +# +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_l3_interfaces: + config: + - name: ge-1/0/0 + ipv4: + - address: 192.168.100.1/24 + - address: 10.200.16.20/24 + unit: 0 + + - name: ge-2/0/0 + ipv4: + - address: 192.168.100.2/24 + - address: 10.200.16.21/24 + unit: 0 + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:interfaces +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:interface> +# <nc:name>ge-1/0/0</nc:name> +# <nc:unit> +# <nc:name>0</nc:name> +# <nc:family> +# <nc:inet> +# <nc:address> +# <nc:name>192.168.100.1/24</nc:name> +# </nc:address> +# <nc:address> +# <nc:name>10.200.16.20/24</nc:name> +# </nc:address> +# </nc:inet> +# </nc:family> +# </nc:unit> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-2/0/0</nc:name> +# <nc:unit> +# <nc:name>0</nc:name> +# <nc:family> +# <nc:inet> +# <nc:address> +# <nc:name>192.168.100.2/24</nc:name> +# </nc:address> +# <nc:address> +# <nc:name>10.200.16.21/24</nc:name> +# </nc:address> +# </nc:inet> +# </nc:family> +# </nc:unit> +# </nc:interface> +# </nc:interfaces>" + +""" +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The configuration as structured data after module completion. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:interfaces + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:interface> + <nc:name>ge-1/0/0</nc:name> + <nc:unit> + <nc:name>0</nc:name> + <nc:family> + <nc:inet> + <nc:address> + <nc:name>192.168.100.1/24</nc:name> + </nc:address> + <nc:address> + <nc:name>10.200.16.20/24</nc:name> + </nc:address> + </nc:inet> + </nc:family> + </nc:unit> +</nc:interfaces>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.l3_interfaces.l3_interfaces import ( + L3_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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",)), + ] + + module = AnsibleModule( + argument_spec=L3_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = L3_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp.py new file mode 100644 index 000000000..b2636f285 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp.py @@ -0,0 +1,305 @@ +#!/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 junos_lacp +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_lacp +short_description: Global Link Aggregation Control Protocol (LACP) Junos resource + module +description: This module provides declarative management of global LACP on Juniper + Junos network devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: A dictionary of LACP global options + type: dict + suboptions: + system_priority: + description: + - LACP priority for the system. + type: int + link_protection: + description: + - Enable LACP link-protection for the system. If the value is set to C(non-revertive) + it will not revert links when a better priority link comes up. By default + the link will be reverted. + type: str + choices: + - revertive + - non-revertive + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the Junos device + by executing the command B(show chassis aggregated-devices ethernet 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 + - gathered + - rendered + - parsed + default: merged +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 18.1R1. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +""" +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# user@junos01# show chassis aggregated-devices ethernet lacp +# system-priority 63; +# link-protection { +# non-revertive; +# } + +- name: Delete global LACP attributes + junipernetworks.junos.junos_lacp: + state: deleted + +# After state: +# ------------ +# user@junos01# show chassis aggregated-devices ethernet lacp +# + + +# Using merged + +# Before state: +# ------------- +# user@junos01# show chassis aggregated-devices ethernet lacp +# + +- name: Merge global LACP attributes + junipernetworks.junos.junos_lacp: + config: + system_priority: 63 + link_protection: revertive + state: merged + +# After state: +# ------------ +# user@junos01# show chassis aggregated-devices ethernet lacp +# system-priority 63; +# link-protection { +# non-revertive; +# } + + +# Using replaced + +# Before state: +# ------------- +# user@junos01# show chassis aggregated-devices ethernet lacp +# system-priority 63; +# link-protection { +# non-revertive; +# } + +- name: Replace global LACP attributes + junipernetworks.junos.junos_lacp: + config: + system_priority: 30 + link_protection: non-revertive + state: replaced + +# After state: +# ------------ +# user@junos01# show chassis aggregated-devices ethernet lacp +# system-priority 30; +# link-protection; +# +# Using gathered +# Before state: +# ------------ +# +# ansible@cm123456tr21# show chassis aggregated-devices ethernet lacp +# system-priority 63; +# link-protection; + +- name: Gather junos lacp as in given arguments + junipernetworks.junos.junos_lacp: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": { +# "link_protection": "revertive", +# "system_priority": 63 +# } +# After state: +# ------------ +# +# ansible@cm123456tr21# show chassis aggregated-devices ethernet lacp +# system-priority 63; +# link-protection; +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_lacp: + config: + system_priority: 63 + link_protection: revertive + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:chassis +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:aggregated-devices> +# <nc:ethernet> +# <nc:lacp> +# <nc:system-priority>63</nc:system-priority> +# <nc:link-protection> +# <nc:non-revertive delete=\"delete\"/> +# </nc:link-protection> +# </nc:lacp> +# </nc:ethernet> +# </nc:aggregated-devices> +# </nc:chassis> +# +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <chassis> +# <aggregated-devices> +# <ethernet> +# <lacp> +# <system-priority>63</system-priority> +# <link-protection> +# </link-protection> +# </lacp> +# </ethernet> +# </aggregated-devices> +# </chassis> +# </configuration> +# </rpc-reply> +# - name: Convert lacp config to argspec without connecting to the appliance +# junipernetworks.junos.junos_lacp: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": { +# "link_protection": "revertive", +# "system_priority": 63 +# } + +""" +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. +xml: + description: The set of xml rpc payload pushed to the remote device. + returned: always + type: list + sample: ['<nc:chassis + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:aggregated-devices> + <nc:ethernet> + <nc:lacp> + <nc:system-priority>63</nc:system-priority> + <nc:link-protection> + <nc:non-revertive delete=\"delete\"/> + </nc:link-protection> + </nc:lacp> + </nc:ethernet> + </nc:aggregated-devices> +</nc:chassis>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lacp.lacp import ( + LacpArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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",)), + ] + + module = AnsibleModule( + argument_spec=LacpArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lacp(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp_interfaces.py new file mode 100644 index 000000000..0c05a0c54 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lacp_interfaces.py @@ -0,0 +1,967 @@ +#!/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 junos_lacp_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_lacp_interfaces +short_description: LACP interfaces resource module +description: +- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces + on Juniper JUNOS devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: The list of dictionaries of LACP interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Name Identifier of the interface or link aggregation group. + type: str + period: + description: + - Timer interval for periodic transmission of LACP packets. If the value is + set to C(fast) the packets are received every second and if the value is + C(slow) the packets are received every 30 seconds. This value is applicable + for aggregate interface only. + type: str + choices: + - fast + - slow + sync_reset: + description: + - The argument notifies minimum-link failure out of sync to peer. If the value + is C(disable) it disables minimum-link failure handling at LACP level and + if value is C(enable) it enables minimum-link failure handling at LACP level. + This value is applicable for aggregate interface only. + type: str + choices: + - disable + - enable + force_up: + description: + - This is a boolean argument to control if the port should be up in absence + of received link Aggregation Control Protocol Data Unit (LACPDUS). This + value is applicable for member interfaces only. + type: bool + port_priority: + description: + - Priority of the member port. This value is applicable for member interfaces + only. + - Refer to vendor documentation for valid values. + type: int + system: + description: + - This dict object contains configurable options related to LACP system parameters + for the link aggregation group. This value is applicable for aggregate interface + only. + 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: dict + 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 Junos device + by executing the command B(show 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 of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad ae0; +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +- name: Merge provided configuration with device configuration + junipernetworks.junos.junos_lacp_interfaces: + config: + - name: ae0 + period: fast + sync_reset: enable + system: + priority: 100 + mac: + address: 00:00:00:00:00:02 + - name: ge-0/0/3 + port_priority: 100 + force_up: true + state: merged + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# periodic fast; +# sync-reset enable; +# system-priority 100; +# system-id 00:00:00:00:00:02; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +# Using replaced +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# periodic fast; +# sync-reset enable; +# system-priority 100; +# system-id 00:00:00:00:00:02; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +- name: Replace device LACP interfaces configuration with provided configuration + junipernetworks.junos.junos_lacp_interfaces: + config: + - name: ae0 + period: slow + state: replaced + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# periodic slow; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +# Using overridden +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# periodic slow; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +- name: Overrides all device LACP interfaces configuration with provided configuration + junipernetworks.junos.junos_lacp_interfaces: + config: + - name: ae0 + system: + priority: 300 + mac: + address: 00:00:00:00:00:03 + - name: ge-0/0/2 + port_priority: 200 + force_up: false + state: overridden + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad { +# lacp { +# port-priority 200; +# } +# ae4; +# } +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# system-priority 300; +# system-id 00:00:00:00:00:03; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +# Using deleted +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad { +# lacp { +# port-priority 200; +# } +# ae4; +# } +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae0; +# } +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# system-priority 300; +# system-id 00:00:00:00:00:03; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } + +- name: "Delete LACP interfaces attributes of given interfaces (Note: This won't delete the interface itself)" + junipernetworks.junos.junos_lacp_interfaces: + config: + - name: ae0 + - name: ge-0/0/3 + - name: ge-0/0/2 + state: deleted + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/2 { +# ether-options { +# 802.3ad ae4; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad ae0; +# } +# } +# ae0 { +# description "lag interface merged"; +# aggregated-ether-options { +# lacp { +# passive; +# } +# } +# } +# ae4 { +# description "test aggregate interface"; +# aggregated-ether-options { +# lacp { +# passive; +# link-protection; +# } +# } +# } +# Using gathered +# Before state: +# ------------ +# +# user@junos01# show interfaces +# ansible@cm123456tr21# show interfaces +# ge-0/0/1 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae1; +# } +# } +# } +# ge-0/0/2 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad ae2; +# } +# } +# ge-0/0/4 { +# ether-options { +# 802.3ad ae2; +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# ae1 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# lacp { +# periodic fast; +# sync-reset enable; +# system-priority 100; +# system-id 00:00:00:00:00:02; +# } +# } +# } +# ae2 { +# description "Configured by Ansible"; +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +- name: Gather junos lacp interfaces as in given arguments + junipernetworks.junos.junos_lacp_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "force_up": true, +# "name": "ge-0/0/1", +# "port_priority": 100 +# }, +# { +# "name": "ae1", +# "period": "fast", +# "sync_reset": "enable", +# "system": { +# "mac": { +# "address": "00:00:00:00:00:02" +# }, +# "priority": 100 +# } +# } +# ] +# After state: +# ------------ +# +# ansible@cm123456tr21# show interfaces +# ge-0/0/1 { +# ether-options { +# 802.3ad { +# lacp { +# force-up; +# port-priority 100; +# } +# ae1; +# } +# } +# } +# ge-0/0/2 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad ae2; +# } +# } +# ge-0/0/4 { +# ether-options { +# 802.3ad ae2; +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# ae1 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# lacp { +# periodic fast; +# sync-reset enable; +# system-priority 100; +# system-id 00:00:00:00:00:02; +# } +# } +# } +# ae2 { +# description "Configured by Ansible"; +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <interfaces> +# <interface> +# <name>ge-0/0/1</name> +# <ether-options> +# <ieee-802.3ad> +# <lacp> +# <force-up/> +# <port-priority>100</port-priority> +# </lacp> +# <bundle>ae1</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/2</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae1</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/3</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae2</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/4</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae2</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-1/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.1/24</name> +# </address> +# <address> +# <name>10.200.16.20/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-2/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.2/24</name> +# </address> +# <address> +# <name>10.200.16.21/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-3/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.3/24</name> +# </address> +# <address> +# <name>10.200.16.22/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ae1</name> +# <description>Configured by Ansible</description> +# <aggregated-ether-options> +# <lacp> +# <periodic>fast</periodic> +# <sync-reset>enable</sync-reset> +# <system-priority>100</system-priority> +# <system-id>00:00:00:00:00:02</system-id> +# </lacp> +# </aggregated-ether-options> +# </interface> +# <interface> +# <name>ae2</name> +# <description>Configured by Ansible</description> +# </interface> +# <interface> +# <name>em1</name> +# <description>TEST</description> +# </interface> +# <interface> +# <name>fxp0</name> +# <description>ANSIBLE</description> +# <speed>1g</speed> +# <link-mode>automatic</link-mode> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>10.8.38.38/24</name> +# </address> +# </inet> +# </family> +# </unit> +# </interface> +# </interfaces> +# </configuration> +# </rpc-reply> +# - name: Convert interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_lacp_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "force_up": true, +# "name": "ge-0/0/1", +# "port_priority": 100 +# }, +# { +# "name": "ae1", +# "period": "fast", +# "sync_reset": "enable", +# "system": { +# "mac": { +# "address": "00:00:00:00:00:02" +# }, +# "priority": 100 +# } +# } +# ] +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_lacp_interfaces: + config: + - name: ae1 + period: fast + sync_reset: enable + system: + priority: 100 + mac: + address: 00:00:00:00:00:02 + + - name: ge-0/0/1 + port_priority: 100 + force_up: true + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:interfaces +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:interface> +# <nc:name>ae1</nc:name> +# <nc:aggregated-ether-options> +# <nc:lacp> +# <nc:periodic>fast</nc:periodic> +# <nc:sync-reset>enable</nc:sync-reset> +# <nc:system-id>00:00:00:00:00:02</nc:system-id> +# <nc:system-priority>100</nc:system-priority> +# </nc:lacp> +# </nc:aggregated-ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/1</nc:name> +# <nc:ether-options> +# <nc:ieee-802.3ad> +# <nc:lacp> +# <nc:port-priority>100</nc:port-priority> +# <nc:force-up/> +# </nc:lacp> +# </nc:ieee-802.3ad> +# </nc:ether-options> +# </nc:interface> +# </nc:interfaces>" + +""" + +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: ['<nc:interfaces + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:interface> + <nc:name>ae1</nc:name> + <nc:aggregated-ether-options> + <nc:lacp> + <nc:periodic>fast</nc:periodic> + <nc:sync-reset>enable</nc:sync-reset> + <nc:system-id>00:00:00:00:00:02</nc:system-id> + <nc:system-priority>100</nc:system-priority> + </nc:lacp> + </nc:aggregated-ether-options> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/1</nc:name> + <nc:ether-options> + <nc:ieee-802.3ad> + <nc:lacp> + <nc:port-priority>100</nc:port-priority> + <nc:force-up/> + </nc:lacp> + </nc:ieee-802.3ad> + </nc:ether-options> + </nc:interface> +</nc:interfaces>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=Lacp_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lacp_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_lag_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lag_interfaces.py new file mode 100644 index 000000000..81efca836 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lag_interfaces.py @@ -0,0 +1,887 @@ +#!/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 junos_lag_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_lag_interfaces +short_description: Link Aggregation Juniper JUNOS resource module +description: This module manages properties of Link Aggregation Group on Juniper JUNOS + devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: A list of link aggregation group configurations. + type: list + elements: dict + suboptions: + name: + description: + - Name of the link aggregation group (LAG). + type: str + required: true + mode: + description: + - LAG mode. A value of C(passive) will enable LACP in C(passive) mode that + is it will respond to LACP packets and C(active) configures the link to + initiate transmission of LACP packets. + type: str + choices: + - active + - passive + link_protection: + description: + - This boolean option indicates if link protection should be enabled for the + LAG interface. If value is C(True) link protection is enabled on LAG and + if value is C(False) link protection is disabled. + type: bool + members: + description: + - List of member interfaces of the link aggregation group. The value can be + single interface or list of interfaces. + type: list + elements: dict + suboptions: + member: + description: + - Name of the member interface. + type: str + link_type: + description: + - The value of this options configures the member link as either C(primary) + or C(backup). Value C(primary) configures primary interface for link-protection + mode and C(backup) configures backup interface for link-protection mode. + type: str + choices: + - primary + - backup + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the Junos device + by executing the command B(show interfaces). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state of the configuration after module completion + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 18.4R1. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ae0 { +# description "lag interface"; +# } +# ae1 { +# description "lag interface 1"; +# } + +- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)" + junipernetworks.junos.junos_lag_interfaces: + config: + - name: ae0 + - name: ae1 + state: deleted + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# } + + +# Using merged + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# } + +- name: Merge provided configuration with device configuration + junipernetworks.junos.junos_lag_interfaces: + config: + - name: ae0 + members: + - member: ge-0/0/1 + link_type: primary + - member: ge-0/0/2 + link_type: backup + state: merged + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# ether-options { +# 802.3ad { +# ae0; +# primary; +# } +# } +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# ether-options { +# 802.3ad { +# ae0; +# backup; +# } +# } +# } + + +# Using merged + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ae0 { +# description "lag interface"; +# } +# ae3 { +# description "lag interface 3"; +# } + +- name: Overrides all device LAG configuration with provided configuration + junipernetworks.junos.junos_lag_interfaces: + config: + - name: ae0 + members: + - member: ge-0/0/2 + - name: ae1 + members: + - member: ge-0/0/1 + mode: passive + state: overridden + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ae0 { +# description "lag interface"; +# } +# ae1 { +# aggregated-ether-options { +# lacp { +# active; +# } +# } +# } + + +# Using merged + +# Before state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# } +# ge-0/0/3 { +# description "Ansible configured interface 3"; +# } + +- name: Replace device LAG configuration with provided configuration + junipernetworks.junos.junos_lag_interfaces: + config: + - name: ae0 + members: + - member: ge-0/0/1 + mode: active + state: replaced + +# After state: +# ------------- +# user@junos01# show interfaces +# ge-0/0/1 { +# description "Ansible configured interface 1"; +# ether-options { +# 802.3ad ae0; +# } +# } +# ge-0/0/2 { +# description "Ansible configured interface 2"; +# } +# ae0 { +# aggregated-ether-options { +# lacp { +# active; +# } +# } +# } +# ge-0/0/3 { +# description "Ansible configured interface 3"; +# } +# Using gathered +# Before state: +# ------------ +# +# ansible@cm123456tr21# show interfaces +# ge-0/0/1 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/2 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# ae2; +# primary; +# } +# } +# } +# ge-0/0/4 { +# ether-options { +# 802.3ad { +# ae2; +# backup; +# } +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# ae1 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# lacp { +# active; +# } +# } +# } +# ae2 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# link-protection; +# lacp { +# passive; +# } +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +- name: Gather junos lag interfaces as in given arguments + junipernetworks.junos.junos_lag_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "members": [ +# { +# "member": "ge-0/0/1" +# }, +# { +# "member": "ge-0/0/2" +# } +# ], +# "mode": "active", +# "name": "ae1" +# }, +# { +# "link_protection": true, +# "members": [ +# { +# "link_type": "primary", +# "member": "ge-0/0/3" +# }, +# { +# "link_type": "backup", +# "member": "ge-0/0/4" +# } +# ], +# "mode": "passive", +# "name": "ae2" +# } +# ] +# After state: +# ------------ +# +# ansible@cm123456tr21# show interfaces +# ge-0/0/1 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/2 { +# ether-options { +# 802.3ad ae1; +# } +# } +# ge-0/0/3 { +# ether-options { +# 802.3ad { +# ae2; +# primary; +# } +# } +# } +# ge-0/0/4 { +# ether-options { +# 802.3ad { +# ae2; +# backup; +# } +# } +# } +# ge-1/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.1/24; +# address 10.200.16.20/24; +# } +# family inet6; +# } +# } +# ge-2/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.2/24; +# address 10.200.16.21/24; +# } +# family inet6; +# } +# } +# ge-3/0/0 { +# unit 0 { +# family inet { +# address 192.168.100.3/24; +# address 10.200.16.22/24; +# } +# family inet6; +# } +# } +# ae1 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# lacp { +# active; +# } +# } +# } +# ae2 { +# description "Configured by Ansible"; +# aggregated-ether-options { +# link-protection; +# lacp { +# passive; +# } +# } +# } +# em1 { +# description TEST; +# } +# fxp0 { +# description ANSIBLE; +# speed 1g; +# link-mode automatic; +# unit 0 { +# family inet { +# address 10.8.38.38/24; +# } +# } +# } +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <interfaces> +# <interface> +# <name>ge-0/0/1</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae1</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/2</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae1</bundle> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/3</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae2</bundle> +# <primary/> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-0/0/4</name> +# <ether-options> +# <ieee-802.3ad> +# <bundle>ae2</bundle> +# <backup/> +# </ieee-802.3ad> +# </ether-options> +# </interface> +# <interface> +# <name>ge-1/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.1/24</name> +# </address> +# <address> +# <name>10.200.16.20/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-2/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.2/24</name> +# </address> +# <address> +# <name>10.200.16.21/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-3/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>192.168.100.3/24</name> +# </address> +# <address> +# <name>10.200.16.22/24</name> +# </address> +# </inet> +# <inet6> +# </inet6> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ae1</name> +# <description>Configured by Ansible</description> +# <aggregated-ether-options> +# <lacp> +# <active/> +# </lacp> +# </aggregated-ether-options> +# </interface> +# <interface> +# <name>ae2</name> +# <description>Configured by Ansible</description> +# <aggregated-ether-options> +# <link-protection> +# </link-protection> +# <lacp> +# <passive/> +# </lacp> +# </aggregated-ether-options> +# </interface> +# <interface> +# <name>em1</name> +# <description>TEST</description> +# </interface> +# <interface> +# <name>fxp0</name> +# <description>ANSIBLE</description> +# <speed>1g</speed> +# <link-mode>automatic</link-mode> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>10.8.38.38/24</name> +# </address> +# </inet> +# </family> +# </unit> +# </interface> +# </interfaces> +# </configuration> +# </rpc-reply> +# - name: Convert interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_lag_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "members": [ +# { +# "member": "ge-0/0/1" +# }, +# { +# "member": "ge-0/0/2" +# } +# ], +# "mode": "active", +# "name": "ae1" +# }, +# { +# "link_protection": true, +# "members": [ +# { +# "link_type": "primary", +# "member": "ge-0/0/3" +# }, +# { +# "link_type": "backup", +# "member": "ge-0/0/4" +# } +# ], +# "mode": "passive", +# "name": "ae2" +# } +# ] +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_lag_interfaces: + config: + - name: ae1 + members: + - member: ge-0/0/1 + - member: ge-0/0/2 + mode: active + + - name: ae2 + link_protection: true + members: + - member: ge-0/0/3 + link_type: primary + - member: ge-0/0/4 + link_type: backup + mode: passive +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:interfaces +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:interface> +# <nc:name>ae1</nc:name> +# <nc:aggregated-ether-options> +# <nc:lacp> +# <nc:active/> +# </nc:lacp> +# </nc:aggregated-ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/1</nc:name> +# <nc:ether-options> +# <nc:ieee-802.3ad> +# <nc:bundle>ae1</nc:bundle> +# </nc:ieee-802.3ad> +# </nc:ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/2</nc:name> +# <nc:ether-options> +# <nc:ieee-802.3ad> +# <nc:bundle>ae1</nc:bundle> +# </nc:ieee-802.3ad> +# </nc:ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ae2</nc:name> +# <nc:aggregated-ether-options> +# <nc:lacp> +# <nc:passive/> +# </nc:lacp> +# <nc:link-protection/> +# </nc:aggregated-ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/3</nc:name> +# <nc:ether-options> +# <nc:ieee-802.3ad> +# <nc:bundle>ae2</nc:bundle> +# <nc:primary/> +# </nc:ieee-802.3ad> +# </nc:ether-options> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/4</nc:name> +# <nc:ether-options> +# <nc:ieee-802.3ad> +# <nc:bundle>ae2</nc:bundle> +# <nc:backup/> +# </nc:ieee-802.3ad> +# </nc:ether-options> +# </nc:interface> +# </nc:interfaces>" + +""" +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. +xml: + description: The set of xml rpc payload pushed to the remote device. + returned: always + type: list + sample: ['<nc:interfaces + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:interface> + <nc:name>ae1</nc:name> + <nc:aggregated-ether-options> + <nc:lacp> + <nc:active/> + </nc:lacp> + </nc:aggregated-ether-options> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/1</nc:name> + <nc:ether-options> + <nc:ieee-802.3ad> + <nc:bundle>ae1</nc:bundle> + </nc:ieee-802.3ad> + </nc:ether-options> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/2</nc:name> + <nc:ether-options> + <nc:ieee-802.3ad> + <nc:bundle>ae1</nc:bundle> + </nc:ieee-802.3ad> + </nc:ether-options> + </nc:interface> + <nc:interface> + <nc:name>ae2</nc:name> + <nc:aggregated-ether-options> + <nc:lacp> + <nc:passive/> + </nc:lacp> + <nc:link-protection/> + </nc:aggregated-ether-options> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/3</nc:name> + <nc:ether-options> + <nc:ieee-802.3ad> + <nc:bundle>ae2</nc:bundle> + <nc:primary/> + </nc:ieee-802.3ad> + </nc:ether-options> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/4</nc:name> + <nc:ether-options> + <nc:ieee-802.3ad> + <nc:bundle>ae2</nc:bundle> + <nc:backup/> + </nc:ieee-802.3ad> + </nc:ether-options> + </nc:interface> +</nc:interfaces>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.lag_interfaces.lag_interfaces import ( + Lag_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=Lag_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lag_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_global.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_global.py new file mode 100644 index 000000000..ebf826ce6 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_global.py @@ -0,0 +1,331 @@ +#!/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 junos_lldp_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_lldp_global +short_description: LLDP resource module +description: +- This module manages link layer discovery protocol (LLDP) attributes on Juniper JUNOS + devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: The list of link layer discovery protocol attribute configurations + type: dict + suboptions: + enabled: + description: + - This argument is a boolean value to enabled or disable LLDP. + type: bool + interval: + description: + - Frequency at which LLDP advertisements are sent (in seconds). + type: int + address: + description: + - This argument sets the management address from LLDP. + type: str + transmit_delay: + description: + - Specify the number of seconds the device waits before sending advertisements + to neighbors after a change is made in local system. + type: int + hold_multiplier: + description: + - Specify the number of seconds that LLDP information is held before it is + discarded. The multiplier value is used in combination with the C(interval) + value. + 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 Junos device + by executing the command B(show protocols lldp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - deleted + - gathered + - rendered + - parsed + default: merged +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 18.4R1. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +""" +EXAMPLES = """ +# Using merged +# Before state: +# ------------- +# user@junos01# # show protocols lldp +# +- name: Merge provided configuration with device configuration + junipernetworks.junos.junos_lldp_global: + config: + interval: 10000 + address: 10.1.1.1 + transmit_delay: 400 + hold_multiplier: 10 + state: merged + +# After state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# transmit-delay 400; +# hold-multiplier 10; + +# Using replaced +# Before state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# transmit-delay 400; +# hold-multiplier 10; + +- name: Replace provided configuration with device configuration + junipernetworks.junos.junos_lldp_global: + config: + address: 20.2.2.2 + hold_multiplier: 30 + enabled: false + state: replaced + +# After state: +# ------------- +# user@junos01# show protocols lldp +# disable; +# management-address 20.2.2.2; +# hold-multiplier 30; + +# Using deleted +# Before state: +# ------------- +# user@junos01# show protocols lldp +# management-address 20.2.2.2; +# hold-multiplier 30; + +- name: Delete lldp configuration (this will by default remove all lldp configuration) + junipernetworks.junos.junos_lldp_global: + state: deleted + +# After state: +# ------------- +# user@junos01# # show protocols lldp +# +# +# Using gathered +# Before state: +# ------------ +# +# ansible@cm123456tr21# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# transmit-delay 400; +# hold-multiplier 10; +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } +- name: Gather junos lldp_global as in given arguments + junipernetworks.junos.junos_lldp_global: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": { +# "address": "10.1.1.1", +# "hold_multiplier": 10, +# "interval": 10000, +# "transmit_delay": 400 +# } +# After state: +# ------------ +# +# ansible@cm123456tr21# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# transmit-delay 400; +# hold-multiplier 10; +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_lldp_global: + config: + interval: 10000 + address: 10.1.1.1 + transmit_delay: 400 + hold_multiplier: 10 + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:protocols +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:lldp> +# <nc:management-address>10.1.1.1</nc:management-address> +# <nc:advertisement-interval>10000</nc:advertisement-interval> +# <nc:transmit-delay>400</nc:transmit-delay> +# <nc:hold-multiplier>10</nc:hold-multiplier> +# <nc:disable delete=\"delete\"/> +# </nc:lldp> +# </nc:protocols>" +# +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <protocols> +# <ospf> +# <area> +# <name>0.0.0.0</name> +# <interface> +# <name>ge-0/0/0.0</name> +# </interface> +# </area> +# </ospf> +# <lldp> +# <management-address>10.1.1.1</management-address> +# <advertisement-interval>10000</advertisement-interval> +# <transmit-delay>400</transmit-delay> +# <hold-multiplier>10</hold-multiplier> +# <interface> +# <name>ge-0/0/1</name> +# </interface> +# <interface> +# <name>ge-0/0/2</name> +# <disable/> +# </interface> +# </lldp> +# </protocols> +# </configuration> +# </rpc-reply> +# - name: Convert lldp global config to argspec without connecting to the appliance +# junipernetworks.junos.junos_lldp_global: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": { +# "address": "10.1.1.1", +# "hold_multiplier": 10, +# "interval": 10000, +# "transmit_delay": 400 +# } +""" +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: ['<nc:protocols + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:lldp> + <nc:management-address>10.1.1.1</nc:management-address> + <nc:advertisement-interval>10000</nc:advertisement-interval> + <nc:transmit-delay>400</nc:transmit-delay> + <nc:hold-multiplier>10</nc:hold-multiplier> + <nc:disable delete=\"delete\"/> + </nc:lldp> +</nc:protocols>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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",)), + ] + + module = AnsibleModule( + argument_spec=Lldp_globalArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lldp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_interfaces.py new file mode 100644 index 000000000..b3d1b61ed --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_lldp_interfaces.py @@ -0,0 +1,359 @@ +#!/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 junos_lldp_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_lldp_interfaces +short_description: LLDP interfaces resource module +description: +- This module manages link layer discovery protocol (LLDP) attributes of interfaces + on Juniper JUNOS devices. +version_added: 1.0.0 +author: Ganesh Nalawade (@ganeshrn) +options: + config: + description: The list of link layer discovery protocol interface attribute configurations + type: list + elements: dict + suboptions: + name: + description: + - Name of the interface LLDP needs to be configured on. + type: str + required: true + enabled: + description: + - This is a boolean value to control disabling of LLDP on the interface C(name) + 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 Junos device + by executing the command B(show protocols lldp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged +# Before state: +# ------------- +# user@junos01# # show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; + +- name: Merge provided configuration with device configuration + junipernetworks.junos.junos_lldp_interfaces: + config: + - name: ge-0/0/1 + - name: ge-0/0/2 + enabled: false + state: merged + +# After state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } + +# Using replaced +# Before state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } + +- name: Replace provided configuration with device configuration + junipernetworks.junos.junos_lldp_interfaces: + config: + - name: ge-0/0/2 + disable: false + - name: ge-0/0/3 + enabled: false + state: replaced + +# After state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/1; +# interface ge-0/0/2; +# interface ge-0/0/3 { +# disable; +# } + +# Using overridden +# Before state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } + +- name: Override provided configuration with device configuration + junipernetworks.junos.junos_lldp_interfaces: + config: + - name: ge-0/0/2 + enabled: false + state: overridden + +# After state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/2 { +# disable; +# } + +# Using deleted +# Before state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/1; +# interface ge-0/0/2; +# interface ge-0/0/3 { +# disable; +# } +- name: Delete lldp interface configuration (this will not delete other lldp configuration) + junipernetworks.junos.junos_lldp_interfaces: + config: + - name: ge-0/0/1 + - name: ge-0/0/3 + state: deleted + +# After state: +# ------------- +# user@junos01# show protocols lldp +# management-address 10.1.1.1; +# advertisement-interval 10000; +# interface ge-0/0/2; +# interface ge-0/0/1; +# Using gathered +# Before state: +# ------------ +# +#ansible@cm123456tr21# show protocols lldp +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } +- name: Gather junos lldp interfaces as in given arguments + junipernetworks.junos.junos_lldp_interfaces: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "name": "ge-0/0/1" +# }, +# { +# "enabled": false, +# "name": "ge-0/0/2" +# } +# ] +# After state: +# ------------ +# +#ansible@cm123456tr21# show protocols lldp +# interface ge-0/0/1; +# interface ge-0/0/2 { +# disable; +# } +# Using rendered +- name: Render platform specific xml from task input using rendered state + junipernetworks.junos.junos_lldp_interfaces: + config: + - name: ge-0/0/1 + - name: ge-0/0/2 + enabled: false + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": "<nc:protocols +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:lldp> +# <nc:interface> +# <nc:name>ge-0/0/1</nc:name> +# <nc:disable delete=\"delete\"/> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-0/0/2</nc:name> +# <nc:disable/> +# </nc:interface> +# </nc:lldp> +# </nc:protocols>" +# Using parsed +# parsed.cfg +# ------------ +# +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <protocols> +# <ospf> +# <area> +# <name>0.0.0.0</name> +# <interface> +# <name>ge-0/0/0.0</name> +# </interface> +# </area> +# </ospf> +# <lldp> +# <interface> +# <name>ge-0/0/1</name> +# </interface> +# <interface> +# <name>ge-0/0/2</name> +# <disable/> +# </interface> +# </lldp> +# </protocols> +# </configuration> +# </rpc-reply> +# - name: Convert lldp interfaces config to argspec without connecting to the appliance +# junipernetworks.junos.junos_lldp_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "name": "ge-0/0/1" +# }, +# { +# "enabled": false, +# "name": "ge-0/0/2" +# } +# ] +""" + +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The configuration as structured data after module completion. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:protocols + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:lldp> + <nc:interface> + <nc:name>ge-0/0/1</nc:name> + <nc:disable delete=\"delete\"/> + </nc:interface> + <nc:interface> + <nc:name>ge-0/0/2</nc:name> + <nc:disable/> + </nc:interface> + </nc:lldp> +</nc:protocols>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.lldp_interfaces.lldp_interfaces import ( + Lldp_interfaces, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=Lldp_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lldp_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging.py new file mode 100644 index 000000000..c2713a855 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging.py @@ -0,0 +1,403 @@ +#!/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: junos_logging +author: Ganesh Nalawade (@ganeshrn) +short_description: Manage logging on network devices +description: +- This module provides declarative management of logging on Juniper JUNOS devices. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +deprecated: + alternative: junos_logging_global + why: Updated module released with more functionality. + removed_at_date: '2023-08-01' +options: + dest: + description: + - Destination of the logs. + choices: + - console + - host + - file + - user + type: str + name: + description: + - If value of C(dest) is I(file) it indicates file-name, for I(user) it indicates + username and for I(host) indicates the host name to be notified. + type: str + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + type: str + aggregate: + description: + - List of logging definitions. + type: list + elements: dict + suboptions: + dest: + description: + - Destination of the logs. + choices: + - console + - host + - file + - user + type: str + name: + description: + - If value of C(dest) is I(file) it indicates file-name, for I(user) it indicates + username and for I(host) indicates the host name to be notified. + type: str + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + type: str + state: + description: + - State of the logging configuration. + type: str + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + type: bool + rotate_frequency: + description: + - Rotate log frequency in minutes, this is applicable if value of I(dest) is C(file). + The acceptable value is in range of 1 to 59. This controls the frequency after + which log file is rotated. + type: int + required: false + size: + description: + - Size of the file in archive, this is applicable if value of I(dest) is C(file). + The acceptable value is in range from 65536 to 1073741824 bytes. + type: int + required: false + files: + description: + - Number of files to be archived, this is applicable if value of I(dest) is C(file). + The acceptable value is in range from 1 to 1000. + type: int + required: false + state: + description: + - State of the logging configuration. + default: present + type: str + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + default: true + type: bool + rotate_frequency: + description: + - Rotate log frequency in minutes, this is applicable if value of I(dest) is C(file). + The acceptable value is in range of 1 to 59. This controls the frequency after + which log file is rotated. + type: int + required: false + size: + description: + - Size of the file in archive, this is applicable if value of I(dest) is C(file). + The acceptable value is in range from 65536 to 1073741824 bytes. + required: false + type: int + files: + description: + - Number of files to be archived, this is applicable if value of I(dest) is C(file). + The acceptable value is in range from 1 to 1000. + type: int + required: false +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: configure console logging + junipernetworks.junos.junos_logging: + dest: console + facility: any + level: critical + +- name: remove console logging configuration + junipernetworks.junos.junos_logging: + dest: console + state: absent + +- name: configure file logging + junipernetworks.junos.junos_logging: + dest: file + name: test + facility: pfe + level: error + +- name: configure logging parameter + junipernetworks.junos.junos_logging: + files: 30 + size: 65536 + rotate_frequency: 10 + +- name: Configure file logging using aggregate + junipernetworks.junos.junos_logging: + dest: file + aggregate: + - name: test-1 + facility: pfe + level: critical + - name: test-2 + facility: kernel + level: emergency + active: true + +- name: Delete file logging using aggregate + junipernetworks.junos.junos_logging: + aggregate: + - {dest: file, name: test-1, facility: pfe, level: critical} + - {dest: file, name: test-2, facility: kernel, level: emergency} + state: absent +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: str + sample: > + [edit system syslog] + + [edit system syslog] + file interactive-commands { ... } + + file test { + + pfe critical; + + } +""" +import collections + +from copy import deepcopy + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.validation import check_required_if +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + map_obj_to_ele, + map_params_to_obj, + to_param_list, + tostring, +) + + +USE_PERSISTENT_CONNECTION = True + + +def validate_files(value, module): + if value and not 1 <= value <= 1000: + module.fail_json(msg="files must be between 1 and 1000") + + +def validate_size(value, module): + if value and not 65536 <= value <= 1073741824: + module.fail_json(msg="size must be between 65536 and 1073741824") + + +def validate_rotate_frequency(value, module): + if value and not 1 <= value <= 59: + module.fail_json(msg="rotate_frequency must be between 1 and 59") + + +def validate_param_values(module, obj, param=None): + if not param: + param = module.params + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(param.get(key), module) + + +def main(): + """main entry point for module execution""" + element_spec = dict( + dest=dict(choices=["console", "host", "file", "user"]), + name=dict(), + facility=dict(), + level=dict(), + rotate_frequency=dict(type="int"), + size=dict(type="int"), + files=dict(type="int"), + state=dict(default="present", choices=["present", "absent"]), + active=dict(default=True, type="bool"), + ) + + aggregate_spec = deepcopy(element_spec) + + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec), + ) + + argument_spec.update(element_spec) + + required_if = [ + ("dest", "host", ["name", "facility", "level"]), + ("dest", "file", ["name", "facility", "level"]), + ("dest", "user", ["name", "facility", "level"]), + ("dest", "console", ["facility", "level"]), + ] + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + warnings = list() + result = {"changed": False} + + if warnings: + result["warnings"] = warnings + + params = to_param_list(module) + + requests = list() + for param in params: + # if key doesn't exist in the item, get it from module.params + for key in param: + if param.get(key) is None: + param[key] = module.params[key] + + try: + check_required_if(required_if, param) + except TypeError as exc: + module.fail_json(to_text(exc)) + + item = param.copy() + dest = item.get("dest") + if dest == "console" and item.get("name"): + module.fail_json( + msg="%s and %s are mutually exclusive" % ("console", "name"), + ) + + top = "system/syslog" + is_facility_key = False + field_top = None + if dest: + if dest == "console": + field_top = dest + is_facility_key = True + else: + field_top = dest + "/contents" + is_facility_key = False + + param_to_xpath_map = collections.OrderedDict() + param_to_xpath_map.update( + [ + ("name", {"xpath": "name", "is_key": True, "top": dest}), + ( + "facility", + { + "xpath": "name", + "is_key": is_facility_key, + "top": field_top, + }, + ), + ( + "size", + { + "xpath": "size", + "leaf_only": True, + "is_key": True, + "top": "archive", + }, + ), + ( + "files", + { + "xpath": "files", + "leaf_only": True, + "is_key": True, + "top": "archive", + }, + ), + ( + "rotate_frequency", + {"xpath": "log-rotate-frequency", "leaf_only": True}, + ), + ], + ) + + if item.get("level"): + param_to_xpath_map["level"] = { + "xpath": item.get("level"), + "tag_only": True, + "top": field_top, + } + + validate_param_values(module, param_to_xpath_map, param=item) + + want = map_params_to_obj(module, param_to_xpath_map, param=item) + requests.append(map_obj_to_ele(module, want, top, param=item)) + + diff = None + with locked_config(module): + for req in requests: + diff = load_config(module, tostring(req), warnings, action="merge") + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging_global.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging_global.py new file mode 100644 index 000000000..bd4cdaadf --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_logging_global.py @@ -0,0 +1,1743 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_logging_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: junos_logging_global +version_added: 2.4.0 +short_description: Manage logging configuration on Junos devices. +description: This module manages logging configuration on devices running Junos. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show system syslog). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + config: + description: A dictionary of logging configuration. + type: dict + suboptions: + allow_duplicates: &allow_duplicates + description: Do not suppress the repeated message for all targets. + type: bool + archive: + description: Specify archive file information. + type: dict + suboptions: + set: + description: Set archive file information. + type: bool + binary_data: + description: Mark file as if it contains binary data. + type: bool + files: + description: Specify number of files to be archived (1..1000). + type: int + no_binary_data: + description: Don't mark file as if it contains binary data. + type: bool + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + file_size: + description: Size of files to be archived (65536..1073741824 bytes). + type: str + world_readable: + description: Allow any user to read the log file. + type: bool + console: + description: Set console logging parameters. + type: dict + suboptions: + any: &any + description: Set All facilities. + type: dict + suboptions: + level: &level + description: Set severity logging level. + type: str + required: true + choices: ["alert", "any", "critical", "emergency", "error", "info", "none", "notice", "warning"] + authorization: &authorization + description: Specify authorization system. + type: dict + suboptions: + level: *level + change_log: &change_log + description: Specify configuration change log. + type: dict + suboptions: + level: *level + conflict_log: &conflict_log + description: Specify configuration conflict log. + type: dict + suboptions: + level: *level + daemon: &daemon + description: Specify various system processes. + type: dict + suboptions: + level: *level + dfc: &dfc + description: Specify dynamic flow capture. + type: dict + suboptions: + level: *level + external: &external + description: Specify Local external applications. + type: dict + suboptions: + level: *level + firewall: &firewall + description: Specify Firewall filtering system. + type: dict + suboptions: + level: *level + ftp: &ftp + description: Specify FTP process. + type: dict + suboptions: + level: *level + interactive_commands: &interactive_commands + description: Specify commands executed by the UI. + type: dict + suboptions: + level: *level + kernel: &kernel + description: Specify Kernel specific logging. + type: dict + suboptions: + level: *level + ntp: &ntp + description: Specify NTP process specific logging. + type: dict + suboptions: + level: *level + pfe: &pfe + description: Specify Packet Forwarding Engine specific logging. + type: dict + suboptions: + level: *level + security: &security + description: Specify Security related logging. + type: dict + suboptions: + level: *level + user: &user + description: Specify user specific logging. + type: dict + suboptions: + level: *level + files: + description: Specify files logging. + type: list + elements: dict + suboptions: + name: + description: Specify filename in which to log data. + type: str + allow_duplicates: *allow_duplicates + any: *any + archive: + description: Specify archive file information. + type: dict + suboptions: + set: + description: Set archive file information. + type: bool + archive_sites: + description: Specify Primary and failover URLs to receive archive facilities. + type: list + elements: str + binary_data: + description: Mark file as if it contains binary data. + type: bool + files: + description: Specify number of files to be archived (1..1000). + type: int + no_binary_data: + description: Don't mark file as if it contains binary data. + type: bool + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + file_size: + description: Size of files to be archived (65536..1073741824 bytes). + type: str + start_time: + description: Specify start time for file transmission (yyyy-mm-dd.hh:mm). + type: str + transfer_interval: + description: Specify frequency at which to transfer files to archive sites (5..2880 minutes). + type: int + world_readable: + description: Allow any user to read the log file. + type: bool + authorization: *authorization + change_log: *change_log + conflict_log: *conflict_log + daemon: *daemon + dfc: *dfc + explicit_priority: &explicit_priority + description: Include priority and facility in messages. + type: bool + external: *external + firewall: *firewall + ftp: *ftp + interactive_commands: *interactive_commands + kernel: *kernel + match: &match + description: Specify regular expression for lines to be logged. + type: str + match_strings: &match_strings + description: Specify matching string(s) for lines to be logged. + type: list + elements: str + ntp: *ntp + pfe: *pfe + security: *security + structured_data: &structured_data + description: Specify Log system message in structured format. + type: dict + suboptions: + set: + description: Set Log system message in structured format. + type: bool + brief: + description: Omit English-language text from end of logged messages. + type: bool + user: *user + hosts: &hosts + description: Specify hosts to be notified. + type: list + elements: dict + suboptions: + name: + description: Specify the host name. + type: str + allow_duplicates: *allow_duplicates + any: *any + authorization: *authorization + change_log: *change_log + conflict_log: *conflict_log + daemon: *daemon + dfc: *dfc + exclude_hostname: + description: Specify exclude hostname field in messages. + type: bool + explicit_priority: *explicit_priority + external: *external + facility_override: + description: Specify alternate facility for logging to remote host. + type: str + firewall: *firewall + ftp: *ftp + interactive_commands: *interactive_commands + kernel: *kernel + log_prefix: + description: Prefix for all logging to this host. + type: str + match: *match + match_strings: *match_strings + ntp: *ntp + pfe: *pfe + port: + description: Specify port number. + type: int + routing_instance: + description: Specify routing-instance. + type: str + security: *security + source_address: + description: Specify address as source address. + type: str + structured_data: *structured_data + user: *user + log_rotate_frequency: + description: Specify Rotate log frequency (1..59 minutes). + type: int + routing_instance: + description: Specify Routing routing-instance. + type: str + server: + description: Specify syslog server logging. + type: dict + suboptions: + set: + description: Enable syslog server. + type: bool + routing_instance: + description: nable/disable syslog server in routing-instances. + type: dict + suboptions: + all: + description: Enable/disable all routing instances. + type: bool + default: + description: Enable/disable default routing instances. + type: bool + routing_instances: + description: Specify routing-instances. + type: list + elements: dict + suboptions: + name: + description: Specify routing-instance name. + type: str + disable: + description: Disable syslog server in this routing instances. + type: bool + source_address: + description: Specify address as source address. + type: str + time_format: + description: Specify additional information to include in system log timestamp. + type: dict + suboptions: + set: + description: Set time-format + type: bool + millisecond: + description: Include milliseconds in timestamp. + type: bool + year: + description: Include year in timestamp. + type: bool + users: + description: Specify user logging + type: list + elements: dict + suboptions: + name: + description: Specify user name. + type: str + allow_duplicates: *allow_duplicates + any: *any + authorization: *authorization + change_log: *change_log + conflict_log: *conflict_log + daemon: *daemon + dfc: *dfc + external: *external + firewall: *firewall + ftp: *ftp + interactive_commands: *interactive_commands + kernel: *kernel + match: + description: Specify regular expression for lines to be logged. + type: str + match_strings: + description: Specify matching string(s) for lines to be logged. + type: list + elements: str + ntp: *ntp + pfe: *pfe + security: *security + user: *user + state: + description: + - The state the configuration should be left in. + - Refer to examples for more details. + type: str + choices: + - merged + - replaced + - deleted + - overridden + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show system syslog +# +# [edit] +# vagrant@vsrx# show routing-instances +# inst11 { +# description inst11; +# } +- name: Merge provided logging configuration into running configuration. + junipernetworks.junos.junos_logging_global: + config: + allow_duplicates: true + archive: + set: true + no_binary_data: true + files: 10 + file_size: 65578 + no_world_readable: true + console: + any: + level: "info" + authorization: + level: "any" + change_log: + level: "critical" + ftp: + level: "none" + files: + - name: "file101" + allow_duplicates: true + - name: "file102" + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + - name: "file103" + archive: + set: true + no_binary_data: true + files: 10 + file_size: 65578 + no_world_readable: true + explicit_priority: true + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + hosts: + - name: host111 + exclude_hostname: true + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + brief: true + facility_override: "ftp" + log_prefix: "field" + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + port: 1231 + routing_instance: "inst11" + source_address: "11.1.1.11" + routing_instance: "inst11" + log_rotate_frequency: 45 + source_address: "33.33.33.33" + time_format: + millisecond: true + year: true + users: + - name: "user1" + allow_duplicates: true + - name: "user2" + allow_duplicates: true + any: + level: "any" + user: + level: info + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "allow_duplicates": true, +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "console": { +# "any": { +# "level": "info" +# }, +# "authorization": { +# "level": "any" +# }, +# "change_log": { +# "level": "critical" +# }, +# "ftp": { +# "level": "none" +# } +# }, +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file101" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# }, +# { +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "explicit_priority": true, +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "file103" +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host111", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "log_rotate_frequency": 45, +# "routing_instance": "inst11", +# "source_address": "33.33.33.33", +# "time_format": { +# "millisecond": true, +# "year": true +# }, +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:syslog><nc:allow-duplicates/><nc:archive><nc:files>10</nc:files>" +# "<nc:no-binary-data/><nc:size>65578</nc:size><nc:no-world-readable/></nc:archive>" +# "<nc:console><nc:name>change-log</nc:name><nc:critical/></nc:console><nc:console>" +# "<nc:name>any</nc:name><nc:info/></nc:console><nc:console><nc:name>authorization</nc:name>" +# "<nc:any/></nc:console><nc:console><nc:name>ftp</nc:name><nc:none/></nc:console><nc:file>" +# "<nc:name>file101</nc:name><nc:allow-duplicates/></nc:file><nc:file><nc:name>file102</nc:name>" +# "<nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/></nc:contents><nc:structured-data/>" +# "</nc:file><nc:file><nc:name>file103</nc:name><nc:archive><nc:files>10</nc:files><nc:no-binary-data/>" +# "<nc:size>65578</nc:size><nc:no-world-readable/></nc:archive><nc:explicit-priority/>" +# "<nc:match>^set*</nc:match><nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings></nc:file><nc:host><nc:name>host111</nc:name>" +# "<nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/></nc:contents>" +# "<nc:exclude-hostname/><nc:facility-override>ftp</nc:facility-override>" +# "<nc:log-prefix>field</nc:log-prefix><nc:match>^set*</nc:match><nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings><nc:port>1231</nc:port>" +# "<nc:routing-instance>inst11</nc:routing-instance><nc:source-address>11.1.1.11</nc:source-address>" +# "<nc:structured-data><nc:brief/></nc:structured-data></nc:host>" +# "<nc:log-rotate-frequency>45</nc:log-rotate-frequency><nc:routing-instance>inst11</nc:routing-instance>" +# "<nc:source-address>33.33.33.33</nc:source-address><nc:time-format><nc:millisecond/>" +# "<nc:year/></nc:time-format><nc:user><nc:name>user1</nc:name><nc:allow-duplicates/></nc:user>" +# "<nc:user><nc:name>user2</nc:name><nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/>" +# "</nc:contents><nc:contents><nc:name>user</nc:name><nc:info/></nc:contents></nc:user></nc:syslog></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system syslog +# archive size 65578 files 10 no-world-readable no-binary-data; +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host111 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# allow-duplicates; +# file file101 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +# file file103 { +# match "^set*"; +# archive size 65578 files 10 no-world-readable no-binary-data; +# explicit-priority; +# match-strings [ "^delete" "^prompt" ]; +# } +# console { +# any info; +# authorization any; +# ftp none; +# change-log critical; +# } +# time-format year millisecond; +# source-address 33.33.33.33; +# routing-instance inst11; +# log-rotate-frequency 45; +# Using replaced +# +# Before state +# ------------ +# +# vagrant@vsrx# show system syslog +# archive size 65578 files 10 no-world-readable no-binary-data; +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host111 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# allow-duplicates; +# file file101 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +# file file103 { +# match "^set*"; +# archive size 65578 files 10 no-world-readable no-binary-data; +# explicit-priority; +# match-strings [ "^delete" "^prompt" ]; +# } +# console { +# any info; +# authorization any; +# ftp none; +# change-log critical; +# } +# time-format year millisecond; +# source-address 33.33.33.33; +# routing-instance inst11; +# log-rotate-frequency 45; +- name: Replaced running logging global configuration with provided configuration + junipernetworks.junos.junos_logging_global: + config: + files: + - name: "file104" + allow_duplicates: true + - name: "file102" + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + hosts: + - name: host222 + exclude_hostname: true + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + brief: true + facility_override: "ftp" + log_prefix: "field" + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + port: 1231 + routing_instance: "inst11" + source_address: "11.1.1.11" + users: + - name: "user1" + allow_duplicates: true + - name: "user2" + allow_duplicates: true + any: + level: "any" + user: + level: info + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file104" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host222", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "before": { +# "allow_duplicates": true, +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "console": { +# "any": { +# "level": "info" +# }, +# "authorization": { +# "level": "any" +# }, +# "change_log": { +# "level": "critical" +# }, +# "ftp": { +# "level": "none" +# } +# }, +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file101" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# }, +# { +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "explicit_priority": true, +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "file103" +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host111", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "log_rotate_frequency": 45, +# "routing_instance": "inst11", +# "source_address": "33.33.33.33", +# "time_format": { +# "millisecond": true, +# "year": true +# }, +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:syslog delete=\"delete\"/><nc:syslog><nc:file><nc:name>file104</nc:name>" +# "<nc:allow-duplicates/></nc:file><nc:file><nc:name>file102</nc:name><nc:allow-duplicates/>" +# "<nc:contents><nc:name>any</nc:name><nc:any/></nc:contents><nc:structured-data/></nc:file>" +# "<nc:host><nc:name>host222</nc:name><nc:allow-duplicates/><nc:contents><nc:name>any</nc:name>" +# "<nc:any/></nc:contents><nc:exclude-hostname/><nc:facility-override>ftp</nc:facility-override>" +# "<nc:log-prefix>field</nc:log-prefix><nc:match>^set*</nc:match>" +# "<nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings><nc:port>1231</nc:port>" +# "<nc:routing-instance>inst11</nc:routing-instance><nc:source-address>11.1.1.11</nc:source-address>" +# "<nc:structured-data><nc:brief/></nc:structured-data></nc:host><nc:user><nc:name>user1</nc:name>" +# "<nc:allow-duplicates/></nc:user><nc:user><nc:name>user2</nc:name><nc:allow-duplicates/><nc:contents>" +# "<nc:name>any</nc:name><nc:any/></nc:contents>" +# "<nc:contents><nc:name>user</nc:name><nc:info/></nc:contents></nc:user></nc:syslog></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system syslog +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host222 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# file file104 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show system syslog +# archive size 65578 files 10 no-world-readable no-binary-data; +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host111 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# allow-duplicates; +# file file101 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +# file file103 { +# match "^set*"; +# archive size 65578 files 10 no-world-readable no-binary-data; +# explicit-priority; +# match-strings [ "^delete" "^prompt" ]; +# } +# console { +# any info; +# authorization any; +# ftp none; +# change-log critical; +# } +# time-format year millisecond; +# source-address 33.33.33.33; +# routing-instance inst11; +# log-rotate-frequency 45; +- name: Override running logging global configuration with provided configuration + junipernetworks.junos.junos_logging_global: + config: + files: + - name: "file104" + allow_duplicates: true + - name: "file102" + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + hosts: + - name: host222 + exclude_hostname: true + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + brief: true + facility_override: "ftp" + log_prefix: "field" + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + port: 1231 + routing_instance: "inst11" + source_address: "11.1.1.11" + users: + - name: "user1" + allow_duplicates: true + - name: "user2" + allow_duplicates: true + any: + level: "any" + user: + level: info + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file104" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host222", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "before": { +# "allow_duplicates": true, +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "console": { +# "any": { +# "level": "info" +# }, +# "authorization": { +# "level": "any" +# }, +# "change_log": { +# "level": "critical" +# }, +# "ftp": { +# "level": "none" +# } +# }, +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file101" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# }, +# { +# "archive": { +# "file_size": 65578, +# "files": 10, +# "no_binary_data": true, +# "no_world_readable": true +# }, +# "explicit_priority": true, +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "file103" +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host111", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "log_rotate_frequency": 45, +# "routing_instance": "inst11", +# "source_address": "33.33.33.33", +# "time_format": { +# "millisecond": true, +# "year": true +# }, +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:syslog delete=\"delete\"/><nc:syslog><nc:file><nc:name>file104</nc:name>" +# "<nc:allow-duplicates/></nc:file><nc:file><nc:name>file102</nc:name><nc:allow-duplicates/>" +# "<nc:contents><nc:name>any</nc:name><nc:any/></nc:contents><nc:structured-data/></nc:file>" +# "<nc:host><nc:name>host222</nc:name><nc:allow-duplicates/><nc:contents><nc:name>any</nc:name>" +# "<nc:any/></nc:contents><nc:exclude-hostname/><nc:facility-override>ftp</nc:facility-override>" +# "<nc:log-prefix>field</nc:log-prefix><nc:match>^set*</nc:match>" +# "<nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings><nc:port>1231</nc:port>" +# "<nc:routing-instance>inst11</nc:routing-instance><nc:source-address>11.1.1.11</nc:source-address>" +# "<nc:structured-data><nc:brief/></nc:structured-data></nc:host><nc:user><nc:name>user1</nc:name>" +# "<nc:allow-duplicates/></nc:user><nc:user><nc:name>user2</nc:name><nc:allow-duplicates/><nc:contents>" +# "<nc:name>any</nc:name><nc:any/></nc:contents>" +# "<nc:contents><nc:name>user</nc:name><nc:info/></nc:contents></nc:user></nc:syslog></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system syslog +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host222 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# file file104 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show system syslog +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host222 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# file file104 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +- name: Delete running logging global configuration + junipernetworks.junos.junos_logging_global: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file104" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host222", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:syslog delete=\"delete\"/></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system syslog +# +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show system syslog +# user user1 { +# allow-duplicates; +# } +# user user2 { +# any any; +# user info; +# allow-duplicates; +# } +# host host222 { +# any any; +# match "^set*"; +# allow-duplicates; +# port 1231; +# facility-override ftp; +# log-prefix field; +# source-address 11.1.1.11; +# routing-instance inst11; +# exclude-hostname; +# match-strings [ "^delete" "^prompt" ]; +# structured-data { +# brief; +# } +# } +# file file104 { +# allow-duplicates; +# } +# file file102 { +# any any; +# allow-duplicates; +# structured-data; +# } +- name: Gather running logging global configuration + junipernetworks.junos.junos_logging_global: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "files": [ +# { +# "allow_duplicates": true, +# "name": "file104" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "file102", +# "structured_data": { +# "set": true +# } +# } +# ], +# "hosts": [ +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "exclude_hostname": true, +# "facility_override": "ftp", +# "log_prefix": "field", +# "match": "^set*", +# "match_strings": [ +# "^delete", +# "^prompt" +# ], +# "name": "host222", +# "port": 1231, +# "routing_instance": "inst11", +# "source_address": "11.1.1.11", +# "structured_data": { +# "brief": true +# } +# } +# ], +# "users": [ +# { +# "allow_duplicates": true, +# "name": "user1" +# }, +# { +# "allow_duplicates": true, +# "any": { +# "level": "any" +# }, +# "name": "user2", +# "user": { +# "level": "info" +# } +# } +# ] +# }, +# "changed": false, +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_logging_global: + config: + allow_duplicates: true + archive: + set: true + no_binary_data: true + files: 10 + file_size: 65578 + no_world_readable: true + console: + any: + level: "info" + authorization: + level: "any" + change_log: + level: "critical" + ftp: + level: "none" + files: + - name: "file101" + allow_duplicates: true + - name: "file102" + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + - name: "file103" + archive: + set: true + no_binary_data: true + files: 10 + file_size: 65578 + no_world_readable: true + explicit_priority: true + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + hosts: + - name: host111 + exclude_hostname: true + allow_duplicates: true + any: + level: "any" + structured_data: + set: true + brief: true + facility_override: "ftp" + log_prefix: "field" + match: "^set*" + match_strings: + - "^delete" + - "^prompt" + port: 1231 + routing_instance: "inst11" + source_address: "11.1.1.11" + routing_instance: "inst11" + log_rotate_frequency: 45 + source_address: "33.33.33.33" + time_format: + millisecond: true + year: true + users: + - name: "user1" + allow_duplicates: true + - name: "user2" + allow_duplicates: true + any: + level: "any" + user: + level: info + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:syslog><nc:allow-duplicates/><nc:archive><nc:files>10</nc:files>" +# "<nc:no-binary-data/><nc:size>65578</nc:size><nc:no-world-readable/></nc:archive>" +# "<nc:console><nc:name>change-log</nc:name><nc:critical/></nc:console><nc:console>" +# "<nc:name>any</nc:name><nc:info/></nc:console><nc:console><nc:name>authorization</nc:name>" +# "<nc:any/></nc:console><nc:console><nc:name>ftp</nc:name><nc:none/></nc:console><nc:file>" +# "<nc:name>file101</nc:name><nc:allow-duplicates/></nc:file><nc:file><nc:name>file102</nc:name>" +# "<nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/></nc:contents><nc:structured-data/>" +# "</nc:file><nc:file><nc:name>file103</nc:name><nc:archive><nc:files>10</nc:files><nc:no-binary-data/>" +# "<nc:size>65578</nc:size><nc:no-world-readable/></nc:archive><nc:explicit-priority/>" +# "<nc:match>^set*</nc:match><nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings></nc:file><nc:host><nc:name>host111</nc:name>" +# "<nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/></nc:contents>" +# "<nc:exclude-hostname/><nc:facility-override>ftp</nc:facility-override>" +# "<nc:log-prefix>field</nc:log-prefix><nc:match>^set*</nc:match><nc:match-strings>^delete</nc:match-strings>" +# "<nc:match-strings>^prompt</nc:match-strings><nc:port>1231</nc:port>" +# "<nc:routing-instance>inst11</nc:routing-instance><nc:source-address>11.1.1.11</nc:source-address>" +# "<nc:structured-data><nc:brief/></nc:structured-data></nc:host>" +# "<nc:log-rotate-frequency>45</nc:log-rotate-frequency><nc:routing-instance>inst11</nc:routing-instance>" +# "<nc:source-address>33.33.33.33</nc:source-address><nc:time-format><nc:millisecond/>" +# "<nc:year/></nc:time-format><nc:user><nc:name>user1</nc:name><nc:allow-duplicates/></nc:user>" +# "<nc:user><nc:name>user2</nc:name><nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/>" +# "</nc:contents><nc:contents><nc:name>user</nc:name><nc:info/></nc:contents></nc:user></nc:syslog></nc:system>" +# ] +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <system xmlns="http://yang.juniper.net/junos-es/conf/system"> +# <syslog> +# <user> +# <name>*</name> +# <contents> +# <name>any</name> +# <emergency/> +# </contents> +# </user> +# <file> +# <name>messages</name> +# <contents> +# <name>any</name> +# <any/> +# </contents> +# <contents> +# <name>authorization</name> +# <info/> +# </contents> +# </file> +# <file> +# <name>interactive-commands</name> +# <contents> +# <name>interactive-commands</name> +# <any/> +# </contents> +# </file> +# </syslog> +# </system> +# </configuration> +# </rpc-reply> +- name: Parse logging global running config + junipernetworks.junos.junos_routing_instances: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "files": [ +# { +# "any": { +# "level": "any" +# }, +# "authorization": { +# "level": "info" +# }, +# "name": "messages" +# }, +# { +# "interactive_commands": { +# "level": "any" +# }, +# "name": "interactive-commands" +# } +# ], +# "users": [ +# { +# "any": { +# "level": "emergency" +# }, +# "name": "*" +# } +# ] +# } +# +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:allow-duplicates/></nc:user><nc:user><nc:name>user2</nc:name> + <nc:allow-duplicates/><nc:contents><nc:name>any</nc:name><nc:any/> + </nc:contents><nc:contents><nc:name>user</nc:name><nc:info/></nc:contents> + </nc:user></nc:syslog></nc:system>"', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.logging_global.logging_global import ( + Logging_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.logging_global.logging_global import ( + Logging_global, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Logging_globalArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Logging_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_netconf.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_netconf.py new file mode 100644 index 000000000..f1b3c4b52 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_netconf.py @@ -0,0 +1,193 @@ +#!/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: junos_netconf +author: Peter Sprygada (@privateip) +short_description: Configures the Junos Netconf system service +description: +- This module provides an abstraction that enables and configures the netconf system + service running on Junos devices. 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. If the C(netconf_port) is not mentioned + in the task by default netconf will be enabled on port 830 only. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +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 + default: 830 + aliases: + - listens_on + type: int + state: + description: + - Specifies the state of the C(junos_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: +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(network_cli). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +- If C(netconf_port) value is not mentioned in task by default it will be enabled + on port 830 only. Although C(netconf_port) value can be from 1 through 65535, avoid + configuring access on a port that is normally assigned for another service. This + practice avoids potential resource conflicts. +""" + +EXAMPLES = """ +- name: enable netconf service on port 830 + junipernetworks.junos.junos_netconf: + listens_on: 830 + state: present + +- name: disable netconf service + junipernetworks.junos.junos_netconf: + state: absent +""" + +RETURN = """ +commands: + description: Returns the command sent to the remote device + returned: when changed is True + type: str + sample: 'set system services netconf ssh port 830' +""" +import re + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_connection, +) + + +USE_PERSISTENT_CONNECTION = True + + +def map_obj_to_commands(updates, module): + want, have = updates + commands = list() + + if want["state"] == "absent": + if have["state"] == "present": + commands.append("delete system services netconf") + else: + if have["state"] == "absent" or want["netconf_port"] != have.get( + "netconf_port", + ): + commands.append( + "set system services netconf ssh port %s" % want["netconf_port"], + ) + + return commands + + +def parse_port(config): + match = re.search(r"port (\d+)", config) + if match: + return int(match.group(1)) + + +def map_config_to_obj(module): + conn = get_connection(module) + out = conn.get(command="show configuration system services netconf") + if out is None: + module.fail_json(msg="unable to retrieve current config") + config = str(out).strip() + + obj = {"state": "absent"} + if "ssh" in config: + obj.update({"state": "present", "netconf_port": parse_port(config)}) + 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"], + "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 load_config(module, config, commit=False): + conn = get_connection(module) + try: + resp = conn.edit_config(to_list(config) + ["top"], commit) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + diff = resp.get("diff", "") + return to_text(diff, errors="surrogate_then_replace").strip() + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + netconf_port=dict(type="int", default=830, aliases=["listens_on"]), + state=dict(default="present", choices=["present", "absent"]), + ) + + 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), module) + result["commands"] = commands + + if commands: + commit = not module.check_mode + diff = load_config(module, commands, commit=commit) + if diff: + if module._diff: + result["diff"] = {"prepared": diff} + result["changed"] = True + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_ntp_global.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ntp_global.py new file mode 100644 index 000000000..0bc72b4d1 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ntp_global.py @@ -0,0 +1,1019 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_logging_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: junos_ntp_global +version_added: 2.6.0 +short_description: Manage NTP configuration on Junos devices. +description: This module manages NTP configuration on devices running Junos. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show system syslog). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + config: + description: A dictionary of NTP configuration. + type: dict + suboptions: + authentication_keys: + description: NTP authentication key. + type: list + elements: dict + suboptions: + id: + description: Authentication key number. + type: int + algorithm: + description: Authentication key type. + type: str + choices: ['md5', 'sha1', 'sha256'] + key: + description: Authentication key value. + type: str + boot_server: + description: Server to query during boot sequence. + type: str + broadcasts: + description: Broadcast parameters. + type: list + elements: dict + suboptions: + address: + description: Broadcast or multicast address to use. + type: str + key: + description: Authentication key. + type: str + routing_instance_name: + description: Routing intance name in which interface has address in broadcast subnet. + type: str + ttl: + description: TTL value to transmit. + type: int + version: + description: NTP version to use. + type: int + broadcast_client: + description: Listen to broadcast NTP. + type: bool + interval_range: + description: Set the minpoll and maxpoll interval range. + type: int + multicast_client: + description: Listen to multicast NTP address. + type: str + peers: + description: NTP Peers. + type: list + elements: dict + suboptions: &peers + peer: + description: Hostname/IP address of the NTP Peer. + type: str + key_id: + description: Key-id to be used while communicating. + type: int + prefer: + description: Prefer this peer. + type: bool + version: + description: NTP version to use. + type: int + servers: + description: NTP Servers. + type: list + elements: dict + suboptions: + server: + description: IP address or hostname of the server. + type: str + key_id: + description: Key-id to be used while communicating. + type: int + prefer: + description: Prefer this peer_serv. + type: bool + version: + description: NTP version to use. + type: int + routing_instance: + description: Routing instance through which server is reachable. + type: str + source_addresses: + description: Source-Address parameters. + type: list + elements: dict + suboptions: + source_address: + description: Use specified address as source address. + type: str + routing_instance: + description: Routing intance name in which source address is defined. + type: str + threshold: + description: Set the maximum threshold(sec) allowed for NTP adjustment. + type: dict + suboptions: + value: + description: The maximum value(sec) allowed for NTP adjustment. + type: int + action: + description: Select actions for NTP abnormal adjustment. + type: str + choices: ['accept', 'reject'] + trusted_keys: + description: List of trusted authentication keys. + type: list + elements: dict + suboptions: + key_id: + description: Trusted-Key number. + type: int + state: + description: + - The state the configuration should be left in. + - The states I(replaced) and I(overridden) have identical + behaviour for this module. + - Refer to examples for more details. + type: str + choices: + - merged + - replaced + - deleted + - overridden + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# +# [edit] +# vagrant@vsrx# show routing-instances +# rt1 { +# description rt1; +# } +# rt2 { +- name: Merge provided NTP configuration into running configuration. + junipernetworks.junos.junos_ntp_global: + config: + boot_server: '78.46.194.186' + broadcasts: + - address: '172.16.255.255' + key: '50' + ttl: 200 + version: 3 + routing_instance_name: 'rt1' + - address: '192.16.255.255' + key: '50' + ttl: 200 + version: 3 + routing_instance_name: 'rt2' + broadcast_client: true + interval_range: 2 + multicast_client: "224.0.0.1" + peers: + - peer: "78.44.194.186" + - peer: "172.44.194.186" + key_id: 10000 + prefer: true + version: 3 + servers: + - server: "48.46.194.186" + key_id: 34 + prefer: true + version: 2 + routing_instance: 'rt1' + - server: "48.45.194.186" + key_id: 34 + prefer: true + version: 2 + source_addresses: + - source_address: "172.45.194.186" + routing_instance: 'rt1' + - source_address: "171.45.194.186" + routing_instance: 'rt2' + threshold: + value: 300 + action: "accept" + trusted_keys: + - key_id: 3000 + - key_id: 2000 + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "boot_server": "78.46.194.186", +# "broadcast_client": true, +# "broadcasts": [ +# { +# "address": "172.16.255.255", +# "key": "50", +# "routing_instance_name": "rt1", +# "ttl": 200, +# "version": 3 +# }, +# { +# "address": "192.16.255.255", +# "key": "50", +# "routing_instance_name": "rt2", +# "ttl": 200, +# "version": 3 +# } +# ], +# "interval_range": 2, +# "multicast_client": "224.0.0.1", +# "peers": [ +# { +# "peer": "78.44.194.186" +# }, +# { +# "key_id": 10000, +# "peer": "172.44.194.186", +# "prefer": true, +# "version": 3 +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ], +# "source_addresses": [ +# { +# "routing_instance": "rt1", +# "source_address": "172.45.194.186" +# }, +# { +# "routing_instance": "rt2", +# "source_address": "171.45.194.186" +# } +# ], +# "threshold": { +# "action": "accept", +# "value": 300 +# }, +# "trusted_keys": [ +# {"key_id": 2000}, +# {"key_id": 3000} +# ] +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:ntp><nc:boot-server>78.46.194.186</nc:boot-server><nc:broadcast>" +# "<nc:name>172.16.255.255</nc:name><nc:key>50</nc:key><nc:routing-instance-name>rt1</nc:routing-instance-name>" +# "<nc:ttl>200</nc:ttl><nc:version>3</nc:version></nc:broadcast><nc:broadcast><nc:name>192.16.255.255</nc:name>" +# "<nc:key>50</nc:key><nc:routing-instance-name>rt2</nc:routing-instance-name><nc:ttl>200</nc:ttl>" +# "<nc:version>3</nc:version></nc:broadcast><nc:broadcast-client/><nc:interval-range>2</nc:interval-range>" +# "<nc:multicast-client>224.0.0.1</nc:multicast-client><nc:peer><nc:name>78.44.194.186</nc:name></nc:peer>" +# "<nc:peer><nc:name>172.44.194.186</nc:name><nc:key>10000</nc:key><nc:prefer/><nc:version>3</nc:version>" +# "</nc:peer><nc:server><nc:name>48.46.194.186</nc:name><nc:key>34</nc:key><nc:routing-instance>rt1</nc:routing-instance>" +# "<nc:prefer/><nc:version>2</nc:version></nc:server><nc:server><nc:name>48.45.194.186</nc:name><nc:key>34</nc:key>" +# "<nc:prefer/><nc:version>2</nc:version></nc:server><nc:source-address><nc:name>172.45.194.186</nc:name>" +# "<nc:routing-instance>rt1</nc:routing-instance></nc:source-address><nc:source-address>" +# "<nc:name>171.45.194.186</nc:name><nc:routing-instance>rt2</nc:routing-instance></nc:source-address>" +# "<nc:threshold><nc:value>300</nc:value><nc:action>accept</nc:action></nc:threshold>" +# "<nc:trusted-key>3000</nc:trusted-key><nc:trusted-key>2000</nc:trusted-key></nc:ntp></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# boot-server 78.46.194.186; +# interval-range 2; +# peer 78.44.194.186; +# peer 172.44.194.186 key 10000 version 3 prefer; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# broadcast 172.16.255.255 routing-instance-name rt1 key 50 version 3 ttl 200; +# broadcast 192.16.255.255 routing-instance-name rt2 key 50 version 3 ttl 200; +# broadcast-client; +# multicast-client 224.0.0.1; +# trusted-key [ 3000 2000 ]; +# threshold 300 action accept; +# source-address 172.45.194.186 routing-instance rt1; +# source-address 171.45.194.186 routing-instance rt2; +# +# +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# boot-server 78.46.194.186; +# interval-range 2; +# peer 78.44.194.186; +# peer 172.44.194.186 key 10000 version 3 prefer; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# broadcast 172.16.255.255 routing-instance-name rt1 key 50 version 3 ttl 200; +# broadcast 192.16.255.255 routing-instance-name rt2 key 50 version 3 ttl 200; +# broadcast-client; +# multicast-client 224.0.0.1; +# trusted-key [ 3000 2000 ]; +# threshold 300 action accept; +# source-address 172.45.194.186 routing-instance rt1; +# source-address 171.45.194.186 routing-instance rt2; + +- name: Replaced running ntp global configuration with provided configuration + junipernetworks.junos.junos_ntp_global: + config: + authentication_keys: + - id: 2 + algorithm: 'md5' + key: 'asdfghd' + - id: 5 + algorithm: 'sha1' + key: 'aasdad' + servers: + - server: "48.46.194.186" + key_id: 34 + prefer: true + version: 2 + routing_instance: 'rt1' + - server: "48.45.194.186" + key_id: 34 + prefer: true + version: 2 + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "id": 2, +# "key": "$9$03aAB1hreW7NbO1rvMLVbgoJ" +# }, +# { +# "algorithm": "sha1", +# "id": 5, +# "key": "$9$DXiHmf5F/A0ZUjq.P3n" +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ] +# }, +# "before": { +# "boot_server": "78.46.194.186", +# "broadcast_client": true, +# "broadcasts": [ +# { +# "address": "172.16.255.255", +# "key": "50", +# "routing_instance_name": "rt1", +# "ttl": 200, +# "version": 3 +# }, +# { +# "address": "192.16.255.255", +# "key": "50", +# "routing_instance_name": "rt2", +# "ttl": 200, +# "version": 3 +# } +# ], +# "interval_range": 2, +# "multicast_client": "224.0.0.1", +# "peers": [ +# { +# "peer": "78.44.194.186" +# }, +# { +# "key_id": 10000, +# "peer": "172.44.194.186", +# "prefer": true, +# "version": 3 +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ], +# "source_addresses": [ +# { +# "routing_instance": "rt1", +# "source_address": "172.45.194.186" +# }, +# { +# "routing_instance": "rt2", +# "source_address": "171.45.194.186" +# } +# ], +# "threshold": { +# "action": "accept", +# "value": 300 +# }, +# "trusted_keys": [ +# {"key_id": 2000}, +# {"key_id": 3000} +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:ntp delete=\"delete\"/><nc:ntp><nc:authentication-key><nc:name>2</nc:name><nc:type>md5</nc:type> +# "<nc:value>asdfghd</nc:value></nc:authentication-key><nc:authentication-key><nc:name>5</nc:name> +# "<nc:type>sha1</nc:type><nc:value>aasdad</nc:value></nc:authentication-key><nc:server> +# "<nc:name>48.46.194.186</nc:name><nc:key>34</nc:key><nc:routing-instance>rt1</nc:routing-instance> +# "<nc:prefer/><nc:version>2</nc:version></nc:server><nc:server><nc:name>48.45.194.186</nc:name> +# "<nc:key>34</nc:key><nc:prefer/><nc:version>2</nc:version></nc:server></nc:ntp></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# authentication-key 2 type md5 value "$9$03aAB1hreW7NbO1rvMLVbgoJ"; ## SECRET-DATA +# authentication-key 5 type sha1 value "$9$DXiHmf5F/A0ZUjq.P3n"; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA + +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# boot-server 78.46.194.186; +# interval-range 2; +# peer 78.44.194.186; +# peer 172.44.194.186 key 10000 version 3 prefer; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# broadcast 172.16.255.255 routing-instance-name rt1 key 50 version 3 ttl 200; +# broadcast 192.16.255.255 routing-instance-name rt2 key 50 version 3 ttl 200; +# broadcast-client; +# multicast-client 224.0.0.1; +# trusted-key [ 3000 2000 ]; +# threshold 300 action accept; +# source-address 172.45.194.186 routing-instance rt1; +# source-address 171.45.194.186 routing-instance rt2; + +- name: Override running ntp global configuration with provided configuration + junipernetworks.junos.junos_ntp_global: + config: + authentication_keys: + - id: 2 + algorithm: 'md5' + key: 'asdfghd' + - id: 5 + algorithm: 'sha1' + key: 'aasdad' + servers: + - server: "48.46.194.186" + key_id: 34 + prefer: true + version: 2 + routing_instance: 'rt1' + - server: "48.45.194.186" + key_id: 34 + prefer: true + version: 2 + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "id": 2, +# "key": "$9$03aAB1hreW7NbO1rvMLVbgoJ" +# }, +# { +# "algorithm": "sha1", +# "id": 5, +# "key": "$9$DXiHmf5F/A0ZUjq.P3n" +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ] +# }, +# "before": { +# "boot_server": "78.46.194.186", +# "broadcast_client": true, +# "broadcasts": [ +# { +# "address": "172.16.255.255", +# "key": "50", +# "routing_instance_name": "rt1", +# "ttl": 200, +# "version": 3 +# }, +# { +# "address": "192.16.255.255", +# "key": "50", +# "routing_instance_name": "rt2", +# "ttl": 200, +# "version": 3 +# } +# ], +# "interval_range": 2, +# "multicast_client": "224.0.0.1", +# "peers": [ +# { +# "peer": "78.44.194.186" +# }, +# { +# "key_id": 10000, +# "peer": "172.44.194.186", +# "prefer": true, +# "version": 3 +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ], +# "source_addresses": [ +# { +# "routing_instance": "rt1", +# "source_address": "172.45.194.186" +# }, +# { +# "routing_instance": "rt2", +# "source_address": "171.45.194.186" +# } +# ], +# "threshold": { +# "action": "accept", +# "value": 300 +# }, +# "trusted_keys": [ +# {"key_id": 2000}, +# {"key_id": 3000} +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:ntp delete=\"delete\"/><nc:ntp><nc:authentication-key><nc:name>2</nc:name><nc:type>md5</nc:type> +# "<nc:value>asdfghd</nc:value></nc:authentication-key><nc:authentication-key><nc:name>5</nc:name> +# "<nc:type>sha1</nc:type><nc:value>aasdad</nc:value></nc:authentication-key><nc:server> +# "<nc:name>48.46.194.186</nc:name><nc:key>34</nc:key><nc:routing-instance>rt1</nc:routing-instance> +# "<nc:prefer/><nc:version>2</nc:version></nc:server><nc:server><nc:name>48.45.194.186</nc:name> +# "<nc:key>34</nc:key><nc:prefer/><nc:version>2</nc:version></nc:server></nc:ntp></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# authentication-key 2 type md5 value "$9$03aAB1hreW7NbO1rvMLVbgoJ"; ## SECRET-DATA +# authentication-key 5 type sha1 value "$9$DXiHmf5F/A0ZUjq.P3n"; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# authentication-key 2 type md5 value "$9$03aAB1hreW7NbO1rvMLVbgoJ"; ## SECRET-DATA +# authentication-key 5 type sha1 value "$9$DXiHmf5F/A0ZUjq.P3n"; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# +- name: Delete running NTP global configuration + junipernetworks.junos.junos_ntp_global: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "id": 2, +# "key": "$9$03aAB1hreW7NbO1rvMLVbgoJ" +# }, +# { +# "algorithm": "sha1", +# "id": 5, +# "key": "$9$DXiHmf5F/A0ZUjq.P3n" +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:ntp delete=\"delete\"/></nc:system>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# boot-server 78.46.194.186; +# interval-range 2; +# peer 78.44.194.186; +# peer 172.44.194.186 key 10000 version 3 prefer; ## SECRET-DATA +# server 48.46.194.186 key 34 version 2 prefer routing-instance rt1; ## SECRET-DATA +# server 48.45.194.186 key 34 version 2 prefer; ## SECRET-DATA +# broadcast 172.16.255.255 routing-instance-name rt1 key 50 version 3 ttl 200; +# broadcast 192.16.255.255 routing-instance-name rt2 key 50 version 3 ttl 200; +# broadcast-client; +# multicast-client 224.0.0.1; +# trusted-key [ 3000 2000 ]; +# threshold 300 action accept; +# source-address 172.45.194.186 routing-instance rt1; +# source-address 171.45.194.186 routing-instance rt2; +- name: Gather running NTP global configuration + junipernetworks.junos.junos_ntp_global: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "boot_server": "78.46.194.186", +# "broadcast_client": true, +# "broadcasts": [ +# { +# "address": "172.16.255.255", +# "key": "50", +# "routing_instance_name": "rt1", +# "ttl": 200, +# "version": 3 +# }, +# { +# "address": "192.16.255.255", +# "key": "50", +# "routing_instance_name": "rt2", +# "ttl": 200, +# "version": 3 +# } +# ], +# "interval_range": 2, +# "multicast_client": "224.0.0.1", +# "peers": [ +# { +# "peer": "78.44.194.186" +# }, +# { +# "key_id": 10000, +# "peer": "172.44.194.186", +# "prefer": true, +# "version": 3 +# } +# ], +# "servers": [ +# { +# "key_id": 34, +# "prefer": true, +# "routing_instance": "rt1", +# "server": "48.46.194.186", +# "version": 2 +# }, +# { +# "key_id": 34, +# "prefer": true, +# "server": "48.45.194.186", +# "version": 2 +# } +# ], +# "source_addresses": [ +# { +# "routing_instance": "rt1", +# "source_address": "172.45.194.186" +# }, +# { +# "routing_instance": "rt2", +# "source_address": "171.45.194.186" +# } +# ], +# "threshold": { +# "action": "accept", +# "value": 300 +# }, +# "trusted_keys": [ +# {"key_id": 2000}, +# {"key_id": 3000} +# ] +# }, +# "changed": false, +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_ntp_global: + config: + boot_server: '78.46.194.186' + broadcasts: + - address: '172.16.255.255' + key: '50' + ttl: 200 + version: 3 + routing_instance_name: 'rt1' + - address: '192.16.255.255' + key: '50' + ttl: 200 + version: 3 + routing_instance_name: 'rt2' + broadcast_client: true + interval_range: 2 + multicast_client: "224.0.0.1" + peers: + - peer: "78.44.194.186" + - peer: "172.44.194.186" + key_id: 10000 + prefer: true + version: 3 + servers: + - server: "48.46.194.186" + key_id: 34 + prefer: true + version: 2 + routing_instance: 'rt1' + - server: "48.45.194.186" + key_id: 34 + prefer: true + version: 2 + source_addresses: + - source_address: "172.45.194.186" + routing_instance: 'rt1' + - source_address: "171.45.194.186" + routing_instance: 'rt2' + threshold: + value: 300 + action: "accept" + trusted_keys: + - 3000 + - 2000 + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:system xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:ntp><nc:boot-server>78.46.194.186</nc:boot-server><nc:broadcast><nc:name>172.16.255.255</nc:name>" +# "<nc:key>50</nc:key><nc:routing-instance-name>rt1</nc:routing-instance-name><nc:ttl>200</nc:ttl>" +# "<nc:version>3</nc:version></nc:broadcast><nc:broadcast><nc:name>192.16.255.255</nc:name>" +# "<nc:key>50</nc:key><nc:routing-instance-name>rt2</nc:routing-instance-name>" +# "<nc:ttl>200</nc:ttl><nc:version>3</nc:version></nc:broadcast><nc:broadcast-client/>" +# "<nc:interval-range>2</nc:interval-range><nc:multicast-client>224.0.0.1</nc:multicast-client><nc:peer>" +# "<nc:name>78.44.194.186</nc:name></nc:peer><nc:peer><nc:name>172.44.194.186</nc:name>" +# "<nc:key>10000</nc:key><nc:prefer/><nc:version>3</nc:version></nc:peer><nc:server>" +# "<nc:name>48.46.194.186</nc:name><nc:key>34</nc:key><nc:routing-instance>rt1</nc:routing-instance>" +# "<nc:prefer/><nc:version>2</nc:version></nc:server><nc:server><nc:name>48.45.194.186</nc:name>" +# "<nc:key>34</nc:key><nc:prefer/><nc:version>2</nc:version></nc:server><nc:source-address>" +# "<nc:name>172.45.194.186</nc:name><nc:routing-instance>rt1</nc:routing-instance></nc:source-address>" +# "<nc:source-address><nc:name>171.45.194.186</nc:name><nc:routing-instance>rt2</nc:routing-instance>" +# "</nc:source-address><nc:threshold><nc:value>300</nc:value><nc:action>accept</nc:action></nc:threshold>" +# "<nc:trusted-key>3000</nc:trusted-key><nc:trusted-key>2000</nc:trusted-key></nc:ntp></nc:system>" +# ] +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <system xmlns="http://yang.juniper.net/junos-es/conf/system"> +# <ntp> +# <authentication-key> +# <name>2</name> +# <type>md5</type> +# <value>$9$GxDjqfT3CA0UjfzF6u0RhS</value> +# </authentication-key> +# <authentication-key> +# <name>5</name> +# <type>sha1</type> +# <value>$9$ZsUDk.mT3/toJGiHqQz</value> +# </authentication-key> +# </ntp> +# </system> +# </configuration> +# </rpc-reply> +# +- name: Parse NTP global running config + junipernetworks.junos.junos_ntp_global: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "id": 2, +# "key": "$9$GxDjqfT3CA0UjfzF6u0RhS" +# }, +# { +# "algorithm": "sha1", +# "id": 5, +# "key": "$9$ZsUDk.mT3/toJGiHqQz" +# } +# ] +# } +# +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ["<nc:name>78.44.194.186</nc:name></nc:peer><nc:peer><nc:name>172.44.194.186</nc:name>", + 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ntp_global.ntp_global import ( + Ntp_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.ntp_global.ntp_global import ( + Ntp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Ntp_globalArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Ntp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospf_interfaces.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospf_interfaces.py new file mode 100644 index 000000000..fdfaf2640 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospf_interfaces.py @@ -0,0 +1,611 @@ +#!/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 junos_ospf_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_ospf_interfaces +version_added: 1.3.0 +short_description: OSPF Interfaces Resource Module. +description: + - This module manages OSPF(v2/v3) configuration of interfaces on devices running Juniper JUNOS. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +options: + config: + description: A list of OSPF configuration for interfaces. + type: list + elements: dict + suboptions: + router_id: + description: + - The OSPF router id. + type: str + name: + description: + - Name/Identifier 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: dict + suboptions: + area: + description: Specify the area-id + type: dict + suboptions: + area_id: + description: Specify area id. + type: str + authentication: + description: Specify authentication type + type: dict + suboptions: + simple_password: + description: + - Specify password for authentication. + type: str + md5: + description: + - Specify md5 based authentication. + type: dict + suboptions: + key_id: + description: Specify md5 key-id + type: str + key_value: + description: Specify key value + type: str + start_time: + description: Specify start time for key transmission + type: str + interface_type: + description: Specify type of interface + type: str + choices: ["nbma", "p2mp", "p2p"] + bandwidth_based_metrics: + description: Specify list of bandwidth based metrics + type: list + elements: dict + suboptions: + bandwidth: + description: + - BW to apply metric to. + type: str + choices: [1g, 10g] + metric: + description: Specify metric + type: int + priority: + description: + - Priority for the interface. + type: int + passive: + description: + - Do not run OSPF, but advertise it. + type: bool + metric: + description: + - Metric applied to the interface. + type: int + te_metric: + description: + - Traffic engineering metric applied to the interface. + type: int + mtu: + description: + - Maximum OSPF packet size + type: int + ipsec_sa: + description: + - IPSec security association name + type: str + secondary: + description: + - Treat interface as secondary + type: bool + flood_reduction: + description: + - Enable flood reduction. + type: bool + demand_circuit: + description: + - Interface functions as a demand circuit. + type: bool + no_advertise_adjacency_segment: + description: + - Do not advertise an adjacency segment for this interface. + type: bool + no_eligible_backup: + description: + - Not eligible to backup traffic from protected interfaces. + type: bool + no_eligible_remote_backup: + description: + - Not eligible for Remote-LFA backup traffic from protected interfaces. + type: bool + no_interface_state_traps: + description: + - Do not send interface state change traps. + type: bool + no_neighbor_down_notification: + description: + - Don't inform other protocols about neighbor down events. + type: bool + node_link_protection: + description: + - Protect interface from both link and node faults. + type: str + dead_interval: + description: + - Dead interval (seconds). + type: int + hello_interval: + description: + - Hello interval (seconds). + type: int + poll_interval: + description: + - Poll interval (seconds). + type: int + retransmit_interval: + description: + - Retransmit interval (seconds). + type: int + transit_delay: + description: + - Transit delay (seconds). + 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 Junos device + by executing the command B(show protocols 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 + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show protocols ospf + +- name: Merge Junos OSPF interfaces config + junipernetworks.junos.junos_ospf_interfaces: + config: + - name: 'ge-0/0/2.0' + address_family: + - afi: 'ipv4' + processes: + area: + area_id: '0.0.0.2' + priority: 3 + metric: 5 + state: merged + +# After state +# ----------- +# +# admin# show protocols ospf +# area 0.0.0.2 { +# interface ge-0/0/2.0 { +# metric 5; +# priority 3; +# } +# } + +# Using replaced +# +# Before state +# ------------ +# +# admin# show protocols ospf +# area 0.0.0.2 { +# interface ge-0/0/2.0 { +# metric 5; +# priority 3; +# } +# } +- name: Replace Junos OSPF interfaces config + junipernetworks.junos.junos_ospf_interfaces: + config: + - name: 'ge-0/0/2.0' + address_family: + - afi: 'ipv4' + processes: + area: + area_id: '0.0.0.1' + priority: 6 + metric: 6 + state: replaced + +# After state +# ----------- +# +# admin# show protocols ospf +# area 0.0.0.1 { +# interface ge-0/0/2.0 { +# metric 6; +# priority 6; +# } +# } + +# Using overridden +# +# Before state +# ------------ +# +# admin# show protocols ospf +# area 0.0.0.3 { +# interface ge-0/0/3.0 { +# metric 5; +# priority 3; +# } +# } +# area 0.0.0.2 { +# interface ge-0/0/2.0 { +# metric 5; +# priority 3; +# } +# } + +- name: Override Junos OSPF interfaces config + junipernetworks.junos.junos_ospf_interfaces: + config: + - name: 'ge-0/0/1.0' + address_family: + - afi: 'ipv4' + processes: + area: + area_id: '0.0.0.1' + priority: 3 + metric: 5 + state: overridden + +# After state +# ----------- +# +# admin# show protocols ospf +# area 0.0.0.1 { +# interface ge-0/0/1.0 { +# metric 5; +# priority 3; +# } +# } + +# +# Using deleted +# +# Before state +# ------------ +# +# admin# show protocols ospf +# area 0.0.0.1 { +# interface ge-0/0/1.0 { +# metric 5; +# priority 3; +# } +# } + +- name: Delete Junos OSPF interfaces config + junipernetworks.junos.junos_ospf_interfaces: + config: + - name: 'ge-0/0/1.0' + state: deleted + +# After state +# ----------- +# +# admin# show protocols ospf +# Using gathered +# +# Before state +# ------------ +# +# admin# show protocols ospf +# area 0.0.0.3 { +# interface ge-0/0/3.0 { +# metric 5; +# priority 3; +# } +# } +# area 0.0.0.2 { +# interface ge-0/0/2.0 { +# metric 5; +# priority 3; +# } +# } + +- name: Gather Junos OSPF interfaces config + junipernetworks.junos.junos_ospf_interfaces: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "processes": { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "metric": 5, +# "priority": 3 +# } +# } +# ], +# "name": "ge-0/0/3.0", +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "processes": { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "metric": 5, +# "priority": 3 +# } +# } +# ], +# "name": "ge-0/0/2.0", +# } +# ] +# +# Using rendered +# +# +- name: Render the commands for provided configuration + junipernetworks.junos.junos_ospf_interfaces: + config: + - name: 'ge-0/0/2.0' + address_family: + - afi: 'ipv4' + processes: + area: + area_id: '0.0.0.2' + priority: 3 + metric: 5 + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": " +# <nc:protocols +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:ospf> +# <nc:area> +# <nc:name>0.0.0.2</nc:name> +# <nc:interface> +# <nc:name>ge-0/0/2.0</nc:name> +# <nc:priority>3</nc:priority> +# <nc:metric>5</nc:metric> +# </nc:interface> +# </nc:area> +# </nc:ospf> +# </nc:protocols>" +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <protocols> +# <ospf> +# <area> +# <name>0.0.0.2</name> +# <stub> +# <default-metric>200</default-metric> +# </stub> +# <interface> +# <name>ge-0/0/2.0</name> +# <metric>5</metric> +# <priority>3</priority> +# </interface> +# </area> +# </ospf> +# </protocols> +# <routing-options> +# <router-id>10.200.16.75</router-id> +# </routing-options> +# </configuration> +# </rpc-reply> + + +- name: Parsed the device configuration to get output commands + junipernetworks.junos.junos_ospf_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "processes": { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "metric": 5, +# "priority": 3 +# } +# } +# ], +# "name": "ge-0/0/2.0", +# } +# ] +# +""" +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: ['<nc:protocols + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:ospf> + <nc:area> + <nc:name>0.0.0.3</nc:name> + <nc:interface> + <nc:name>ge-0/0/3.0</nc:name> + <nc:priority>3</nc:priority> + <nc:metric>5</nc:metric> + </nc:interface> + </nc:area> + <nc:area> + <nc:name>0.0.0.2</nc:name> + <nc:interface> + <nc:name>ge-0/0/2.0</nc:name> + <nc:priority>3</nc:priority> + <nc:metric>5</nc:metric> + </nc:interface> + </nc:area> + </nc:ospf> +</nc:protocols>", + " +<nc:routing-options + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:router-id>10.200.16.75</nc:router-id> + <nc:router-id>10.200.16.75</nc:router-id> +</nc:routing-options>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.ospf_interfaces.ospf_interfaces import ( + Ospf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Ospf_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Ospf_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv2.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv2.py new file mode 100644 index 000000000..9d951cf79 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv2.py @@ -0,0 +1,347 @@ +#!/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 junos_ospfv2 +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_ospfv2 +short_description: OSPFv2 resource module +description: +- This module manages global OSPFv2 configuration on devices running Juniper JUNOS. +version_added: 1.0.0 +author: +- Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +- xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). +- See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A list of OSPFv2 process configuration. + type: list + elements: dict + suboptions: + router_id: + description: + - The OSPFv2 router id. + type: str + areas: + description: + - A list of OSPFv2 areas' configuration. + type: list + elements: dict + suboptions: + area_id: + description: + - The Area ID as an integer or IP Address. + type: str + required: true + area_range: + description: + - Configure an address range for the area. + type: str + stub: + description: + - Settings for configuring the area as a stub. + type: dict + suboptions: + default_metric: + description: + - Metric for the default route in this area. + type: int + set: + description: + - Configure the area as a stub. + type: bool + interfaces: + description: + - List of interfaces in this area. + type: list + elements: dict + suboptions: + authentication: + description: Specify authentication type + type: dict + suboptions: + type: + description: + - Type of authentication to use. + type: dict + bandwidth_based_metrics: + description: Specify list of bandwidth based metrics + type: list + elements: dict + suboptions: + bandwidth: + description: + - BW to apply metric to. + type: str + choices: [1g, 10g] + metric: + description: Specify metric + type: int + name: + description: + - Name of the interface. + type: str + required: true + priority: + description: + - Priority for the interface. + type: int + metric: + description: + - Metric applied to the interface. + type: int + flood_reduction: + description: + - Enable flood reduction. + type: bool + passive: + description: Specify passive + type: bool + timers: + description: Specify timers + type: dict + suboptions: + dead_interval: + description: + - Dead interval (seconds). + type: int + hello_interval: + description: + - Hello interval (seconds). + type: int + poll_interval: + description: + - Poll interval (seconds). + type: int + retransmit_interval: + description: + - Retransmit interval (seconds). + type: int + transit_delay: + description: + - Transit delay (seconds). + type: int + external_preference: + description: + - Preference of external routes. + type: int + overload: + description: Specify time for overload mode reset + type: dict + suboptions: + timeout: + description: + - Time after which overload mode is reset (seconds). + type: int + preference: + description: + - Preference of internal routes. + type: int + prefix_export_limit: + description: + - Maximum number of external prefixes that can be exported. + type: int + reference_bandwidth: + description: + - Bandwidth for calculating metric defaults. + type: str + choices: [1g, 10g] + rfc1583compatibility: + description: + - Set RFC1583 compatibility + type: bool + spf_options: + description: + - Configure options for SPF. + type: dict + suboptions: + delay: + description: + - Time to wait before running an SPF (seconds). + type: int + holddown: + description: + - Time to hold down before running an SPF (seconds). + type: int + rapid_runs: + description: + - Number of maximum rapid SPF runs before holddown (seconds). + 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 Junos device + by executing the command B(show protocols 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 + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show protocols ospf + +- name: Merge Junos OSPFv2 config + junipernetworks.junos.junos_ospfv2: + config: + - reference_bandwidth: 10g + areas: + - area_id: 0.0.0.100 + area_range: 10.200.16.0/24 + stub: + default_metric: 100 + set: true + interfaces: + - name: so-0/0/0.0 + priority: 3 + metric: 5 + flood_reduction: false + passive: true + bandwidth_based_metrics: + - bandwidth: 1g + metric: 5 + - bandwidth: 10g + metric: 40 + timers: + dead_interval: 4 + hello_interval: 2 + poll_interval: 2 + retransmit_interval: 2 + rfc1583compatibility: false + state: merged + +# After state +# ----------- +# +# admin# show protocols ospf +# reference-bandwidth 10g; +# no-rfc-1583; +# area 0.0.0.100 { +# stub default-metric 100; +# area-range 10.200.16.0/24; +# interface so-0/0/0.0 { +# passive; +# bandwidth-based-metrics { +# bandwidth 1g metric 5; +# bandwidth 10g metric 40; +# } +# metric 5; +# priority 3; +# retransmit-interval 2; +# hello-interval 2; +# dead-interval 4; +# poll-interval 2; +# } +# } + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.ospfv2.ospfv2 import ( + Ospfv2, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Ospfv2Args.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Ospfv2(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv3.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv3.py new file mode 100644 index 000000000..0d49c625b --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ospfv3.py @@ -0,0 +1,693 @@ +#!/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 junos_ospfv3 +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_ospfv3 +short_description: OSPFv3 resource module +description: +- This module manages global OSPFv3 configuration on devices running Juniper JUNOS. +version_added: 1.2.0 +author: Rohit Thakur (@rohitthakur2590) +requirements: +- ncclient (>=v0.6.4) +- xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A list of OSPFv3 process configuration. + type: list + elements: dict + suboptions: + router_id: + description: + - The OSPFv3 router id. + type: str + areas: + description: + - A list of OSPFv3 areas' configuration. + type: list + elements: dict + suboptions: + area_id: + description: + - The Area ID as an integer or IP Address. + type: str + required: true + area_range: + description: + - Configure an address range for the area. + type: str + stub: + description: + - Settings for configuring the area as a stub. + type: dict + suboptions: + default_metric: + description: + - Metric for the default route in this area. + type: int + set: + description: + - Configure the area as a stub. + type: bool + interfaces: + description: + - List of interfaces in this area. + type: list + elements: dict + suboptions: + authentication: + description: Specify authentication type + type: dict + suboptions: + type: + description: + - Type of authentication to use. + type: dict + bandwidth_based_metrics: + description: Specify list of bandwidth based metrics + type: list + elements: dict + suboptions: + bandwidth: + description: + - BW to apply metric to. + type: str + choices: [1g, 10g] + metric: + description: Specify metric + type: int + name: + description: + - Name of the interface. + type: str + required: true + priority: + description: + - Priority for the interface. + type: int + metric: + description: + - Metric applied to the interface. + type: int + flood_reduction: + description: + - Enable flood reduction. + type: bool + passive: + description: Specify passive + type: bool + timers: + description: Specify timers + type: dict + suboptions: + dead_interval: + description: + - Dead interval (seconds). + type: int + hello_interval: + description: + - Hello interval (seconds). + type: int + poll_interval: + description: + - Poll interval (seconds). + type: int + retransmit_interval: + description: + - Retransmit interval (seconds). + type: int + transit_delay: + description: + - Transit delay (seconds). + type: int + external_preference: + description: + - Preference of external routes. + type: int + overload: + description: Specify time for overload mode reset + type: dict + suboptions: + timeout: + description: + - Time after which overload mode is reset (seconds). + type: int + preference: + description: + - Preference of internal routes. + type: int + prefix_export_limit: + description: + - Maximum number of external prefixes that can be exported. + type: int + reference_bandwidth: + description: + - Bandwidth for calculating metric defaults. + type: str + choices: [1g, 10g] + rfc1583compatibility: + description: + - Set RFC1583 compatibility + type: bool + spf_options: + description: + - Configure options for SPF. + type: dict + suboptions: + delay: + description: + - Time to wait before running an SPF (seconds). + type: int + holddown: + description: + - Time to hold down before running an SPF (seconds). + type: int + rapid_runs: + description: + - Number of maximum rapid SPF runs before holddown (seconds). + 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 Junos device + by executing the command B(show protocols 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 + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show protocols ospf3 + +- name: Merge Junos OSPFv3 config + junipernetworks.junos.junos_ospfv3: + config: + - areas: + - area_id: 0.0.0.100 + stub: + default_metric: 200 + set: true + interfaces: + - name: so-0/0/0.0 + priority: 3 + metric: 5 + state: merged + +# After state +# ----------- +# +# adimn# show protocols ospf3 +# area 0.0.0.100 { +# stub default-metric 200; +# interface so-0/0/0.0 { +# metric 5; +# priority 3; +# } +# } +# Using replaced +# +# Before state +# ------------ +# +# adimn# show protocols ospf3 +# area 0.0.0.100 { +# stub default-metric 200; +# interface so-0/0/0.0 { +# metric 5; +# priority 3; +# } +# } +- name: Replace Junos OSPFv3 config + junipernetworks.junos.junos_ospfv3: + config: + - areas: + - area_id: 0.0.0.100 + interfaces: + - name: so-0/0/0.0 + state: replaced + +# After state +# ----------- +# +# admin# show protocols ospf3 +# area 0.0.0.100 { +# interface so-0/0/0.0; +# } +# Using overridden +# +# Before state +# ------------ +# +# admin# show protocols ospf3 +# area 0.0.0.100 { +# interface so-0/0/0.0; +# } +- name: Override Junos OSPFv3 config + junipernetworks.junos.junos_ospfv3: + config: + - areas: + - area_id: 0.0.0.100 + stub: + default_metric: 200 + set: true + interfaces: + - name: so-0/0/0.0 + priority: 3 + metric: 5 + flood_reduction: true + passive: true + - area_id: 0.0.0.200 + interfaces: + - name: ge-1/1/0.0 + - name: ge-2/2/0.0 + state: overridden + +# After state +# ----------- +# +# admin# show protocols ospf3 +# area 0.0.0.100 { +# stub default-metric 200; +# interface so-0/0/0.0 { +# passive; +# metric 5; +# priority 3; +# flood-reduction; +# } +# } +# area 0.0.0.200 { +# interface ge-1/1/0.0; +# interface ge-2/2/0.0; +# } +# +# Using deleted +# +# Before state +# ------------ +# +# adimn# show protocols ospf3 +# area 0.0.0.100 { +# stub default-metric 200; +# interface so-0/0/0.0 { +# metric 5; +# priority 3; +# } +# } + +- name: Delete Junos OSPFv3 config + junipernetworks.junos.junos_ospfv3: + config: + - areas: + - area_id: 0.0.0.100 + interfaces: + - name: so-0/0/0.0 + state: deleted + +# After state +# ----------- +# +# admin# show protocols ospf3 +# Using gathered +# +# Before state +# ------------ +# +# adimn# show protocols ospf3 +# area 0.0.0.100 { +# stub default-metric 200; +# interface so-0/0/0.0 { +# passive; +# metric 5; +# priority 3; +# flood-reduction; +# } +# } +# area 0.0.0.200 { +# interface ge-1/1/0.0; +# interface ge-2/2/0.0; +# } + +- name: Gather Junos OSPFv3 config + junipernetworks.junos.junos_ospfv3: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "areas": [ +# { +# "area_id": "0.0.0.100", +# "interfaces": [ +# { +# "flood_reduction": true, +# "metric": 5, +# "name": "so-0/0/0.0", +# "passive": true, +# "priority": 3 +# } +# ], +# "stub": { +# "default_metric": 200, +# "set": true +# } +# }, +# { +# "area_id": "0.0.0.200", +# "interfaces": [ +# { +# "name": "ge-1/1/0.0" +# }, +# { +# "name": "ge-2/2/0.0" +# } +# ] +# } +# ], +# } +# +# Using rendered +# +# +- name: Render the commands for provided configuration + junipernetworks.junos.junos_ospfv3: + config: + - areas: + - area_id: 0.0.0.100 + stub: + default_metric: 200 + set: true + interfaces: + - name: so-0/0/0.0 + priority: 3 + metric: 5 + flood_reduction: true + passive: true + - area_id: 0.0.0.200 + interfaces: + - name: ge-1/1/0.0 + - name: ge-2/2/0.0 + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": " +# <nc:protocols +# xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:ospf3> +# <nc:area> +# <nc:name>0.0.0.100</nc:name> +# <nc:interface> +# <nc:name>so-0/0/0.0</nc:name> +# <nc:priority>3</nc:priority> +# <nc:flood-reduction/> +# <nc:metric>5</nc:metric> +# <nc:passive/> +# </nc:interface> +# <nc:stub> +# <nc:default-metric>200</nc:default-metric> +# </nc:stub> +# </nc:area> +# <nc:area> +# <nc:name>0.0.0.200</nc:name> +# <nc:interface> +# <nc:name>ge-1/1/0.0</nc:name> +# </nc:interface> +# <nc:interface> +# <nc:name>ge-2/2/0.0</nc:name> +# </nc:interface> +# </nc:area> +# </nc:ospf3> +# </nc:protocols>" +# +# Using parsed +# parsed.cfg +# ------------ +# <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:junos="http://xml.juniper.net/junos/18.4R1/junos"> +# <data> +# <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm" +# junos:commit-seconds="1601355317" junos:commit-localtime="2020-09-29 04:55:17 UTC" junos:commit-user="rohit"> +# <version>18.4R1-S2.4</version> +# <interfaces> +# <interface> +# <name>ge-0/0/0</name> +# <description>Configured by Ansi-Team</description> +# </interface> +# <interface> +# <name>gr-0/0/0</name> +# <description>Configured Manually</description> +# </interface> +# <interface> +# <name>fxp0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <dhcp> +# </dhcp> +# </inet> +# </family> +# </unit> +# </interface> +# </interfaces> +# <protocols> +# <ospf3> +# <area> +# <name>0.0.0.100</name> +# <stub> +# <default-metric>200</default-metric> +# </stub> +# <interface> +# <name>so-0/0/0.0</name> +# <passive> +# </passive> +# <metric>5</metric> +# <priority>3</priority> +# <flood-reduction/> +# </interface> +# </area> +# <area> +# <name>0.0.0.200</name> +# <interface> +# <name>ge-1/1/0.0</name> +# </interface> +# <interface> +# <name>ge-2/2/0.0</name> +# </interface> +# </area> +# </ospf3> +# </protocols> +# <routing-options> +# <router-id>10.200.16.75</router-id> +# </routing-options> +# </configuration> +# <database-status-information> +# <database-status> +# <user>rohit</user> +# <terminal>pts/0</terminal> +# <pid>38210</pid> +# <start-time junos:seconds="1601354977">2020-09-29 04:49:37 UTC</start-time> +# <idle-time junos:seconds="546">00:09:06</idle-time> +# <edit-path>[edit]</edit-path> +# </database-status> +# </database-status-information> +# </data> +# </rpc-reply> + +- name: Parsed the device configuration to get output commands + junipernetworks.junos.junos_ospfv3: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "areas": [ +# { +# "area_id": "0.0.0.100", +# "interfaces": [ +# { +# "flood_reduction": true, +# "metric": 5, +# "name": "so-0/0/0.0", +# "passive": true, +# "priority": 3 +# } +# ], +# "stub": { +# "default_metric": 200, +# "set": true +# } +# }, +# { +# "area_id": "0.0.0.200", +# "interfaces": [ +# { +# "name": "ge-1/1/0.0" +# }, +# { +# "name": "ge-2/2/0.0" +# } +# ] +# } +# ], +# } +# ] +# +""" +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: ['<nc:protocols + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:ospf3 delete=\"delete\"/> + <nc:ospf3> + <nc:area> + <nc:name>0.0.0.100</nc:name> + <nc:interface> + <nc:name>so-0/0/0.0</nc:name> + <nc:priority>3</nc:priority> + <nc:flood-reduction/> + <nc:metric>5</nc:metric> + <nc:passive/> + </nc:interface> + <nc:stub> + <nc:default-metric>200</nc:default-metric> + </nc:stub> + </nc:area> + <nc:area> + <nc:name>0.0.0.200</nc:name> + <nc:interface> + <nc:name>ge-1/1/0.0</nc:name> + </nc:interface> + <nc:interface> + <nc:name>ge-2/2/0.0</nc:name> + </nc:interface> + </nc:area> + </nc:ospf3> +</nc:protocols>", + " +<nc:routing-options + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:router-id delete=\"delete\"/> + <nc:router-id>10.200.16.75</nc:router-id> +</nc:routing-options>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.ospfv3.ospfv3 import ( + Ospfv3, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Ospfv3Args.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Ospfv3(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_package.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_package.py new file mode 100644 index 000000000..0cffb4ddb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_package.py @@ -0,0 +1,218 @@ +#!/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: junos_package +author: Peter Sprygada (@privateip) +short_description: Installs packages on remote devices running Junos +description: +- This module can install new and updated packages on remote devices running Junos. The + module will compare the specified package with the one running on the remote device + and install the specified version if there is a mismatch +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + src: + description: + - The I(src) argument specifies the path to the source package to be installed + on the remote device in the advent of a version mismatch. The I(src) argument + can be either a localized path or a full path to the package file to install. + required: true + type: path + aliases: + - package + version: + description: + - The I(version) argument can be used to explicitly specify the version of the + package that should be installed on the remote device. If the I(version) argument + is not specified, then the version is extracts from the I(src) filename. + type: str + reboot: + description: + - In order for a package to take effect, the remote device must be restarted. When + enabled, this argument will instruct the module to reboot the device once the + updated package has been installed. If disabled or the remote package does not + need to be changed, the device will not be started. + type: bool + default: yes + no_copy: + description: + - The I(no_copy) argument is responsible for instructing the remote device on + where to install the package from. When enabled, the package is transferred + to the remote device prior to installing. + type: bool + default: no + validate: + description: + - The I(validate) argument is responsible for instructing the remote device to + skip checking the current device configuration compatibility with the package + being installed. When set to false validation is not performed. + type: bool + default: yes + force: + description: + - The I(force) argument instructs the module to bypass the package version check + and install the packaged identified in I(src) on the remote device. + type: bool + default: no + force_host: + description: + - The I(force_host) argument controls the way software package or bundle is added + on remote JUNOS host and is applicable for JUNOS QFX5100 device. If the value + is set to C(True) it will ignore any warnings while adding the host software + package or bundle. + type: bool + default: false + issu: + description: + - The I(issu) argument is a boolean flag when set to C(True) allows unified in-service + software upgrade (ISSU) feature which enables you to upgrade between two different + Junos OS releases with no disruption on the control plane and with minimal disruption + of traffic. + type: bool + default: false + ssh_private_key_file: + description: + - The C(ssh_private_key_file) argument is path to the SSH private key file. This + can be used if you need to provide a private key rather than loading the key + into the ssh-key-ring/environment + type: path + ssh_config: + description: + - The C(ssh_config) argument is path to the SSH configuration file. This can be + used to load SSH information from a configuration file. If this option is not + given by default ~/.ssh/config is queried. + type: path +requirements: +- junos-eznc +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Works with C(local) connections only. +- Since this module uses junos-eznc to establish connection with junos device the + netconf configuration parameters needs to be passed using module options for example + C(ssh_config) unlike other junos modules that uses C(netconf) connection type. +""" + +EXAMPLES = """ +# the required set of connection arguments have been purposely left off +# the examples for brevity + +- name: install local package on remote device + junipernetworks.junos.junos_package: + src: junos-vsrx-12.1X46-D10.2-domestic.tgz + +- name: install local package on remote device without rebooting + junipernetworks.junos.junos_package: + src: junos-vsrx-12.1X46-D10.2-domestic.tgz + reboot: no + +- name: install local package on remote device with jumpost + junipernetworks.junos.junos_package: + src: junos-vsrx-12.1X46-D10.2-domestic.tgz + ssh_config: /home/user/customsshconfig +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_device, +) + + +try: + from jnpr.junos.utils.sw import SW + + HAS_PYEZ = True +except ImportError: + HAS_PYEZ = False + + +def install_package(module, device): + junos = SW(device) + package = module.params["src"] + no_copy = module.params["no_copy"] + validate = module.params["validate"] + force_host = module.params["force_host"] + issu = module.params["issu"] + + def progress_log(dev, report): + module.log(report) + + module.log("installing package") + result = junos.install( + package, + progress=progress_log, + no_copy=no_copy, + validate=validate, + force_host=force_host, + issu=issu, + ) + + if not result: + module.fail_json(msg="Unable to install package on device") + + if module.params["reboot"]: + module.log("rebooting system") + junos.reboot() + + +def main(): + """Main entry point for Ansible module execution""" + argument_spec = dict( + src=dict(type="path", required=True, aliases=["package"]), + version=dict(), + reboot=dict(type="bool", default=True), + no_copy=dict(default=False, type="bool"), + validate=dict(default=True, type="bool"), + force=dict(type="bool", default=False), + force_host=dict(type="bool", default=False), + issu=dict(type="bool", default=False), + ssh_private_key_file=dict(type="path"), + ssh_config=dict(type="path"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + if not HAS_PYEZ: + module.fail_json( + msg="junos-eznc is required but does not appear to be installed. " + "It can be installed using `pip install junos-eznc`", + ) + + result = dict(changed=False) + + do_upgrade = module.params["force"] or False + + device = get_device(module) + + if not module.params["force"]: + device.facts_refresh() + has_ver = device.facts.get("version") + wants_ver = module.params["version"] + do_upgrade = has_ver != wants_ver + + if do_upgrade: + if not module.check_mode: + install_package(module, device) + result["changed"] = True + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_ping.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ping.py new file mode 100644 index 000000000..4c373474c --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_ping.py @@ -0,0 +1,290 @@ +#!/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: junos_ping +short_description: Tests reachability using ping from devices running Juniper JUNOS +description: +- Tests reachability using ping from devices running Juniper JUNOS to a remote destination. +- Tested against Junos (17.3R1.10) +- For a general purpose network module, see the M(ansible.netcommon.net_ping) module. +- For Windows targets, use the M(ansible.windows.win_ping) module instead. +- For targets running Python, use the M(ansible.builtin.ping) module instead. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +author: +- Nilashish Chakraborty (@NilashishC) +options: + dest: + description: + - The IP Address or hostname (resolvable by the device) of the remote node. + type: str + required: true + df_bit: + description: + - Determines whether to set the DF bit. + type: bool + default: false + rapid: + description: + - Determines whether to send the packets rapidly. + type: bool + default: false + count: + description: + - Number of packets to send to check reachability. + type: int + default: 5 + source: + description: + - The IP Address to use while sending the ping packet(s). + type: str + interface: + description: + - The source interface to use while sending the ping packet(s). + type: str + ttl: + description: + - The time-to-live value for the ICMP packet(s). + type: int + size: + description: + - Determines the size (in bytes) of the ping packet(s). + type: int + interval: + description: + - Determines the interval (in seconds) between consecutive pings. + type: int + state: + description: + - Determines if the expected result is success or fail. + type: str + choices: + - absent + - present + default: present +notes: +- For a general purpose network module, see the M(ansible.netcommon.net_ping) module. +- For Windows targets, use the M(ansible.windows.win_ping) module instead. +- For targets running Python, use the M(ansible.builtin.ping) module instead. +- This module works only with connection C(network_cli). +""" + +EXAMPLES = """ +- name: Test reachability to 10.10.10.10 + junipernetworks.junos.junos_ping: + dest: 10.10.10.10 + +- name: Test reachability to 10.20.20.20 using source and size set + junipernetworks.junos.junos_ping: + dest: 10.20.20.20 + size: 1024 + ttl: 128 + +- name: Test unreachability to 10.30.30.30 using interval + junipernetworks.junos.junos_ping: + dest: 10.30.30.30 + interval: 3 + state: absent + +- name: Test reachability to 10.40.40.40 setting count and interface + junipernetworks.junos.junos_ping: + dest: 10.40.40.40 + interface: fxp0 + count: 20 + size: 512 + +- name: Test reachability to 10.50.50.50 using do-not-fragment and rapid + junipernetworks.junos.junos_ping: + dest: 10.50.50.50 + df_bit: True + rapid: True +""" + +RETURN = """ +commands: + description: List of commands sent. + returned: always + type: list + sample: ["ping 10.8.38.44 count 10 source 10.8.38.38 ttl 128"] +packet_loss: + description: Percentage of packets lost. + returned: always + type: str + sample: "0%" +packets_rx: + description: Packets successfully received. + returned: always + type: int + sample: 20 +packets_tx: + description: Packets successfully transmitted. + returned: always + type: int + sample: 20 +rtt: + description: The round trip time (RTT) stats. + returned: when ping succeeds + type: dict + sample: {"avg": 2, "max": 8, "min": 1, "stddev": 24} +""" + +import re + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_connection, +) + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + count=dict(type="int", default=5), + dest=dict(type="str", required=True), + df_bit=dict(type="bool", default=False), + rapid=dict(type="bool", default=False), + source=dict(), + interface=dict(), + ttl=dict(type="int"), + size=dict(type="int"), + interval=dict(type="int"), + state=dict( + type="str", + choices=["absent", "present"], + default="present", + ), + ) + + module = AnsibleModule(argument_spec=argument_spec) + + count = module.params["count"] + dest = module.params["dest"] + df_bit = module.params["df_bit"] + rapid = module.params["rapid"] + source = module.params["source"] + size = module.params["size"] + ttl = module.params["ttl"] + interval = module.params["interval"] + interface = module.params["interface"] + warnings = list() + + results = {"changed": False} + if warnings: + results["warnings"] = warnings + + results["commands"] = build_ping( + dest, + count, + size, + interval, + source, + ttl, + interface, + df_bit, + rapid, + ) + conn = get_connection(module) + + ping_results = conn.get(results["commands"]) + + rtt_info, rate_info = None, None + for line in ping_results.split("\n"): + if line.startswith("round-trip"): + rtt_info = line + if line.startswith("%s packets transmitted" % count): + rate_info = line + + if rtt_info: + rtt = parse_rtt(rtt_info) + for k, v in rtt.items(): + if rtt[k] is not None: + rtt[k] = float(v) + results["rtt"] = rtt + + pkt_loss, rx, tx = parse_rate(rate_info) + results["packet_loss"] = str(pkt_loss) + "%" + results["packets_rx"] = int(rx) + results["packets_tx"] = int(tx) + + validate_results(module, pkt_loss, results) + + module.exit_json(**results) + + +def build_ping( + dest, + count, + size=None, + interval=None, + source=None, + ttl=None, + interface=None, + df_bit=False, + rapid=False, +): + cmd = "ping {0} count {1}".format(dest, str(count)) + + if source: + cmd += " source {0}".format(source) + + if interface: + cmd += " interface {0}".format(interface) + + if ttl: + cmd += " ttl {0}".format(str(ttl)) + + if size: + cmd += " size {0}".format(str(size)) + + if interval: + cmd += " interval {0}".format(str(interval)) + + if df_bit: + cmd += " do-not-fragment" + + if rapid: + cmd += " rapid" + + return cmd + + +def parse_rate(rate_info): + rate_re = re.compile( + r"(?P<tx>\d*) packets transmitted,(?:\s*)(?P<rx>\d*) packets received,(?:\s*)(?P<pkt_loss>\d*)% packet loss", + ) + rate = rate_re.match(rate_info) + + return rate.group("pkt_loss"), rate.group("rx"), rate.group("tx") + + +def parse_rtt(rtt_info): + rtt_re = re.compile( + r"round-trip (?:.*)=(?:\s*)(?P<min>\d+\.\d+).(?:\d*)/(?P<avg>\d+\.\d+).(?:\d*)/(?P<max>\d*\.\d*).(?:\d*)/(?P<stddev>\d*\.\d*)", + ) + rtt = rtt_re.match(rtt_info) + + return rtt.groupdict() + + +def validate_results(module, loss, results): + state = module.params["state"] + if state == "present" and int(loss) == 100: + module.fail_json(msg="Ping failed unexpectedly", **results) + elif state == "absent" and int(loss) < 100: + module.fail_json(msg="Ping succeeded unexpectedly", **results) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_prefix_lists.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_prefix_lists.py new file mode 100644 index 000000000..40b101f44 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_prefix_lists.py @@ -0,0 +1,682 @@ +#!/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 junos_prefix_lists +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_prefix_lists +version_added: 2.1.0 +short_description: Manage prefix-lists attributes of interfaces on Junos devices. +description: Manage prefix-lists attributes of interfaces on Junos network devices. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show policy-options). + - 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: The provided link BGP address family dictionary. + type: list + elements: dict + suboptions: + name: + description: Specify the name of the prefix-list. + type: str + required: true + address_prefixes: + description: Specify address prefixes. + type: list + elements: str + dynamic_db: + description: Enable object to exist in dynamic DB. + type: bool + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# +# [edit] + +- name: Merge Junos prefix lists + junipernetworks.junos.junos_prefix_lists: + config: + - name: Internal + address_prefixes: + - 172.16.1.32 + - 172.16.3.32 + - name: Test1 + dynamic_db: true + - name: Test2 + address_prefixes: + - 172.16.2.32 + - 172.16.7.32 + - 172.16.9.32 + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [] +# "commands": [ +# "<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list><nc:name>Internal</nc:name><nc:prefix-list-item><nc:name>172.16.1.32</nc:name>" +# "</nc:prefix-list-item><nc:prefix-list-item><nc:name>172.16.3.32</nc:name>" +# "</nc:prefix-list-item></nc:prefix-list><nc:prefix-list><nc:name>Test1</nc:name>" +# "<nc:dynamic-db/></nc:prefix-list><nc:prefix-list><nc:name>Test2</nc:name>" +# "<nc:prefix-list-item><nc:name>172.16.2.32</nc:name></nc:prefix-list-item>" +# "<nc:prefix-list-item><nc:name>172.16.7.32</nc:name></nc:prefix-list-item>" +# "<nc:prefix-list-item><nc:name>172.16.9.32</nc:name></nc:prefix-list-item>" +# "</nc:prefix-list></nc:policy-options>" +# ] +# +# "after": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.2.32/32", +# "172.16.7.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# After state +# ----------- +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.2.32/32; +# 172.16.7.32/32; +# 172.16.9.32/32; +# } +# +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.2.32/32; +# 172.16.7.32/32; +# 172.16.9.32/32; +# } + +- name: Gather Junos prefix-lists + junipernetworks.junos.junos_prefix_lists: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.2.32/32", +# "172.16.7.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# +# Using replaced +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.2.32/32; +# 172.16.7.32/32; +# 172.16.9.32/32; +# } +- name: Replace existing Junos prefix-lists configuration with provided config + junipernetworks.junos.junos_prefix_lists: + config: + - name: Test2 + address_prefixes: + - 172.16.4.32 + - 172.16.8.32 + - 172.16.9.32" + state: replaced +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.2.32/32", +# "172.16.7.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# "commands": [ +# "<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list delete=\"delete\"><nc:name>Test2</nc:name></nc:prefix-list>" +# "<nc:prefix-list><nc:name>Test2</nc:name><nc:prefix-list-item><nc:name>172.16.4.32</nc:name>" +# "</nc:prefix-list-item><nc:prefix-list-item><nc:name>172.16.8.32</nc:name>" +# "</nc:prefix-list-item><nc:prefix-list-item><nc:name>172.16.9.32</nc:name>" +# "</nc:prefix-list-item></nc:prefix-list></nc:policy-options>" +# ] +# +# "after": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.4.32/32", +# "172.16.8.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# After state +# ----------- +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.4.32/32; +# 172.16.8.32/32; +# 172.16.9.32/32; +# } +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.4.32/32; +# 172.16.8.32/32; +# 172.16.9.32/32; +# } +- name: Override Junos prefix-lists configuration with provided configuration + junipernetworks.junos.junos_prefix_lists: + config: + - name: Test2 + address_prefixes: + - 172.16.4.32/28 + - 172.16.8.32/28 + - 172.16.9.32/28 + state: overridden + +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.4.32/32", +# "172.16.8.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# "commands": [ +# "<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list delete=\"delete\"><nc:name>Internal</nc:name>" +# "</nc:prefix-list><nc:prefix-list delete=\"delete\"><nc:name>Test1</nc:name>" +# "</nc:prefix-list><nc:prefix-list delete=\"delete\"><nc:name>Test2</nc:name>" +# "</nc:prefix-list><nc:prefix-list><nc:name>Test2</nc:name><nc:prefix-list-item>" +# "<nc:name>172.16.4.32/28</nc:name></nc:prefix-list-item><nc:prefix-list-item>" +# "<nc:name>172.16.8.32/28</nc:name></nc:prefix-list-item><nc:prefix-list-item>" +# "<nc:name>172.16.9.32/28</nc:name></nc:prefix-list-item></nc:prefix-list></nc:policy-options>" +# ] +# +# "after": [ +# { +# "address_prefixes": [ +# "172.16.4.32/28", +# "172.16.8.32/28", +# "172.16.9.32/28" +# ], +# "name": "Test2" +# } +# ] +# After state +# ----------- +# +# vagrant@vsrx# show policy-options +# prefix-list Test2 { +# 172.16.4.32/28; +# 172.16.8.32/28; +# 172.16.9.32/28; +# } +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.2.32/32; +# 172.16.7.32/32; +# 172.16.9.32/32; +# } + +- name: Delete provided prefix-lists + junipernetworks.junos.junos_prefix_lists: + config: + - name: "Test1" + - name: "Test2" + state: deleted +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.2.32/32", +# "172.16.7.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# "commands": [ +# "<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list delete=\"delete\"><nc:name>Test1</nc:name></nc:prefix-list>" +# "<nc:prefix-list delete=\"delete\"><nc:name>Test2</nc:name></nc:prefix-list></nc:policy-options>" +# ] +# +# "after": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# } +# ] +# After state +# ----------- +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# +# Using deleted without specifying config +# +# Before state +# ------------ +# +# vagrant@vsrx# show policy-options +# prefix-list Internal { +# 172.16.1.32/32; +# 172.16.3.32/32; +# } +# prefix-list Test1 { +# dynamic-db; +# } +# prefix-list Test2 { +# 172.16.2.32/32; +# 172.16.7.32/32; +# 172.16.9.32/32; +# } + +- name: Delete complete Junos prefix-lists configuration + junipernetworks.junos.junos_prefix_lists: + state: deleted + +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": [ +# { +# "address_prefixes": [ +# "172.16.1.32/32", +# "172.16.3.32/32" +# ], +# "name": "Internal" +# }, +# { +# "dynamic_db": true, +# "name": "Test1" +# }, +# { +# "address_prefixes": [ +# "172.16.2.32/32", +# "172.16.7.32/32", +# "172.16.9.32/32" +# ], +# "name": "Test2" +# } +# ] +# "commands": ["<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list delete=\"delete\"/></nc:policy-options>" +# ] +# +# "after": [] +# After state +# ----------- +# +# vagrant@vsrx# show policy-options +# +# [edit] + +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <policy-options> +# <prefix-list> +# <name>64510</name> +# </prefix-list> +# <prefix-list> +# <name>64500</name> +# <dynamic-db/> +# <prefix-list-item> +# <name>172.16.1.16/28</name> +# </prefix-list-item> +# <prefix-list-item> +# <name>172.16.1.32/28</name> +# </prefix-list-item> +# </prefix-list> +# </policy-options> +# </configuration> +# </rpc-reply> +- name: Parse running prefix-lists configuration + junipernetworks.junos.junos_prefix_lists: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "name": "64510" +# }, +# { +# "address_prefixes": [ +# "172.16.1.16/28", +# "172.16.1.32/28" +# ], +# "dynamic_db": true, +# "name": "64500" +# } +# ] +# +# +# Using rendered +# +- name: Render the xml for provided configuration + junipernetworks.junos.junos_prefix_lists: + config: + - name: Internal + address_prefixes: + - 172.16.1.32 + - 172.16.3.32 + - name: Test1 + dynamic_db: true + - name: Test2 + address_prefixes: + - 172.16.2.32 + - 172.16.7.32 + - 172.16.9.32 + state: rendered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": "<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:prefix-list><nc:name>Internal</nc:name><nc:prefix-list-item><nc:name>172.16.1.32</nc:name>" +# "</nc:prefix-list-item><nc:prefix-list-item><nc:name>172.16.3.32</nc:name></nc:prefix-list-item>" +# "</nc:prefix-list><nc:prefix-list><nc:name>Test1</nc:name><nc:dynamic-db/></nc:prefix-list>" +# "<nc:prefix-list><nc:name>Test2</nc:name><nc:prefix-list-item><nc:name>172.16.2.32</nc:name>" +# "</nc:prefix-list-item><nc:prefix-list-item><nc:name>172.16.7.32</nc:name></nc:prefix-list-item>" +# "<nc:prefix-list-item><nc:name>172.16.9.32</nc:name></nc:prefix-list-item>" +# "</nc:prefix-list></nc:policy-options>" +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:policy-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + "<nc:prefix-list delete=\"delete\"/></nc:policy-options>"', 'xml 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.prefix_lists.prefix_lists import ( + Prefix_listsArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.prefix_lists.prefix_lists import ( + Prefix_lists, +) + + +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",)), + ] + module = AnsibleModule( + argument_spec=Prefix_listsArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Prefix_lists(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_instances.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_instances.py new file mode 100644 index 000000000..1b7c1c995 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_instances.py @@ -0,0 +1,766 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_routing_instances +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_routing_instances +version_added: 2.1.0 +short_description: Manage routing instances on Junos devices. +description: Manage routing instances on Junos network devices. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show routing-instances). + - 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: The provided Routing instance configuration list. + type: list + elements: dict + suboptions: + name: + description: Specify routing instance name. + type: str + connector_id_advertise: + description: Advertise connector-id attribute. + type: bool + description: + description: Specify text description of routing instance. + type: str + egress_protection: + description: Egress instance protection dictionary. + type: dict + suboptions: + context_identifier: + description: Specify context identifier. + type: str + protector: + description: Enable Edge Protector functionality for this VPN. + type: bool + instance_role: + description: Primary role of L2Backhaul-vpn router. + type: str + choices: ['access', 'nni'] + type: + description: Specify instance type. + type: str + choices: + - evpn + - evpn-vpws + - forwarding + - l2backhaul-vpn + - l2vpn + - layer2-control + - mac-vrf + - mpls-forwarding + - mpls-internet-multicast + - no-forwarding + - virtual-router + - vpls + - vrf + interfaces: + description: Interface name for this routing instance. + type: list + elements: dict + suboptions: + name: + description: Specify name of the interface. + type: str + protect_interface: + description: Specify name of the protected interface. + type: str + l2vpn_id: + description: Layer-2 vpn-id for this instance. + type: str + no_irb_layer_2_copy: + description: Disable transmission of layer-2 copy of packets of irb routing-interface. + type: bool + no_normalization: + description: Disable vlan id normalization for interfaces. + type: bool + no_local_switching: + description: Disable vlan id normalization for interfaces. + type: bool + no_vrf_advertise: + description: Disable vlan id normalization for interfaces. + type: bool + no_vrf_propagate_ttl: + description: Disable TTL propagation from IP to MPLS (on push) and MPLS to IP (on pop). + type: bool + qualified_bum_pruning_mode: + description: Enable BUM pruning for VPLS instance. + type: bool + route_distinguisher: + description: Route distinguisher for this instance + type: str + routing_interface: + description: Routing interface name for this routing-instance. + type: list + elements: str + vrf_imports: + description: Import policy for VRF instance RIBs. + type: list + elements: str + vrf_exports: + description: Export policy for VRF instance RIBs. + type: list + elements: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# admin# show routing-instances +# +# [edit] +# vagrant@vsrx# show policy-options +# policy-statement test-policy { +# term t1 { +# then reject; +# } +# } +# policy-statement test-policy-1 { +# term t1 { +# then reject; +# } +# } + +- name: Merge Junos BGP address family configuration + junipernetworks.junos.junos_routing_instances: + config: + - name: "test" + type: "vrf" + route_distinguisher: "10.58.255.1:37" + vrf_imports: + - "test-policy" + vrf_exports: + - "test-policy" + - "test-policy-1" + interfaces: + - name: "sp-0/0/0.0" + - name: "gr-0/0/0.0" + connector_id_advertise: true + - name: "forwardinst" + type: "forwarding" + description: "Configured by Ansible Content Team" + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# After state +# ----------- +# +# admin# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } +# +# Using gathered +# +# Before state +# ------------ +# +# admin# show routing-instances +# +# [edit] +# admin# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } +- name: Gather Junos routing-instances + junipernetworks.junos.junos_routing_instances: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "description": "Configured by Ansible Content Team", +# "name": "forwardinst", +# "type": "forwarding" +# }, +# { +# "connector_id_advertise": true, +# "interfaces": [ +# { +# "name": "gr-0/0/0.0" +# }, +# { +# "name": "sp-0/0/0.0" +# } +# ], +# "name": "test", +# "route_distinguisher": "10.58.255.1:37", +# "type": "vrf", +# "vrf_exports": [ +# "test-policy", +# "test-policy-1" +# ], +# "vrf_imports": [ +# "test-policy" +# ] +# } +# ] +# +# Using replaced +# +# Before state +# ------------ +# +# admin# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } + +- name: Replace existing Junos routing instance config with provided config + junipernetworks.junos.junos_routing_instances: + config: + address_family: + - name: "test" + type: "vrf" + route_distinguisher: "10.57.255.1:37" + vrf_imports: + - "test-policy" + vrf_exports: + - "test-policy" + interfaces: + - name: "sp-0/0/0.0" + - name: "gr-0/0/0.0" + connector_id_advertise: false + description: "Configured by Ansible Content Team" + state: replaced + +# After state +# ----------- +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# description "Configured by Ansible Content Team"; +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.57.255.1:37; +# vrf-import test-policy; +# vrf-export test-policy; +# } + +# Using overridden +# +# Before state +# ------------ +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# description "Configured by Ansible Content Team"; +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.57.255.1:37; +# vrf-import test-policy; +# vrf-export test-policy; +# } + +- name: Override Junos routing-instances configuration + junipernetworks.junos.junos_routing_instances: + config: + - name: "test" + type: "vrf" + route_distinguisher: "10.58.255.1:37" + vrf_imports: + - "test-policy" + vrf_exports: + - "test-policy" + - "test-policy-1" + interfaces: + - name: "sp-0/0/0.0" + - name: "gr-0/0/0.0" + connector_id_advertise: true + - name: "forwardinst" + type: "forwarding" + description: "Configured by Ansible Content Team" + - name: "vtest1" + type: "virtual-router" + state: overridden + +# After state +# ----------- +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } +# vtest1 { +# instance-type virtual-router; +# } + + +# Using deleted +# +# Before state +# ------------ +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } + +- name: Delete provided junos routing-instamce + junipernetworks.junos.junos_routing_instances: + config: + - name: "test" + state: deleted + +# After state +# ----------- +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } + +# Using deleted without config +# +# Before state +# ------------ +# +# admin@vsrx# show routing-instances +# forwardinst { +# description "Configured by Ansible Content Team"; +# instance-type forwarding; +# } +# test { +# instance-type vrf; +# interface gr-0/0/0.0; ## 'gr-0/0/0.0' is not defined +# interface sp-0/0/0.0; ## 'sp-0/0/0.0' is not defined +# route-distinguisher 10.58.255.1:37; +# vrf-import test-policy; +# vrf-export [ test-policy test-policy-1 ]; +# connector-id-advertise; +# } +# vtest1 { +# instance-type virtual-router; +# } + +- name: Delete complete Junos routing-instances config + junipernetworks.junos.junos_routing_instances: + config: + state: deleted + +# After state +# ----------- +# +# admin@vsrx# show routing-instances +# +# [edit] + +- name: Gather Junos BGP address family config + junipernetworks.junos.junos_routing_instances: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "address_family": [ +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2001, +# "limit_threshold": 98, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "type": "signaling" +# } +# ], +# "afi": "evpn" +# }, +# { +# "af_type": [ +# { +# "accepted_prefix_limit": { +# "idle_timeout_value": 2000, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "damping": true, +# "defer_initial_multipath_build": { +# "maximum_delay": 2 +# }, +# "delay_route_advertisements": { +# "max_delay_route_age": 20, +# "max_delay_routing_uptime": 32000, +# "min_delay_inbound_convergence": 32000, +# "min_delay_routing_uptime": 23000 +# }, +# "graceful_restart_forwarding_state_bit": "from-fib", +# "type": "any" +# }, +# { +# "legacy_redirect_ip_action": { +# "receive": true, +# "send": true +# }, +# "loops": 4, +# "no_install": true, +# "output_queue_priority_expedited": true, +# "secondary_independent_resolution": true, +# "type": "flow" +# }, +# { +# "entropy_label": { +# "no_next_hop_validation": true +# }, +# "explicit_null": { +# "connected_only": true +# }, +# "per_group_label": true, +# "per_prefix_label": true, +# "prefix_limit": { +# "forever": true, +# "limit_threshold": 99, +# "maximum": 20 +# }, +# "resolve_vpn": true, +# "rib": "inet.3", +# "route_refresh_priority_priority": 3, +# "type": "labeled-unicast" +# }, +# { +# "extended_nexthop": true, +# "extended_nexthop_color": true, +# "local_ipv4_address": "9.9.9.9", +# "type": "unicast" +# } +# ], +# "afi": "inet" +# } +# ] +# } +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <routing-instances> +# <instance> +# <name>forwardinst</name> +# <description>Configured by Ansible Content Team</description> +# <instance-type>forwarding</instance-type> +# </instance> +# <instance> +# <name>test</name> +# <instance-type>vrf</instance-type> +# <interface> +# <name>gr-0/0/0.0</name> +# </interface> +# <interface> +# <name>sp-0/0/0.0</name> +# </interface> +# <route-distinguisher> +# <rd-type>10.58.255.1:37</rd-type> +# </route-distinguisher> +# <vrf-import>test-policy</vrf-import> +# <vrf-export>test-policy</vrf-export> +# <vrf-export>test-policy-1</vrf-export> +# <connector-id-advertise/> +# </instance> +# </routing-instances> +# </configuration> +# </rpc-reply> + +- name: Parse routing instance running config + junipernetworks.junos.junos_routing_instances: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "description": "Configured by Ansible Content Team", +# "name": "forwardinst", +# "type": "forwarding" +# }, +# { +# "connector_id_advertise": true, +# "interfaces": [ +# { +# "name": "gr-0/0/0.0" +# }, +# { +# "name": "sp-0/0/0.0" +# } +# ], +# "name": "test", +# "route_distinguisher": "10.58.255.1:37", +# "type": "vrf", +# "vrf_exports": [ +# "test-policy", +# "test-policy-1" +# ], +# "vrf_imports": [ +# "test-policy" +# ] +# } +# ] +# +# +# Using rendered +# +# +- name: Render the xml for provided configuration + junipernetworks.junos.junos_routing_instances: + config: + - name: "test" + type: "vrf" + route_distinguisher: "10.58.255.1:37" + vrf_imports: + - "test-policy" + vrf_exports: + - "test-policy" + - "test-policy-1" + interfaces: + - name: "sp-0/0/0.0" + - name: "gr-0/0/0.0" + connector_id_advertise: true + - name: "forwardinst" + type: "forwarding" + description: "Configured by Ansible Content Team" + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": "<nc:routing-instances xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# <nc:instance><nc:name>test</nc:name><nc:connector-id-advertise/><nc:instance-type>vrf</nc:instance-type> +# <nc:interface><nc:name>sp-0/0/0.0</nc:name></nc:interface><nc:interface><nc:name>gr-0/0/0.0</nc:name></nc:interface> +# <nc:route-distinguisher><nc:rd-type>10.58.255.1:37</nc:rd-type></nc:route-distinguisher> +# <nc:vrf-import>test-policy</nc:vrf-import><nc:vrf-export>test-policy</nc:vrf-export> +# <nc:vrf-export>test-policy-1</nc:vrf-export></nc:instance> +# <nc:instance><nc:name>forwardinst</nc:name><nc:description>Configured by Ansible Content Team</nc:description> +# <nc:instance-type>forwarding</nc:instance-type></nc:instance></nc:routing-instances>" +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:routing-instances + xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:instance> + <nc:name>test</nc:name> + <nc:connector-id-advertise/> + <nc:instance-type>vrf</nc:instance-type> + <nc:interface> + <nc:name>sp-0/0/0.0</nc:name> + </nc:interface> + <nc:interface> + <nc:name>gr-0/0/0.0</nc:name> + </nc:interface> + <nc:route-distinguisher> + <nc:rd-type>10.58.255.1:37</nc:rd-type> + </nc:route-distinguisher> + <nc:vrf-import>test-policy</nc:vrf-import> + <nc:vrf-export>test-policy</nc:vrf-export> + <nc:vrf-export>test-policy-1</nc:vrf-export> + </nc:instance> + </routing-instances> + </configuration> + </rpc-reply>', 'xml2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.routing_instances.routing_instances import ( + Routing_instancesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.routing_instances.routing_instances import ( + Routing_instances, +) + + +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",)), + ] + module = AnsibleModule( + argument_spec=Routing_instancesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Routing_instances(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_options.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_options.py new file mode 100644 index 000000000..c1935b4d1 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_routing_options.py @@ -0,0 +1,402 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_routing_options +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: junos_routing_options +version_added: 2.8.0 +short_description: Manage routing-options configuration on Junos devices. +description: This module manages routing-options configuration on devices running Junos. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show system routing-options). + - 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 routing-options configuration. + type: dict + suboptions: + autonomous_system: + description: Specify Autonomous system number. + type: dict + suboptions: + as_number: + description: Specify Autonomous system number. + type: str + required: true + loops: + description: Specify maximum number of times this AS can be in an AS path. + type: int + asdot_notation: + description: Enable AS-Dot notation to display true 4 byte AS numbers. + type: bool + router_id: + description: Specify Router identifier. + type: str + state: + description: + - The state the configuration should be left in. + - Refer to examples for more details. + type: str + choices: + - merged + - replaced + - deleted + - overridden + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show system routing-options +# +- name: Merge provided NTP configuration into running configuration. + junipernetworks.junos.junos_routing_options: + config: + autonomous_system: + as_number: 2 + asdot_notation: true + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# } +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "<nc:routing-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:autonomous-system>2<nc:asdot-notation/></nc:autonomous-system></nc:routing-options>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-options +# autonomous-system 2 asdot-notation; +# +# +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx# show routing-options +# autonomous-system 2 asdot-notation; + +- name: Replaced running routing-options configuration with provided configuration + junipernetworks.junos.junos_routing_options: + config: + autonomous_system: + as_number: 2 + asdot_notation: true + router_id: "1.1.1.1" + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# }, +# "router_id": "1.1.1.1" +# }, +# "before": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# } +# }, +# "changed": true, +# "commands": [ +# "<nc:routing-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:autonomous-system delete=\"delete\"/><nc:autonomous-system>2<nc:asdot-notation/>" +# "</nc:autonomous-system><nc:router-id>1.1.1.1</nc:router-id></nc:routing-options>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-options +# router-id 1.1.1.1; +# autonomous-system 2 asdot-notation; + + +# Using overridden +# +# vagrant@vsrx# show routing-options +# autonomous-system 2 asdot-notation; + +- name: Override running routing-options configuration with provided configuration + junipernetworks.junos.junos_routing_options: + config: + autonomous_system: + as_number: 2 + asdot_notation: true + router_id: "1.1.1.1" + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# }, +# "router_id": "1.1.1.1" +# }, +# "before": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# } +# }, +# "changed": true, +# "commands": [ +# "<nc:routing-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:autonomous-system delete=\"delete\"/><nc:autonomous-system>2<nc:asdot-notation/>" +# "</nc:autonomous-system><nc:router-id>1.1.1.1</nc:router-id></nc:routing-options>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-options +# router-id 1.1.1.1; +# autonomous-system 2 asdot-notation; +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show routing-options +# router-id 1.1.1.1; +# autonomous-system 2 asdot-notation; +# +- name: Delete running routing-options configuration + junipernetworks.junos.junos_routing_options: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# }, +# "router_id": "1.1.1.1" +# }, +# "changed": true, +# "commands": [ +# "<nc:routing-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:autonomous-system delete=\"delete\"/><nc:router-id delete=\"delete\"/></nc:routing-options>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-options +# +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show routing-options +# router-id 1.1.1.1; +# autonomous-system 2 asdot-notation; + +- name: Gather running routing-options configuration + junipernetworks.junos.junos_routing_options: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true +# }, +# "router_id": "1.1.1.1" +# }, +# "changed": false, +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_routing_options: + config: + autonomous_system: + as_number: 2 + asdot_notation: true + loops: 4 + router_id: 12.12.12.12 + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:routing-options xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> +# "<nc:autonomous-system>2<nc:loops>4</nc:loops><nc:asdot-notation/></nc:autonomous-system> +# "<nc:router-id>12.12.12.12</nc:router-id></nc:routing-options>" +# ] +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <routing-options> +# <router-id>12.12.12.12</router-id> +# <autonomous-system> +# <as-number>2</as-number> +# <loops>4</loops> +# <asdot-notation/> +# </autonomous-system> +# </routing-options> +# </configuration> +# </rpc-reply> +# +- name: Parse routing-options running config + junipernetworks.junos.junos_routing_options: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "autonomous_system": { +# "as_number": "2", +# "asdot_notation": true, +# "loops": 4 +# }, +# "router_id": "12.12.12.12" +# } +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:autonomous-system delete=\"delete\"/><nc:router-id delete=\"delete\"/></nc:routing-options>'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.routing_options.routing_options import ( + Routing_optionsArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.routing_options.routing_options import ( + Routing_options, +) + + +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=Routing_optionsArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Routing_options(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_rpc.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_rpc.py new file mode 100644 index 000000000..9fda891c3 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_rpc.py @@ -0,0 +1,177 @@ +#!/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: junos_rpc +author: Peter Sprygada (@privateip) +short_description: Runs an arbitrary RPC over NetConf on an Juniper JUNOS device +description: +- Sends a request to the remote device running JUNOS to execute the specified RPC + using the NetConf transport. The reply is then returned to the playbook in the + C(xml) key. If an alternate output format is requested, the reply is transformed + to the requested output. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + rpc: + description: + - The C(rpc) argument specifies the RPC call to send to the remote devices to + be executed. The RPC Reply message is parsed and the contents are returned + to the playbook. + type: str + required: true + args: + description: + - The C(args) argument provides a set of arguments for the RPC call and are encoded + in the request message. This argument accepts a set of key=value arguments. + type: dict + attrs: + description: + - The C(attrs) arguments defines a list of attributes and their values to set + for the RPC call. This accepts a dictionary of key-values. + type: dict + output: + description: + - The C(output) argument specifies the desired output of the return data. This + argument accepts one of C(xml), C(text), or C(json). For C(json), the JUNOS + device must be running a version of software that supports native JSON output. + default: xml + type: str + choices: ["xml", "json", "text"] +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: collect interface information using rpc + junipernetworks.junos.junos_rpc: + rpc: get-interface-information + args: + interface-name: em0 + media: true + +- name: get system information + junipernetworks.junos.junos_rpc: + rpc: get-system-information + +- name: load configuration + junipernetworks.junos.junos_rpc: + rpc: load-configuration + attrs: + action: override + url: /tmp/config.conf +""" + +RETURN = """ +xml: + description: The xml return string from the rpc request. + returned: always + type: str +output: + description: The rpc rely converted to the output format. + returned: always + type: str +output_lines: + description: The text output split into lines for readability. + returned: always + type: list +""" +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + exec_rpc, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + tostring, +) + + +USE_PERSISTENT_CONNECTION = True + +try: + from lxml.etree import Element, SubElement +except ImportError: + from xml.etree.ElementTree import Element, SubElement + + +def main(): + """main entry point for Ansible module""" + argument_spec = dict( + rpc=dict(required=True), + args=dict(type="dict"), + attrs=dict(type="dict"), + output=dict(default="xml", choices=["xml", "json", "text"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + ) + + warnings = list() + result = {"changed": False, "warnings": warnings} + + rpc = str(module.params["rpc"]).replace("_", "-") + + if all((module.check_mode, not rpc.startswith("get"))): + module.fail_json(msg="invalid rpc for running in check_mode") + + args = module.params["args"] or {} + attrs = module.params["attrs"] or {} + + xattrs = {"format": module.params["output"]} + + for key, value in iteritems(attrs): + xattrs.update({key: value}) + + element = Element(module.params["rpc"], xattrs) + + for key, value in iteritems(args): + key = str(key).replace("_", "-") + if isinstance(value, list): + for item in value: + child = SubElement(element, key) + if item is not True: + child.text = item + else: + child = SubElement(element, key) + if value is not True: + child.text = value + + reply = exec_rpc(module, tostring(element), ignore_warning=False) + + result["xml"] = tostring(reply) + + if module.params["output"] == "text": + data = reply.find(".//output") + result["output"] = data.text.strip() + result["output_lines"] = result["output"].split("\n") + + elif module.params["output"] == "json": + result["output"] = module.from_json(reply.text.strip()) + + else: + result["output"] = tostring(reply).split("\n") + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_scp.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_scp.py new file mode 100644 index 000000000..34657ae8d --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_scp.py @@ -0,0 +1,172 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2018, 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: junos_scp +author: Christian Giese (@GIC-de) +short_description: Transfer files from or to remote devices running Junos +description: +- This module transfers files via SCP from or to remote devices running Junos. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + src: + description: + - The C(src) argument takes a single path, or a list of paths to be transferred. + The argument C(recursive) must be C(true) to transfer directories. + required: true + type: list + elements: path + dest: + description: + - The C(dest) argument specifies the path in which to receive the files. + type: path + default: . + recursive: + description: + - The C(recursive) argument enables recursive transfer of files and directories. + type: bool + default: no + remote_src: + description: + - The C(remote_src) argument enables the download of files (I(scp get)) from the + remote device. The default behavior is to upload files (I(scp put)) to the remote + device. + type: bool + default: no + ssh_private_key_file: + description: + - The C(ssh_private_key_file) argument is path to the SSH private key file. This + can be used if you need to provide a private key rather than loading the key + into the ssh-key-ring/environment + type: path + ssh_config: + description: + - The C(ssh_config) argument is path to the SSH configuration file. This can be + used to load SSH information from a configuration file. If this option is not + given by default ~/.ssh/config is queried. + type: path +requirements: +- junos-eznc +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vMX JUNOS version 17.3R1.10. +- Works with C(local) connections only. +- Since this module uses junos-eznc to establish connection with junos device the + netconf configuration parameters needs to be passed using module options for example + C(ssh_config) unlike other junos modules that uses C(netconf) connection type. +""" + +EXAMPLES = """ +# the required set of connection arguments have been purposely left off +# the examples for brevity +- name: upload local file to home directory on remote device + junipernetworks.junos.junos_scp: + src: test.tgz + +- name: upload local file to tmp directory on remote device + junipernetworks.junos.junos_scp: + src: test.tgz + dest: /tmp/ + +- name: download file from remote device + junipernetworks.junos.junos_scp: + src: test.tgz + remote_src: true + +- name: ssh config file path for jumphost config + junipernetworks.junos.junos_scp: + src: test.tgz + remote_src: true + ssh_config: /home/user/customsshconfig +""" + +RETURN = """ +changed: + description: always true + returned: always + type: bool +""" +from ansible.module_utils._text import to_native +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + get_device, +) + + +try: + from jnpr.junos.utils.scp import SCP + + HAS_PYEZ = True +except ImportError: + HAS_PYEZ = False + + +def transfer_files(module, device): + dest = module.params["dest"] + recursive = module.params["recursive"] + + with SCP(device) as scp: + for src in module.params["src"]: + if module.params["remote_src"]: + scp.get(src.strip(), local_path=dest, recursive=recursive) + else: + scp.put(src.strip(), remote_path=dest, recursive=recursive) + + +def main(): + """Main entry point for Ansible module execution""" + argument_spec = dict( + src=dict(type="list", required=True, elements="path"), + dest=dict(type="path", required=False, default="."), + recursive=dict(type="bool", default=False), + remote_src=dict(type="bool", default=False), + ssh_private_key_file=dict(type="path"), + ssh_config=dict(type="path"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + if not HAS_PYEZ: + module.fail_json( + msg="junos-eznc is required but does not appear to be installed. " + "It can be installed using `pip install junos-eznc`", + ) + + result = dict(changed=True) + + if not module.check_mode: + # open pyez connection and transfer files via SCP + try: + device = get_device(module) + transfer_files(module, device) + except Exception as ex: + module.fail_json(msg=to_native(ex)) + finally: + try: + # close pyez connection and ignore exceptions + device.close() + except Exception: + pass + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies.py new file mode 100644 index 000000000..bf8c4f6ee --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies.py @@ -0,0 +1,2707 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_security_policies +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_security_policies +version_added: 2.9.0 +short_description: Create and manage security policies on Juniper JUNOS devices +description: This module provides declarative creation and management of security policies on Juniper JUNOS devices +author: Pranav Bhatt (@pranav-bhatt) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed + - This module works with connection C(netconf) + - See L(the Junos OS Platform Options,https://docsansiblecom/ansible/latest/network/user_guide/platform_junoshtml) + - Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of security policies + type: dict + suboptions: + from_zones: + description: + - List of security zones from which the traffic originates from + type: list + elements: dict + suboptions: + name: + description: + - The name of the security zone from which the traffic originates from + type: str + to_zones: + description: + - List of destination security zones of the traffic + type: list + elements: dict + suboptions: + name: + description: + - The name of the destination security zone of the traffic + type: str + policies: &policy_attributes + description: + - List of policies defined for the associated category + type: list + elements: dict + suboptions: + name: + description: + - Name of the policy + type: str + description: + description: + - Description of the security policy + type: str + scheduler_name: + description: + - Name of the scheduler to run this policy + type: str + match: + description: Configure security policy match criteria + type: dict + suboptions: + application: + description: + - Specify the IP or remote procedure call (RPC) application or set of applications to be used as match criteria + type: dict + suboptions: + names: + description: Name of the predefined or custom application or application set used as match criteria + type: list + elements: str + any: + description: Match any predefined or custom applications or application sets + type: bool + dynamic_application: + description: + - Specify the dynamic applications or dynamic application groups used as match criteria within a security policy + type: dict + suboptions: + names: + description: Specify dynamic applications or dynamic application groups + type: list + elements: str + any: + description: + - Configuring the dynamic application as any installs the policy with the application as a wildcard (default) + type: bool + none: + description: + - Configuring the dynamic application as none ignores classification results from AppID and does not use the + dynamic application in security policy lookups + type: bool + destination_address: + description: + - Define the matching criteria You can specify one or more IP addresses, address sets, or wildcard addresses + type: dict + suboptions: + addresses: + description: + - IP address, IP address set, or address book entry, or wildcard address (represented as ABCD/wildcard_mask) + type: list + elements: str + any: + description: Any IPv4 or IPv6 address + type: bool + any_ipv4: + description: Any IPv4 address + type: bool + any_ipv6: + description: Any IPv6 address + type: bool + destination_address_excluded: + description: + - Exclude destination addresses + type: bool + source_address: + description: + - Define the matching criteria You can specify one or more IP addresses, address sets, or wildcard addresses + type: dict + suboptions: + addresses: + description: + - IP address, IP address set, or address book entry, or wildcard address (represented as ABCD/wildcard_mask) + type: list + elements: str + any: + description: Any IPv4 or IPv6 address + type: bool + any_ipv4: + description: Any IPv4 address + type: bool + any_ipv6: + description: Any IPv6 address + type: bool + source_address_excluded: + description: + - Exclude source addresses + type: bool + source_identity: + description: + - Identifies users and roles to be used as match criteria for a policy + type: dict + suboptions: + names: + description: A list of specific users and roles + type: list + elements: str + any: + description: + - Any user or role, as well as the keywords authenticated_user, unauthenticated_user, + and unknown_user + type: bool + authenticated_user: + description: All users and roles that have been authenticated + type: bool + unauthenticated_user: + description: + - Any user or role that does not have an IP_address mapped to authentication sources and the authentication source + is up and running + type: bool + unknown_user: + description: + - Any user or role that does not have an IP address mapped to authentication sources, because the + authentication source is disconnected from the SRX Series device + type: bool + source_end_user_profile: + description: Source end user profile name + type: str + url_category: + description: URL category + type: dict + suboptions: + names: + description: + - Names of url category to match + type: list + elements: str + any: + description: + - Apply to any url category + type: bool + none: + description: + - Do not apply to the url category + type: bool + from_zone: + description: Identify a single source zone or multiple source zones to be used as a match criteria for a policy + type: dict + suboptions: + names: + description: Name of single or multiple source zone + type: list + elements: str + any: + description: Match any zone + type: bool + junos_host: + description: + - junos-host + type: bool + to_zone: + description: Identify a single destination zone or multiple destination zones to be used as a match criteria for a policy + type: dict + suboptions: + names: + description: Name of single or multiple destination zone + type: list + elements: str + any: + description: Match any zone + type: bool + junos_host: + description: + - junos-host + type: bool + then: + description: Specify the policy action to be performed when packets match the defined criteria + type: dict + suboptions: + count: + description: + - Enable a count, in bytes or kilobytes, of all network traffic the policy allows to pass through the device + in both directions; the originating traffic from the client to the server (from the from_zone to the to_zone), + and the return traffic from the server to the originating client + type: bool + deny: + description: Block the service at the firewall The device drops the packets + type: bool + reject: + description: + - Block the service at the firewall The device drops the packet and sends a TCP reset (RST) segment to the + source host for TCP traffic and an ICMP "destination unreachable, port unreachable" + message (type 3, code 3) for UDP traffic + type: dict + suboptions: + enable: + description: + - Enable rejection of packets based on match criteria + type: bool + profile: + description: + - You can chose to provide a notification to the clients or redirect client request to an informative + Web page when a policy blocks HTTP or HTTPS traffic with a deny or reject action + type: str + ssl_proxy: + description: + - You can apply a redirect SSL proxy profile when a policy blocks HTTPS traffic with a reject action + When you apply am SSL proxy profile, SSL proxy decrypts the traffic and application + identification functionality identifies the application + type: dict + suboptions: + enable: + description: + - Enable SSL proxy + type: bool + profile_name: + description: Name of SSL proxy profile + type: str + log: + description: + - Log traffic information for a specific policy Traffic information is logged when a + session begins (session_init) or closes (session_close) + type: dict + suboptions: + session_init: + description: + - Enable logging on session initialization time + type: bool + session_close: + description: + - Enable logging on session close time + type: bool + permit: + description: Block the service at the firewall The device drops the packets + type: dict + suboptions: + application_services: + description: + - Enable application services within a security policy + type: dict + suboptions: + advanced_anti_malware_policy: + description: + - Specify advanced_anti_malware policy name + type: str + application_firewalls: + description: + - Specify the rule sets configured as part of application firewall to be applied to the permitted traffic + type: list + elements: dict + suboptions: + rule_set: + description: + - name of rule set to use + type: str + application_traffic_control_rule_set: + description: + - Specify the rule set configured as part of AppQoS, application_aware quality of service, + to be applied to the permitted traffic + type: str + gprs_gtp_profile: + description: + - Specify GPRS tunneling protocol profile name + type: str + gprs_sctp_profile: + description: + - Specify GPRS stream control protocol profile name + type: str + icap_redirect: + description: + - Specify icap redirect profile name + type: str + idp: + description: + - Intrusion Detection and Prevention (IDP) + type: bool + idp_policy: + description: + - Specify IDP policy name + type: str + packet_capture: + description: + - Option to enable or disable packet capture + type: bool + redirect_wx: + description: + - Specify the WX redirection needed for the packets that arrive from the LAN + type: bool + reverse_redirect_wx: + description: + - Specify the WX redirection needed for the reverse flow of the packets that arrive from the WAN + type: bool + security_intelligence_policy: + description: + - Specify security_intelligence policy name + type: str + security_intelligence: + description: Specify the security intelligence feed post action + type: dict + suboptions: + add_destination_identity_to_feed: + description: Add destination user identity to the security feed + type: str + add_destination_ip_to_feed: + description: Add the destination IP address to the security feed + type: str + add_source_ip_to_feed: + description: Add the source IP address to the security feed + type: str + add_source_identity_to_feed: + description: Add source user identity to the security feed + type: str + ssl_proxy: + description: You can apply a redirect SSL proxy profile when a policy blocks HTTPS traffic with a reject action + type: dict + suboptions: + enable: + description: + - Enable SSL proxy + type: bool + profile_name: + description: Name of SSL proxy profile + type: str + uac_policy: + description: + - Enable Unified Access Control (UAC) for the security policy + type: dict + suboptions: + enable: + description: + - Enable Unified Access Control (UAC) + type: bool + captive_portal: + description: + - Specify the preconfigured security policy for captive portal on the Junos OS Enforcer to enable the + captive portal feature + type: str + utm_policy: + description: + - Specify UTM policy name + type: str + destination_address: + description: + - Specify whether the traffic permitted by the security policy is limited to packets + where the destination IP address has been translated by means of a destination NAT rule + or to packets where the destination IP address has not been translated + choices: + - drop-translated + - drop-untranslated + type: str + firewall_authentication: + description: Configure firewall authentication methods + type: dict + suboptions: + pass_through: + description: + - Configure pass-through firewall user authentication + type: dict + suboptions: + access_profile: + description: + - Specify the name of the access profile + type: str + client_match: + description: + - Specify the name of the users or user groups in a profile who are allowed access by this policy + type: str + ssl_termination_profile: + description: + - Specify the SSL termination profile used for SSL offloading + type: str + web_redirect: + description: + - Enable redirecting an HTTP request to the device and redirecting the client system to a + webpage for authentication + type: bool + web_redirect_to_https: + description: + - Redirect unauthenticated HTTP requests to the internal HTTPS Web server of the device + type: bool + auth_only_browser: + description: + - Configure firewall authentication to ignore non-browser HTTP/HTTPS traffic + type: bool + auth_user_agent: + description: + - Specify a user-agent value to be used to verify that the user's browser traffic is HTTP/HTTPS traffic + type: str + push_to_identity_management: + description: enables pushing to identity management devices + type: bool + user_firewall: + description: + - Configure user role firewall authentication, and map the source IP address to the username and its + associated roles (groups) + type: dict + suboptions: + access_profile: + description: + - Specify the name of the access profile to be used for authentication + type: str + domain: + description: + - Specify the name of the domain where firewall authentication occurs in the event that the + Windows Management Instrumentation client (WMIC) is not available to get + IP_to_user mapping for the integrated user firewall feature + type: str + ssl_termination_profile: + description: + - For HTTPS traffic, specify the name of the SSL termination profile used for SSL offloading + type: str + web_redirect: + description: + - Enable webpage redirection + type: bool + web_redirect_to_https: + description: + - Enable redirection to HTTPS + type: bool + auth_only_browser: + description: + - Configure firewall authentication to ignore non-browser HTTP/HTTPS traffic + type: bool + auth_user_agent: + description: + - Specify a user-agent value to be used to verify that the user's browser traffic is HTTP/HTTPS traffic + type: str + web_authentication: + description: + - Specify that the policy allows access to users or user groups who have previously been authenticated by + Web authentication + type: list + elements: str + tcp_options: + description: + - Specify the TCP options for each policy You can configure sync and sequence checks for each policy + based on your requirements, and, because each policy has two directions, you can configure a + TCP MSS value for both directions or for just one direction + type: dict + suboptions: + initial_tcp_mss: + description: + - Configure the TCP maximum segment size (MSS) for packets that arrive at the + ingress interface (initial direction), match a specific policy, and for which a session is created + type: int + reverse_tcp_mss: + description: + - Configure the TCP maximum segment size (MSS) for packets that match a specific policy and travel in the + reverse direction of a session + type: int + sequence_check_required: + description: + - Enable sequence check per policy The sequence_check_required value overrides the global value no_sequence_check + type: bool + syn_check_required: + description: + - Enable sync check per policy The syn_check_required value overrides the global value no_syn_check + type: bool + window_scale: + description: + - Enable window_scale per policy + type: bool + tunnel: + description: Encapsulate outgoing IP packets and decapsulate incoming IP packets + type: dict + suboptions: + ipsec_vpn: + description: + - name of the ipsec policy + type: str + pair_policy: + description: + - name of the pair policy + type: str + global: + description: + - List of global security policies + type: dict + suboptions: + policies: *policy_attributes + running_config: + description: + - This option is used only with state I(parsed) + - The value of this option should be the output received from the JunOS device + by executing the command B(show configuration security policies) + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result For state I(rendered) active connection to remote host is + not required + - The state I(replaced) will replace the running configuration with the provided + configuration + - The state I(replaced) and state I(overridden) have the same behaviour + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result The value of C(running_config) + option should be the same format as the output of command + I(show security policies detail) executed on device For state I(parsed) active + connection to remote host is not required + type: str +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# Global policies: +# Policy: test_glob, State: enabled, Index: 4, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any-ipv4 +# Destination addresses: any-ipv4 +# Applications: any +# Action: deny +# +# vagrant@vsrx> show security zones +# +# Security zone: one +# Send reset for non-SYN session TCP packets: Off +# Policy configurable: Yes +# Interfaces bound: 1 +# Interfaces: +# ge-0/0/0.0 +# +# Security zone: three +# Send reset for non-SYN session TCP packets: Off +# Policy configurable: Yes +# Interfaces bound: 1 +# Interfaces: +# ge-0/0/2.0 +# +# Security zone: two +# Send reset for non-SYN session TCP packets: Off +# Policy configurable: Yes +# Interfaces bound: 1 +# Interfaces: +# ge-0/0/1.0 +# +# Security zone: junos-host +# Send reset for non-SYN session TCP packets: Off +# Policy configurable: Yes +# Interfaces bound: 0 +# Interfaces: +# +- junipernetworks.junos.junos_security_policies: + config: + from_zones: + - name: one + to_zones: + - name: two + policies: + - match: + application: + names: + - junos-dhcp-relay + - junos-finger + destination_address: + addresses: + - a2 + - a4 + destination_address_excluded: true + dynamic_application: + names: + - any + source_address: + addresses: + - a1 + - a3 + source_address_excluded: true + source_end_user_profile: test_end_user_profile + source_identity: + unknown_user: true + url_category: + names: + - Enhanced_Web_Chat + name: test_policy_1 + then: + count: true + deny: true + log: session-close + - match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + addresses: + - a1 + name: test_policy_2 + then: + reject: + enable: true + profile: test_dyn_app + ssl_proxy: + enable: true + profile_name: SECURITY-SSL-PROXY + - name: three + policies: + - match: + application: + any: true + destination_address: + addresses: + - a2 + source_address: + addresses: + - a1 + name: test_policy_3 + then: + permit: + application_services: + application_traffic_control_rule_set: test_traffic_control + gprs_gtp_profile: gtp1 + icap_redirect: test_icap + reverse_redirect_wx: 'True' + uac_policy: + enable: true + firewall_authentication: + push_to_identity_management: true + web_authentication: + - FWClient1 + tcp_options: + initial_tcp_mss: 64 + window_scale: true + global: + policies: + - match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + any_ipv6: true + name: test_glob_1 + then: + deny: true + - match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + any_ipv6: true + name: test_glob_2 + then: + deny: true + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "from_zones": [ +# { +# "name": "one", +# "to_zones": [ +# { +# "name": "two", +# "policies": [ +# { +# "match": { +# "application": { +# "names": [ +# "junos-dhcp-relay", +# "junos-finger" +# ] +# }, +# "destination_address": { +# "addresses": [ +# "a2", +# "a4" +# ] +# }, +# "destination_address_excluded": true, +# "dynamic_application": { +# "names": [ +# "any" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1", +# "a3" +# ] +# }, +# "source_address_excluded": true, +# "source_end_user_profile": "test_end_user_profile", +# "source_identity": { +# "unknown_user": true +# }, +# "url_category": { +# "names": [ +# "Enhanced_Web_Chat" +# ] +# } +# }, +# "name": "test_policy_1", +# "then": { +# "count": true, +# "deny": true, +# "log": "session-close" +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_2", +# "then": { +# "reject": { +# "enable": true, +# "profile": "test_dyn_app", +# "ssl_proxy": { +# "enable": true, +# "profile_name": "SECURITY-SSL-PROXY" +# } +# } +# } +# } +# ] +# }, +# { +# "name": "three", +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "addresses": [ +# "a2" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_3", +# "then": { +# "permit": { +# "application_services": { +# "application_traffic_control_rule_set": "test_traffic_control", +# "gprs_gtp_profile": "gtp1", +# "icap_redirect": "test_icap", +# "reverse_redirect_wx": "True", +# "uac_policy": { +# "enable": true +# } +# }, +# "firewall_authentication": { +# "push_to_identity_management": true, +# "web_authentication": [ +# "FWClient1" +# ] +# }, +# "tcp_options": { +# "initial_tcp_mss": 64, +# "window_scale": true +# } +# } +# } +# } +# ] +# } +# ] +# } +# ], +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv4": true +# }, +# "source_address": { +# "any_ipv4": true +# } +# }, +# "name": "test_glob", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_1", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_2", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "before": { +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv4": true +# }, +# "source_address": { +# "any_ipv4": true +# } +# }, +# "name": "test_glob", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "changed": true, +# "commands": "<nc:security +# xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies> +# <nc:policy> +# <nc:from-zone-name>one</nc:from-zone-name> +# <nc:to-zone-name>two</nc:to-zone-name> +# <nc:policy> +# <nc:name>test_policy_1</nc:name> +# <nc:match> +# <nc:source-address>a1</nc:source-address> +# <nc:source-address>a3</nc:source-address> +# <nc:source-address-excluded/> +# <nc:destination-address>a2</nc:destination-address> +# <nc:destination-address>a4</nc:destination-address> +# <nc:destination-address-excluded/> +# <nc:application>junos-dhcp-relay</nc:application> +# <nc:application>junos-finger</nc:application> +# <nc:source-end-user-profile>test_end_user_profile</nc:source-end-user-profile> +# <nc:source-identity>unknown-user</nc:source-identity> +# <nc:url-category>Enhanced_Web_Chat</nc:url-category> +# <nc:dynamic-application>any</nc:dynamic-application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# <nc:count></nc:count> +# <nc:log> +# <nc:session-close/> +# </nc:log> +# </nc:then> +# </nc:policy> +# <nc:policy> +# <nc:name>test_policy_2</nc:name> +# <nc:match> +# <nc:source-address>a1</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:reject> +# <nc:profile>test_dyn_app</nc:profile> +# <nc:ssl-proxy> +# <nc:profile-name>SECURITY-SSL-PROXY</nc:profile-name> +# </nc:ssl-proxy> +# </nc:reject> +# </nc:then> +# </nc:policy> +# </nc:policy> +# <nc:policy> +# <nc:from-zone-name>one</nc:from-zone-name> +# <nc:to-zone-name>three</nc:to-zone-name> +# <nc:policy> +# <nc:name>test_policy_3</nc:name> +# <nc:match> +# <nc:source-address>a1</nc:source-address> +# <nc:destination-address>a2</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:permit> +# <nc:application-services> +# <nc:application-traffic-control> +# <nc:rule-set>test_traffic_control</nc:rule-set> +# </nc:application-traffic-control> +# <nc:gprs-gtp-profile>gtp1</nc:gprs-gtp-profile> +# <nc:icap-redirect>test_icap</nc:icap-redirect> +# <nc:reverse-redirect-wx/> +# <nc:uac-policy></nc:uac-policy> +# </nc:application-services> +# <nc:firewall-authentication> +# <nc:push-to-identity-management/> +# <nc:web-authentication> +# <nc:client-match>FWClient1</nc:client-match> +# </nc:web-authentication> +# </nc:firewall-authentication> +# <nc:tcp-options> +# <nc:initial-tcp-mss>64</nc:initial-tcp-mss> +# <nc:window-scale/> +# </nc:tcp-options> +# </nc:permit> +# </nc:then> +# </nc:policy> +# </nc:policy> +# <nc:global> +# <nc:policy> +# <nc:name>test_glob_1</nc:name> +# <nc:match> +# <nc:source-address>any-ipv6</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# </nc:then> +# </nc:policy> +# <nc:policy> +# <nc:name>test_glob_2</nc:name> +# <nc:match> +# <nc:source-address>any-ipv6</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# </nc:then> +# </nc:policy> +# </nc:global> +# </nc:policies> +# </nc:security> +# " +# After state +# ----------- +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# From zone: one, To zone: two +# Policy: test_policy_1, State: enabled, Index: 5, Scope Policy: 0, Sequence number: 1 +# Source addresses(excluded): a1, a3 +# Destination addresses(excluded): a2, a4 +# Source-end-user-profile: test_end_user_profile(1) +# Applications: junos-dhcp-relay, junos-finger +# Dynamic Applications: any +# Url-category: Enhanced_Web_Chat +# Source identities: unknown-user +# Action: deny, log, count +# Policy: test_policy_2, State: enabled, Index: 6, Scope Policy: 0, Sequence number: 2 +# Source addresses: a1 +# Destination addresses: any-ipv6 +# Applications: any +# Action: reject +# dynapp-redir-profile: test_dyn_app(1) +# From zone: one, To zone: three +# Policy: test_policy_3, State: enabled, Index: 7, Scope Policy: 0, Sequence number: 1 +# Source addresses: a1 +# Destination addresses: a2 +# Applications: any +# Action: permit, firewall authentication, application services, unified access control +# Application traffic control: test_traffic_control +# Global policies: +# Policy: test_glob, State: enabled, Index: 4, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any-ipv4 +# Destination addresses: any-ipv4 +# Applications: any +# Action: deny +# Policy: test_glob_1, State: enabled, Index: 8, Scope Policy: 0, Sequence number: 2 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# Policy: test_glob_2, State: enabled, Index: 9, Scope Policy: 0, Sequence number: 3 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny + + +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# From zone: one, To zone: two +# Policy: test_policy_1, State: enabled, Index: 5, Scope Policy: 0, Sequence number: 1 +# Source addresses(excluded): a1, a3 +# Destination addresses(excluded): a2, a4 +# Source-end-user-profile: test_end_user_profile(1) +# Applications: junos-dhcp-relay, junos-finger +# Dynamic Applications: any +# Url-category: Enhanced_Web_Chat +# Source identities: unknown-user +# Action: deny, log, count +# Policy: test_policy_2, State: enabled, Index: 6, Scope Policy: 0, Sequence number: 2 +# Source addresses: a1 +# Destination addresses: any-ipv6 +# Applications: any +# Action: reject +# dynapp-redir-profile: test_dyn_app(1) +# From zone: one, To zone: three +# Policy: test_policy_3, State: enabled, Index: 7, Scope Policy: 0, Sequence number: 1 +# Source addresses: a1 +# Destination addresses: a2 +# Applications: any +# Action: permit, firewall authentication, application services, unified access control +# Application traffic control: test_traffic_control +# Global policies: +# Policy: test_glob, State: enabled, Index: 4, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any-ipv4 +# Destination addresses: any-ipv4 +# Applications: any +# Action: deny +# Policy: test_glob_1, State: enabled, Index: 8, Scope Policy: 0, Sequence number: 2 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# Policy: test_glob_2, State: enabled, Index: 9, Scope Policy: 0, Sequence number: 3 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# +- junipernetworks.junos.junos_security_policies: + config: + global: + policies: + - description: test update + match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + any: true + name: test_glob_3 + then: + deny: true + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "global": { +# "policies": [ +# { +# "description": "test update", +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any": true +# } +# }, +# "name": "test_glob_3", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "before": { +# "from_zones": [ +# { +# "name": "one", +# "to_zones": [ +# { +# "name": "two", +# "policies": [ +# { +# "match": { +# "application": { +# "names": [ +# "junos-dhcp-relay", +# "junos-finger" +# ] +# }, +# "destination_address": { +# "addresses": [ +# "a2", +# "a4" +# ] +# }, +# "destination_address_excluded": true, +# "dynamic_application": { +# "names": [ +# "any" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1", +# "a3" +# ] +# }, +# "source_address_excluded": true, +# "source_end_user_profile": "test_end_user_profile", +# "source_identity": { +# "unknown_user": true +# }, +# "url_category": { +# "names": [ +# "Enhanced_Web_Chat" +# ] +# } +# }, +# "name": "test_policy_1", +# "then": { +# "count": true, +# "deny": true, +# "log": "session-close" +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_2", +# "then": { +# "reject": { +# "enable": true, +# "profile": "test_dyn_app", +# "ssl_proxy": { +# "enable": true, +# "profile_name": "SECURITY-SSL-PROXY" +# } +# } +# } +# } +# ] +# }, +# { +# "name": "three", +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "addresses": [ +# "a2" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_3", +# "then": { +# "permit": { +# "application_services": { +# "application_traffic_control_rule_set": "test_traffic_control", +# "gprs_gtp_profile": "gtp1", +# "icap_redirect": "test_icap", +# "reverse_redirect_wx": "True", +# "uac_policy": { +# "enable": true +# } +# }, +# "firewall_authentication": { +# "push_to_identity_management": true, +# "web_authentication": [ +# "FWClient1" +# ] +# }, +# "tcp_options": { +# "initial_tcp_mss": 64, +# "window_scale": true +# } +# } +# } +# } +# ] +# } +# ] +# } +# ], +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv4": true +# }, +# "source_address": { +# "any_ipv4": true +# } +# }, +# "name": "test_glob", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_1", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_2", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "changed": true, +# "commands": "<nc:security +# xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/> +# <nc:policies> +# <nc:global> +# <nc:policy> +# <nc:name>test_glob_3</nc:name> +# <nc:description>test update</nc:description> +# <nc:match> +# <nc:source-address>any</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# </nc:then> +# </nc:policy> +# </nc:global> +# </nc:policies> +# </nc:security>" +# } +# After state +# ----------- +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# Global policies: +# Policy: test_glob_3, State: enabled, Index: 10, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny + + +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# From zone: one, To zone: two +# Policy: test_policy_1, State: enabled, Index: 5, Scope Policy: 0, Sequence number: 1 +# Source addresses(excluded): a1, a3 +# Destination addresses(excluded): a2, a4 +# Source-end-user-profile: test_end_user_profile(1) +# Applications: junos-dhcp-relay, junos-finger +# Dynamic Applications: any +# Url-category: Enhanced_Web_Chat +# Source identities: unknown-user +# Action: deny, log, count +# Policy: test_policy_2, State: enabled, Index: 6, Scope Policy: 0, Sequence number: 2 +# Source addresses: a1 +# Destination addresses: any-ipv6 +# Applications: any +# Action: reject +# dynapp-redir-profile: test_dyn_app(1) +# From zone: one, To zone: three +# Policy: test_policy_3, State: enabled, Index: 7, Scope Policy: 0, Sequence number: 1 +# Source addresses: a1 +# Destination addresses: a2 +# Applications: any +# Action: permit, firewall authentication, application services, unified access control +# Application traffic control: test_traffic_control +# Global policies: +# Policy: test_glob, State: enabled, Index: 4, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any-ipv4 +# Destination addresses: any-ipv4 +# Applications: any +# Action: deny +# Policy: test_glob_1, State: enabled, Index: 8, Scope Policy: 0, Sequence number: 2 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# Policy: test_glob_2, State: enabled, Index: 9, Scope Policy: 0, Sequence number: 3 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# +- junipernetworks.junos.junos_security_policies: + config: + global: + policies: + - description: test update + match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + any: true + name: test_glob_3 + then: + deny: true + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "global": { +# "policies": [ +# { +# "description": "test update", +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any": true +# } +# }, +# "name": "test_glob_3", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "before": { +# "from_zones": [ +# { +# "name": "one", +# "to_zones": [ +# { +# "name": "two", +# "policies": [ +# { +# "match": { +# "application": { +# "names": [ +# "junos-dhcp-relay", +# "junos-finger" +# ] +# }, +# "destination_address": { +# "addresses": [ +# "a2", +# "a4" +# ] +# }, +# "destination_address_excluded": true, +# "dynamic_application": { +# "names": [ +# "any" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1", +# "a3" +# ] +# }, +# "source_address_excluded": true, +# "source_end_user_profile": "test_end_user_profile", +# "source_identity": { +# "unknown_user": true +# }, +# "url_category": { +# "names": [ +# "Enhanced_Web_Chat" +# ] +# } +# }, +# "name": "test_policy_1", +# "then": { +# "count": true, +# "deny": true, +# "log": "session-close" +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_2", +# "then": { +# "reject": { +# "enable": true, +# "profile": "test_dyn_app", +# "ssl_proxy": { +# "enable": true, +# "profile_name": "SECURITY-SSL-PROXY" +# } +# } +# } +# } +# ] +# }, +# { +# "name": "three", +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "addresses": [ +# "a2" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_3", +# "then": { +# "permit": { +# "application_services": { +# "application_traffic_control_rule_set": "test_traffic_control", +# "gprs_gtp_profile": "gtp1", +# "icap_redirect": "test_icap", +# "reverse_redirect_wx": "True", +# "uac_policy": { +# "enable": true +# } +# }, +# "firewall_authentication": { +# "push_to_identity_management": true, +# "web_authentication": [ +# "FWClient1" +# ] +# }, +# "tcp_options": { +# "initial_tcp_mss": 64, +# "window_scale": true +# } +# } +# } +# } +# ] +# } +# ] +# } +# ], +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv4": true +# }, +# "source_address": { +# "any_ipv4": true +# } +# }, +# "name": "test_glob", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_1", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_2", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "changed": true, +# "commands": "<nc:security +# xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/> +# <nc:policies> +# <nc:global> +# <nc:policy> +# <nc:name>test_glob_3</nc:name> +# <nc:description>test update</nc:description> +# <nc:match> +# <nc:source-address>any</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# </nc:then> +# </nc:policy> +# </nc:global> +# </nc:policies> +# </nc:security>" +# } +# After state +# ----------- +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# Global policies: +# Policy: test_glob_3, State: enabled, Index: 10, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny + + +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# Global policies: +# Policy: test_glob_3, State: enabled, Index: 10, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# +- junipernetworks.junos.junos_security_policies: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "after": {}, +# "before": { +# "global": { +# "policies": [ +# { +# "description": "test update", +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any": true +# } +# }, +# "name": "test_glob_3", +# "then": { +# "deny": true +# } +# } +# ] +# } +# }, +# "changed": true, +# "commands": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/></nc:security>" +# +# After state +# ----------- +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all + + +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply> +# <configuration> +# <version>18.4R1-S3.1</version> +# <services> +# <ssl> +# <termination> +# <profile> +# <name>test_ssl_term</name> +# <server-certificate>SECURITY-cert</server-certificate> +# </profile> +# </termination> +# <proxy> +# <profile> +# <name>SECURITY-SSL-PROXY</name> +# <root-ca>SECURITY-cert</root-ca> +# </profile> +# </proxy> +# </ssl> +# <icap-redirect> +# <profile> +# <name>test_icap</name> +# <server> +# <name>test_icap_server</name> +# <host>10.10.10.11</host> +# </server> +# </profile> +# </icap-redirect> +# <user-identification> +# <device-information> +# <end-user-profile> +# <profile-name> +# <name>test_end_user_profile</name> +# <domain-name>test_domain</domain-name> +# <attribute> +# <name>device-identity</name> +# <string>Windows</string> +# </attribute> +# </profile-name> +# </end-user-profile> +# </device-information> +# </user-identification> +# </services> +# <security> +# <address-book> +# <name>global</name> +# <address> +# <name>a1</name> +# <ip-prefix>200.0.113.0/24</ip-prefix> +# </address> +# <address> +# <name>a2</name> +# <ip-prefix>201.0.113.0/24</ip-prefix> +# </address> +# <address> +# <name>a3</name> +# <ip-prefix>202.0.113.0/24</ip-prefix> +# </address> +# <address> +# <name>a4</name> +# <ip-prefix>203.0.113.0/24</ip-prefix> +# </address> +# </address-book> +# <dynamic-application> +# <profile> +# <name>test_dyn_app</name> +# <redirect-message> +# <type> +# <custom-text> +# <content>hello_world</content> +# </custom-text> +# </type> +# </redirect-message> +# </profile> +# </dynamic-application> +# <policies> +# <policy> +# <from-zone-name>one</from-zone-name> +# <to-zone-name>two</to-zone-name> +# <policy> +# <name>test_policy_1</name> +# <match> +# <source-address>a1</source-address> +# <source-address>a3</source-address> +# <destination-address>a2</destination-address> +# <destination-address>a4</destination-address> +# <source-address-excluded /> +# <destination-address-excluded /> +# <application>junos-dhcp-relay</application> +# <application>junos-finger</application> +# <source-identity>authenticated-user</source-identity> +# <source-identity>unknown-user</source-identity> +# <source-end-user-profile> +# <source-end-user-profile-name>test_end_user_profile</source-end-user-profile-name> +# </source-end-user-profile> +# <dynamic-application>any</dynamic-application> +# <url-category>Enhanced_Web_Chat</url-category> +# </match> +# <then> +# <deny /> +# <log> +# <session-close /> +# </log> +# <count></count> +# </then> +# </policy> +# <policy> +# <name>test_policy_2</name> +# <match> +# <source-address>a1</source-address> +# <destination-address>any-ipv6</destination-address> +# <application>any</application> +# </match> +# <then> +# <reject> +# <profile>test_dyn_app</profile> +# <ssl-proxy> +# <profile-name>SECURITY-SSL-PROXY</profile-name> +# </ssl-proxy> +# </reject> +# </then> +# </policy> +# </policy> +# <policy> +# <from-zone-name>one</from-zone-name> +# <to-zone-name>three</to-zone-name> +# <policy> +# <name>test_policy_3</name> +# <match> +# <source-address>a1</source-address> +# <destination-address>a2</destination-address> +# <application>any</application> +# </match> +# <then> +# <permit> +# <firewall-authentication> +# <web-authentication> +# <client-match>FWClient1</client-match> +# </web-authentication> +# <push-to-identity-management /> +# </firewall-authentication> +# <destination-address> +# <drop-untranslated /> +# </destination-address> +# <application-services> +# <gprs-gtp-profile>gtp1</gprs-gtp-profile> +# <uac-policy></uac-policy> +# <icap-redirect>test_icap</icap-redirect> +# <application-traffic-control> +# <rule-set>test_traffic_control</rule-set> +# </application-traffic-control> +# <reverse-redirect-wx /> +# </application-services> +# <tcp-options> +# <initial-tcp-mss>64</initial-tcp-mss> +# <window-scale /> +# </tcp-options> +# </permit> +# </then> +# </policy> +# </policy> +# <global> +# <policy> +# <name>test_glob_1</name> +# <match> +# <source-address>any-ipv6</source-address> +# <destination-address>any-ipv6</destination-address> +# <application>any</application> +# </match> +# <then> +# <deny /> +# </then> +# </policy> +# <policy> +# <name>test_glob_2</name> +# <match> +# <source-address>any-ipv6</source-address> +# <destination-address>any-ipv6</destination-address> +# <application>any</application> +# </match> +# <then> +# <deny /> +# </then> +# </policy> +# </global> +# </policies> +# <zones> +# <security-zone> +# <name>one</name> +# <interfaces> +# <name>ge-0/0/0.0</name> +# </interfaces> +# </security-zone> +# <security-zone> +# <name>two</name> +# <interfaces> +# <name>ge-0/0/1.0</name> +# </interfaces> +# </security-zone> +# <security-zone> +# <name>three</name> +# <interfaces> +# <name>ge-0/0/2.0</name> +# </interfaces> +# </security-zone> +# </zones> +# <gprs> +# <gtp> +# <profile> +# <name>gtp1</name> +# </profile> +# </gtp> +# </gprs> +# </security> +# <interfaces> +# <interface> +# <name>ge-0/0/0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>200.0.113.1/24</name> +# </address> +# </inet> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-0/0/1</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>201.0.113.1/24</name> +# </address> +# </inet> +# </family> +# </unit> +# </interface> +# <interface> +# <name>ge-0/0/2</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <address> +# <name>202.0.113.1/24</name> +# </address> +# </inet> +# </family> +# </unit> +# </interface> +# <interface> +# <name>fxp0</name> +# <unit> +# <name>0</name> +# <family> +# <inet> +# <dhcp></dhcp> +# </inet> +# </family> +# </unit> +# </interface> +# </interfaces> +# <class-of-service> +# <application-traffic-control> +# <rule-sets> +# <name>test_traffic_control</name> +# <rule> +# <name>test_rule</name> +# <match> +# <application-known /> +# </match> +# <then> +# <log /> +# </then> +# </rule> +# </rule-sets> +# </application-traffic-control> +# </class-of-service> +# <access> +# <profile> +# <name>WEBAUTH</name> +# <client> +# <name>FWClient1</name> +# <firewall-user> +# <password>$9$kq5Ftu1cSe</password> +# </firewall-user> +# </client> +# </profile> +# <firewall-authentication> +# <web-authentication> +# <default-profile>WEBAUTH</default-profile> +# </web-authentication> +# </firewall-authentication> +# </access> +# </configuration> +# <database-status-information></database-status-information> +# </rpc-reply> +# +- name: Parse NTP global running config + junipernetworks.junos.junos_security_policies: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "parsed": { +# "from_zones": [ +# { +# "name": "one", +# "to_zones": [ +# { +# "name": "two", +# "policies": [ +# { +# "match": { +# "application": { +# "names": [ +# "junos-dhcp-relay", +# "junos-finger" +# ] +# }, +# "destination_address": { +# "addresses": [ +# "a2", +# "a4" +# ] +# }, +# "destination_address_excluded": true, +# "dynamic_application": { +# "names": [ +# "any" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1", +# "a3" +# ] +# }, +# "source_address_excluded": true, +# "source_end_user_profile": "test_end_user_profile", +# "source_identity": { +# "unknown_user": true +# }, +# "url_category": { +# "names": [ +# "Enhanced_Web_Chat" +# ] +# } +# }, +# "name": "test_policy_1", +# "then": { +# "count": true, +# "deny": true, +# "log": "session-close" +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_2", +# "then": { +# "reject": { +# "enable": true, +# "profile": "test_dyn_app", +# "ssl_proxy": { +# "enable": true, +# "profile_name": "SECURITY-SSL-PROXY" +# } +# } +# } +# } +# ] +# }, +# { +# "name": "three", +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "addresses": [ +# "a2" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_3", +# "then": { +# "permit": { +# "application_services": { +# "application_traffic_control_rule_set": "test_traffic_control", +# "gprs_gtp_profile": "gtp1", +# "icap_redirect": "test_icap", +# "reverse_redirect_wx": "True", +# "uac_policy": { +# "enable": true +# } +# }, +# "firewall_authentication": { +# "push_to_identity_management": true, +# "web_authentication": [ +# "FWClient1" +# ] +# }, +# "tcp_options": { +# "initial_tcp_mss": 64, +# "window_scale": true +# } +# } +# } +# } +# ] +# } +# ] +# } +# ], +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_1", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_2", +# "then": { +# "deny": true +# } +# } +# ] +# } +# } + + +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx> show security policies +# Default policy: deny-all +# Pre ID default policy: permit-all +# From zone: one, To zone: two +# Policy: test_policy_1, State: enabled, Index: 4, Scope Policy: 0, Sequence number: 1 +# Source addresses(excluded): a1, a3 +# Destination addresses(excluded): a2, a4 +# Source-end-user-profile: test_end_user_profile(1) +# Applications: junos-dhcp-relay, junos-finger +# Dynamic Applications: any +# Url-category: Enhanced_Web_Chat +# Source identities: authenticated-user, unknown-user +# Action: deny, log, count +# Policy: test_policy_2, State: enabled, Index: 5, Scope Policy: 0, Sequence number: 2 +# Source addresses: a1 +# Destination addresses: any-ipv6 +# Applications: any +# Action: reject +# dynapp-redir-profile: test_dyn_app(1) +# From zone: one, To zone: three +# Policy: test_policy_3, State: enabled, Index: 6, Scope Policy: 0, Sequence number: 1 +# Source addresses: a1 +# Destination addresses: a2 +# Applications: any +# Action: permit, drop-untranslated, firewall authentication, application services, unified access control +# Application traffic control: test_traffic_control +# Global policies: +# Policy: test_glob_1, State: enabled, Index: 7, Scope Policy: 0, Sequence number: 1 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# Policy: test_glob_2, State: enabled, Index: 8, Scope Policy: 0, Sequence number: 2 +# From zones: any +# To zones: any +# Source addresses: any-ipv6 +# Destination addresses: any-ipv6 +# Applications: any +# Action: deny +# +- junipernetworks.junos.junos_security_policies: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "changed": false, +# "gathered": { +# "from_zones": [ +# { +# "name": "one", +# "to_zones": [ +# { +# "name": "two", +# "policies": [ +# { +# "match": { +# "application": { +# "names": [ +# "junos-dhcp-relay", +# "junos-finger" +# ] +# }, +# "destination_address": { +# "addresses": [ +# "a2", +# "a4" +# ] +# }, +# "destination_address_excluded": true, +# "dynamic_application": { +# "names": [ +# "any" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1", +# "a3" +# ] +# }, +# "source_address_excluded": true, +# "source_end_user_profile": "test_end_user_profile", +# "source_identity": { +# "unknown_user": true +# }, +# "url_category": { +# "names": [ +# "Enhanced_Web_Chat" +# ] +# } +# }, +# "name": "test_policy_1", +# "then": { +# "count": true, +# "deny": true, +# "log": "session-close" +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_2", +# "then": { +# "reject": { +# "enable": true, +# "profile": "test_dyn_app", +# "ssl_proxy": { +# "enable": true, +# "profile_name": "SECURITY-SSL-PROXY" +# } +# } +# } +# } +# ] +# }, +# { +# "name": "three", +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "addresses": [ +# "a2" +# ] +# }, +# "source_address": { +# "addresses": [ +# "a1" +# ] +# } +# }, +# "name": "test_policy_3", +# "then": { +# "permit": { +# "application_services": { +# "application_traffic_control_rule_set": "test_traffic_control", +# "gprs_gtp_profile": "gtp1", +# "icap_redirect": "test_icap", +# "reverse_redirect_wx": "True", +# "uac_policy": { +# "enable": true +# } +# }, +# "firewall_authentication": { +# "push_to_identity_management": true, +# "web_authentication": [ +# "FWClient1" +# ] +# }, +# "tcp_options": { +# "initial_tcp_mss": 64, +# "window_scale": true +# } +# } +# } +# } +# ] +# } +# ] +# } +# ], +# "global": { +# "policies": [ +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_1", +# "then": { +# "deny": true +# } +# }, +# { +# "match": { +# "application": { +# "any": true +# }, +# "destination_address": { +# "any_ipv6": true +# }, +# "source_address": { +# "any_ipv6": true +# } +# }, +# "name": "test_glob_2", +# "then": { +# "deny": true +# } +# } +# ] +# } +# } +# } + + +# Using rendered +# +# Before state +# ------------ +# +- junipernetworks.junos.junos_security_policies: + config: + global: + policies: + - description: test update + match: + application: + any: true + destination_address: + any_ipv6: true + source_address: + any: true + name: test_glob_3 + then: + deny: true + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": "<nc:security +# xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies> +# <nc:global> +# <nc:policy> +# <nc:name>test_glob_3</nc:name> +# <nc:description>test update</nc:description> +# <nc:match> +# <nc:source-address>any</nc:source-address> +# <nc:destination-address>any-ipv6</nc:destination-address> +# <nc:application>any</nc:application> +# </nc:match> +# <nc:then> +# <nc:deny/> +# </nc:then> +# </nc:policy> +# </nc:global> +# </nc:policies> +# </nc:security>" + + +""" +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when state is I(merged), I(replaced), I(overridden) or I(deleted) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when state is I(merged), I(replaced), I(overridden) or I(deleted) + type: list + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <global> + <policy> + <name>test_glob_1</name> + <match> + <source-address>any-ipv6</source-address> + <destination-address>any-ipv6</destination-address> + <application>any</application> + </match> + <then> + <deny /> + </then> + </policy> + </global> + </policies> + </security> + </configuration> + </rpc-reply>" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when state is I(rendered) + type: dict + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <global> + <policy> + <name>test_glob_1</name> + <match> + <source-address>any-ipv6</source-address> + <destination-address>any-ipv6</destination-address> + <application>any</application> + </match> + <then> + <deny /> + </then> + </policy> + </global> + </policies> + </security> + </configuration> + </rpc-reply>" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when state is I(gathered) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when state is I(parsed) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_policies.security_policies import ( + Security_policiesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.security_policies.security_policies import ( + Security_policies, +) + + +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=Security_policiesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Security_policies(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies_global.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies_global.py new file mode 100644 index 000000000..19cbe091a --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_policies_global.py @@ -0,0 +1,994 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_security_policies_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_security_policies_global +version_added: 2.9.0 +short_description: Manage global security policy settings on Juniper JUNOS devices +description: This module provides declarative management of global security policy settings on Juniper JUNOS devices +author: Pranav Bhatt (@pranav-bhatt) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). +- See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of security policies + type: dict + suboptions: + default_policy: + description: + - Configure the default security policy that defines the actions the device takes on a packet that does not match any user-defined policy. + choices: + - deny-all + - permit-all + type: str + policy_rematch: + description: + - Enable the device to reevaluate an active session when its associated security policy is modified. The session remains open if it still matches + the policy that allowed the session initially. + type: dict + suboptions: + enable: + description: + - Enable the device to reevaluate an active session when its associated security policy is modified. + The session remains open if it still matches the policy that allowed the session initially. + type: bool + extensive: + description: + - When a policy is modified or deleted, extensive option checks if any suitable policy permit to keep these sessions alive. + type: bool + policy_stats: + description: + - Configure policies statistics. + type: dict + suboptions: + enable: + description: + - Enable policies statistics. + type: bool + system_wide: + description: + - Configure systemwide policies statistics. + type: bool + pre_id_default_policy_action: + description: + - Configures default policy actions that occur prior to dynamic application identification (AppID) when the packet matches the criteria. + type: dict + suboptions: + log: + description: + - Specifies the log details at session close time and session initialization time. + type: dict + suboptions: + session_init: + description: + - Enable logging on session initialization time + type: bool + session_close: + description: + - Enable logging on session close time + type: bool + session_timeout: + description: + - When you update a session, the session timeout is configured, which specifies the session timeout details in seconds. + type: dict + suboptions: + icmp: + description: + - Timeout value for ICMP sessions (seconds) + type: int + icmp6: + description: + - Timeout value for ICMP6 sessions (seconds) + type: int + ospf: + description: + - Timeout value for OSPF sessions (seconds) + type: int + others: + description: + - Timeout value for other sessions (seconds) + type: int + tcp: + description: + - Timeout value for TCP sessions (seconds) + type: int + udp: + description: + - Timeout value for UDP sessions (seconds) + type: int + traceoptions: + description: A dictionary of security policies + type: dict + suboptions: + file: + description: A dictionary to configure the trace file options + type: dict + suboptions: + files: + description: + - Maximum number of trace files + type: int + match: + description: Refine the output to include lines that contain the regular expression. + type: str + size: + description: The maximum tracefile size + type: str + world_readable: + description: The world_readable option enables any user to read the file. + type: bool + no_world_readable: + description: Log files can be accessed only by the user who configures the tracing operation. + type: bool + flag: + description: + - Trace operation to perform. + choices: + - all + - configuration + - compilation + - ipc + - lookup + - routing-socket + - rules + type: str + no_remote_trace: + description: Disable remote tracing. + 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 JunOS device + by executing the command B(show security policies). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + behaviour for this module. + - The state I(replaced) will replace the running configuration with the provided + configuration + - The state I(replaced) and state I(overridden) have the same behaviour + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show security policies detail) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show security policies +# default-policy { +# permit-all; +# } +# +- name: Update the running configuration with provided configuration + junipernetworks.junos.junos_security_policies_global: + config: + policy_rematch: + enable: true + policy_stats: + enable: true + pre_id_default_policy_action: + log: + session_init: true + session_timeout: + icmp: 10 + others: 10 + traceoptions: + file: + files: 4 + match: /[A-Z]*/gm + size: 10k + no_world_readable: true + flag: all + no_remote_trace: true + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "default_policy": "permit-all", +# "policy_rematch": { +# "enable": true, +# "extensive": true +# }, +# "policy_stats": { +# "enable": true, +# "system_wide": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 3, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "before": {}, +# "changed": true, +# "commands": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><nc:policies> +# <nc:policy-rematch> <nc:extensive/></nc:policy-rematch><nc:policy-stats> +# <nc:system-wide>enable</nc:system-wide></nc:policy-stats><nc:pre-id-default-policy> +# <nc:then><nc:log><nc:session-init/></nc:log><nc:session-timeout><nc:icmp>10</nc:icmp> +# <nc:others>10</nc:others></nc:session-timeout></nc:then></nc:pre-id-default-policy> +# <nc:traceoptions><nc:file><nc:files>3</nc:files><nc:match>/[A-Z]*/gm</nc:match> +# <nc:size>10k</nc:size><nc:no-world-readable/></nc:file><nc:flag><nc:name>all +# </nc:name></nc:flag><nc:no-remote-trace/></nc:traceoptions></nc:policies></nc:security>" +# After state +# ----------- +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# permit-all; +# } +# policy-rematch extensive; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } +# +# +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# permit-all; +# } +# policy-rematch extensive; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } + +- name: Replace the running configuration with provided configuration + junipernetworks.junos.junos_security_policies_global: + config: + default_policy: deny-all + policy_rematch: + enable: true + policy_stats: + enable: true + pre_id_default_policy_action: + log: + session_init: true + session_timeout: + icmp: 10 + others: 10 + traceoptions: + file: + files: 4 + match: /[A-Z]*/gm + size: 10k + no_world_readable: true + flag: all + no_remote_trace: true + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "default_policy": "deny-all", +# "policy_rematch": { +# "enable": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "before": { +# "default_policy": "permit-all", +# "policy_rematch": { +# "enable": true, +# "extensive": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "changed": true, +# "commands": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/><nc:policies><nc:default-policy><nc:deny-all/></nc:default-policy> +# <nc:policy-rematch> </nc:policy-rematch><nc:policy-stats> </nc:policy-stats><nc:pre-id-default-policy> +# <nc:then><nc:log><nc:session-init/></nc:log><nc:session-timeout><nc:icmp>10</nc:icmp><nc:others>10 +# </nc:others></nc:session-timeout></nc:then></nc:pre-id-default-policy><nc:traceoptions><nc:file> +# <nc:files>4</nc:files><nc:match>/[A-Z]*/gm</nc:match><nc:size>10k</nc:size><nc:no-world-readable/> +# </nc:file><nc:flag><nc:name>all</nc:name></nc:flag><nc:no-remote-trace/></nc:traceoptions></nc:policies> +# </nc:security>" +# +# After state +# ----------- +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# deny-all; +# } +# policy-rematch; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } + +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# permit-all; +# } +# policy-rematch extensive; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } + +- name: Replace the running configuration with provided configuration + junipernetworks.junos.junos_security_policies_global: + config: + default_policy: deny-all + policy_rematch: + enable: true + policy_stats: + enable: true + pre_id_default_policy_action: + log: + session_init: true + session_timeout: + icmp: 10 + others: 10 + traceoptions: + file: + files: 4 + match: /[A-Z]*/gm + size: 10k + no_world_readable: true + flag: all + no_remote_trace: true + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "default_policy": "deny-all", +# "policy_rematch": { +# "enable": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "before": { +# "default_policy": "permit-all", +# "policy_rematch": { +# "enable": true, +# "extensive": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "changed": true, +# "commands": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/><nc:policies><nc:default-policy><nc:deny-all/></nc:default-policy> +# <nc:policy-rematch> </nc:policy-rematch><nc:policy-stats> </nc:policy-stats><nc:pre-id-default-policy> +# <nc:then><nc:log><nc:session-init/></nc:log><nc:session-timeout><nc:icmp>10</nc:icmp><nc:others>10 +# </nc:others></nc:session-timeout></nc:then></nc:pre-id-default-policy><nc:traceoptions><nc:file> +# <nc:files>4</nc:files><nc:match>/[A-Z]*/gm</nc:match><nc:size>10k</nc:size><nc:no-world-readable/> +# </nc:file><nc:flag><nc:name>all</nc:name></nc:flag><nc:no-remote-trace/></nc:traceoptions></nc:policies> +# </nc:security>" +# +# After state +# ----------- +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# deny-all; +# } +# policy-rematch; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# deny-all; +# } +# policy-rematch; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } +# +- name: Delete the running configuration + junipernetworks.junos.junos_security_policies_global: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "default_policy": "deny-all", +# "policy_rematch": { +# "enable": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# }, +# "changed": true, +# "commands": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# <nc:policies delete="delete"/></nc:security>" +# +# After state +# ----------- +# +# vagrant@vsrx# show security policies +# +# +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show security policies +# traceoptions { +# no-remote-trace; +# file size 10k files 4 no-world-readable match "/[A-Z]*/gm"; +# flag all; +# } +# default-policy { +# deny-all; +# } +# policy-rematch; +# policy-stats; +# pre-id-default-policy { +# then { +# log { +# session-init; +# } +# session-timeout { +# icmp 10; +# others 10; +# } +# } +# } +# +- name: Gather the running configuration + junipernetworks.junos.junos_security_policies_global: + config: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "default_policy": "deny-all", +# "policy_rematch": { +# "enable": true +# }, +# "policy_stats": { +# "enable": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 4, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# } +# +# Using rendered +# +# Before state +# ------------ +# +- name: Render the provided configuration + junipernetworks.junos.junos_security_policies_global: + config: + default_policy: deny-all + policy_rematch: + enable: true + policy_stats: + enable: true + pre_id_default_policy_action: + log: + session_init: true + session_timeout: + icmp: 10 + others: 10 + traceoptions: + file: + files: 4 + match: /[A-Z]*/gm + size: 10k + no_world_readable: true + flag: all + no_remote_trace: true + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": "<nc:security xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><nc:policies> +# <nc:default-policy><nc:deny-all/></nc:default-policy><nc:policy-rematch> </nc:policy-rematch> +# <nc:policy-stats> </nc:policy-stats><nc:pre-id-default-policy><nc:then><nc:log><nc:session-init/> +# </nc:log><nc:session-timeout><nc:icmp>10</nc:icmp><nc:others>10</nc:others></nc:session-timeout> +# </nc:then></nc:pre-id-default-policy><nc:traceoptions><nc:file><nc:files>4</nc:files> +# <nc:match>/[A-Z]*/gm</nc:match><nc:size>10k</nc:size><nc:no-world-readable/></nc:file><nc:flag> +# <nc:name>all</nc:name></nc:flag><nc:no-remote-trace/></nc:traceoptions></nc:policies> +# </nc:security>" +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <security> +# <policies> +# <traceoptions> +# <no-remote-trace /> +# <file> +# <size>10k</size> +# <files>3</files> +# <no-world-readable /> +# <match>/[A-Z]*/gm</match> +# </file> +# <flag> +# <name>all</name> +# </flag> +# </traceoptions> +# <default-policy> +# <permit-all /> +# </default-policy> +# <policy-rematch> +# <extensive /> +# </policy-rematch> +# <policy-stats> +# <system-wide>enable</system-wide> +# </policy-stats> +# <pre-id-default-policy> +# <then> +# <log> +# <session-init /> +# </log> +# <session-timeout> +# <icmp>10</icmp> +# <others>10</others> +# </session-timeout> +# </then> +# </pre-id-default-policy> +# </policies> +# </security> +# </configuration> +# </rpc-reply> +# +# +- name: Parse security policies global running config + junipernetworks.junos.junos_security_policies_global: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "default_policy": "permit-all", +# "policy_rematch": { +# "enable": true, +# "extensive": true +# }, +# "policy_stats": { +# "enable": true, +# "system_wide": true +# }, +# "pre_id_default_policy_action": { +# "log": { +# "session_init": true +# }, +# "session_timeout": { +# "icmp": 10, +# "others": 10 +# } +# }, +# "traceoptions": { +# "file": { +# "files": 3, +# "match": "/[A-Z]*/gm", +# "no_world_readable": true, +# "size": "10k" +# }, +# "flag": "all", +# "no_remote_trace": true +# } +# } +# +# +""" +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when state is I(merged), I(replaced), I(overridden), I(deleted) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when state is I(merged), I(replaced), I(overridden) or I(deleted) + type: list + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <default-policy> + <permit-all /> + </default-policy> + </policies> + </security> + </configuration> + </rpc-reply>" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when state is I(rendered) + type: dict + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <default-policy> + <permit-all /> + </default-policy> + </policies> + </security> + </configuration> + </rpc-reply>" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when state is I(gathered) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when state is I(parsed) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_policies_global.security_policies_global import ( + Security_policies_globalArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.security_policies_global.security_policies_global import ( + Security_policies_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=Security_policies_globalArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Security_policies_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_zones.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_zones.py new file mode 100644 index 000000000..36032cbca --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_security_zones.py @@ -0,0 +1,2001 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_security_zones +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_security_zones +version_added: 2.9.0 +short_description: Manage security zones on Juniper JUNOS devices +description: This module provides declarative management of security zones on Juniper JUNOS devices +author: Pranav Bhatt (@pranav-bhatt) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). +- See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: Dictionary of security zone parameters + type: dict + suboptions: + functional_zone_management: + description: + - Functional zone to configure host for out of band management interfaces + type: dict + suboptions: + description: + description: + - Text description of zone + type: str + host_inbound_traffic: &host_inbound_traffic + description: + - Allowed system services & protocols + type: dict + suboptions: + protocols: + description: + - Protocol type of incoming traffic to accept + type: list + elements: dict + suboptions: + name: + description: + - Type of incoming protocol to accept + type: str + except: + description: + - Disallow the specified protocol traffic + type: bool + system_services: + description: + - Type of incoming system-service traffic to accept + type: list + elements: dict + suboptions: + name: + description: + - Type of incoming system-service traffic to accept + type: str + except: + description: + - Disallow the specified incoming system-service traffic + type: bool + interfaces: &interfaces + description: + - Interfaces that are part of this zone + type: list + elements: str + screen: &screen + description: + - Name of ids option object applied to the zone + type: str + zones: + description: + - Security zones + type: list + elements: dict + suboptions: + name: + description: + - Name of the security zone + type: str + address_book: + description: + - Address book entries + type: dict + suboptions: + addresses: + description: + - Define security addresses + type: list + elements: dict + suboptions: + name: + description: + - Name of address + type: str + ip_prefix: + description: + - Numeric IPv4 or IPv6 address with prefix + type: str + description: + description: + - Text description of address + type: str + dns_name: + description: + - DNS address name + type: dict + suboptions: + name: + description: + - Fully qualified hostname + type: str + ipv4_only: + description: + - IPv4 dns address + type: bool + ipv6_only: + description: + - IPv6 dns address + type: bool + range_address: + description: + - Address range + type: dict + suboptions: + from: + description: + - Start of address range + type: str + to: + description: + - End of address range + type: str + wildcard_address: + description: + - Numeric IPv4 wildcard address with in the form of a.d.d.r/netmask + type: str + address_sets: + description: + - Define security address sets + type: list + elements: dict + suboptions: + name: + description: + - Name of address set + type: str + addresses: + description: + - Addresses to be included in this set + type: list + elements: str + address_sets: + description: + - Define an address-set name + type: list + elements: str + description: + description: + - Text description of address set + type: str + advance_policy_based_routing_profile: + description: + - Enable Advance Policy Based Routing on this zone + type: str + advanced_connection_tracking: + description: + - Enable Advance Policy Based Routing on this zone + type: dict + suboptions: + mode: + description: + - Set connection tracking mode + type: str + choices: + - allow-any-host + - allow-target-host + - allow-target-host-port + timeout: + description: + - Timeout value in seconds for advanced-connection-tracking table for this zone + type: int + track_all_policies_to_this_zone: + description: + - Mandate all policies with to-zone set to this zone to do connection track table lookup + type: bool + application_tracking: + description: + - Enable Application tracking support for this zone + type: bool + description: + description: + - Text description of zone + type: str + enable_reverse_reroute: + description: + - Enable Reverse route lookup when there is change in ingress interface + type: bool + host_inbound_traffic: *host_inbound_traffic + interfaces: *interfaces + screen: *screen + source_identity_log: + description: + - Show user and group info in session log for this zone + type: bool + tcp_rst: + description: + - Send RST for NON-SYN packet not matching TCP session + type: bool + unidirectional_session_refreshing: + description: + - Enable unidirectional session refreshing on this zone + 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 JunOS device + by executing the command B(show security policies). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + behaviour for this module. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show security policies detail) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" +EXAMPLES = """# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show security zones +# +# [edit] +# vagrant@vsrx# show security zones +# +- name: Merge the provided configuration with the exisiting running configuration + junipernetworks.junos.junos_security_zones: &merged + config: + functional_zone_management: + description: test description + host_inbound_traffic: + protocols: + - name: all + - name: bgp + except: true + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/1.0 + - ge-0/0/2.0 + screen: test_screen + security_zones: + - address_book: + address_sets: + - addresses: + - test_adr1 + - test_adr2 + name: test_adrset1 + - addresses: + - test_adr3 + - test_adr4 + name: test_adrset2 + - address_sets: + - test_adrset1 + - test_adrset2 + addresses: + - test_adr5 + description: test description + name: test_adrset3 + addresses: + - description: test desc + ip_prefix: 10.0.0.0/24 + name: test_adr1 + - dns_name: + ipv6_only: true + name: 1.1.1.1 + name: test_adr2 + - name: test_adr3 + range_address: + from: 10.2.0.1 + to: 10.2.0.2 + - name: test_adr4 + wildcard_address: 10.3.0.1/24 + - description: test desc + ip_prefix: 10.1.0.0/24 + name: test_adr5 + advance_policy_based_routing_profile: test_profile + application_tracking: true + description: test description + enable_reverse_reroute: true + host_inbound_traffic: + protocols: + - name: all + - except: true + name: bgp + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/3.0 + - ge-0/0/4.0 + name: test_sec_zone1 + screen: test_screen + source_identity_log: true + tcp_rst: true + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# }, +# "security_zones": [ +# { +# "address_book": { +# "address_sets": [ +# { +# "addresses": [ +# "test_adr1", +# "test_adr2" +# ], +# "name": "test_adrset1" +# }, +# { +# "addresses": [ +# "test_adr3", +# "test_adr4" +# ], +# "name": "test_adrset2" +# }, +# { +# "address_sets": [ +# "test_adrset1", +# "test_adrset2" +# ], +# "addresses": [ +# "test_adr5" +# ], +# "description": "test description", +# "name": "test_adrset3" +# } +# ], +# "addresses": [ +# { +# "description": "test desc", +# "ip_prefix": "10.0.0.0/24", +# "name": "test_adr1" +# }, +# { +# "dns_name": { +# "ipv6_only": true, +# "name": "1.1.1.1" +# }, +# "name": "test_adr2" +# }, +# { +# "name": "test_adr3", +# "range_address": { +# "from": "10.2.0.1", +# "to": "10.2.0.2" +# } +# }, +# { +# "name": "test_adr4", +# "wildcard_address": "10.3.0.1/24" +# }, +# { +# "description": "test desc", +# "ip_prefix": "10.1.0.0/24", +# "name": "test_adr5" +# } +# ] +# }, +# "advance_policy_based_routing_profile": "test_profile", +# "application_tracking": true, +# "description": "test description", +# "enable_reverse_reroute": true, +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/3.0", +# "ge-0/0/4.0" +# ], +# "name": "test_sec_zone1", +# "screen": "test_screen", +# "source_identity_log": true, +# "tcp_rst": true +# } +# ] +# }, +# "before": {}, +# "changed": true, +# "commands": +# '<nc:security xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:zones><nc:functional-zone><nc:management><nc:description>t' +# 'est description</nc:description><nc:host-inbound-traffic><nc:protocols><nc:name>all</nc:name></nc:protocols><nc:protocols><nc:na' +# 'me>bgp</nc:name><nc:except/></nc:protocols><nc:system-services><nc:name>all</nc:name></nc:system-services><nc:system-services><n' +# 'c:name>dhcp</nc:name><nc:except/></nc:system-services></nc:host-inbound-traffic><nc:interfaces><nc:name>ge-0/0/1.0</nc:name></nc' +# ':interfaces><nc:interfaces><nc:name>ge-0/0/2.0</nc:name></nc:interfaces><nc:screen>test_screen</nc:screen></nc:management></nc:f' +# 'unctional-zone><nc:security-zone><nc:name>test_sec_zone1</nc:name><nc:address-book><nc:address><nc:name>test_adr1</nc:name><nc:i' +# 'p-prefix>10.0.0.0/24</nc:ip-prefix><nc:description>test desc</nc:description></nc:address><nc:address><nc:name>test_adr2</nc:nam' +# 'e><nc:dns-name><nc:name>1.1.1.1</nc:name><nc:ipv6-only/></nc:dns-name></nc:address><nc:address><nc:name>test_adr3</nc:name><nc:r' +# 'ange-address><nc:name>10.2.0.1</nc:name><nc:to><nc:range-high>10.2.0.2</nc:range-high></nc:to></nc:range-address></nc:address><n' +# 'c:address><nc:name>test_adr4</nc:name><nc:wildcard-address><nc:name>10.3.0.1/24</nc:name></nc:wildcard-address></nc:address><nc:' +# 'address><nc:name>test_adr5</nc:name><nc:ip-prefix>10.1.0.0/24</nc:ip-prefix><nc:description>test desc</nc:description></nc:addre' +# 'ss><nc:address-set><nc:name>test_adrset1</nc:name><nc:address><nc:name>test_adr1</nc:name></nc:address><nc:address><nc:name>test' +# '_adr2</nc:name></nc:address></nc:address-set><nc:address-set><nc:name>test_adrset2</nc:name><nc:address><nc:name>test_adr3</nc:n' +# 'ame></nc:address><nc:address><nc:name>test_adr4</nc:name></nc:address></nc:address-set><nc:address-set><nc:name>test_adrset3</nc' +# ':name><nc:address><nc:name>test_adr5</nc:name></nc:address><nc:address-set><nc:name>test_adrset1</nc:name></nc:address-set><nc:a' +# 'ddress-set><nc:name>test_adrset2</nc:name></nc:address-set><nc:description>test description</nc:description></nc:address-set></n' +# 'c:address-book><nc:advance-policy-based-routing-profile><nc:profile>test_profile</nc:profile></nc:advance-policy-based-routing-p' +# 'rofile><nc:application-tracking/><nc:description>test description</nc:description><nc:enable-reverse-reroute/><nc:host-inbound-t' +# 'raffic><nc:protocols><nc:name>all</nc:name></nc:protocols><nc:protocols><nc:name>bgp</nc:name><nc:except/></nc:protocols><nc:sys' +# 'tem-services><nc:name>all</nc:name></nc:system-services><nc:system-services><nc:name>dhcp</nc:name><nc:except/></nc:system-servi' +# 'ces></nc:host-inbound-traffic><nc:interfaces><nc:name>ge-0/0/3.0</nc:name></nc:interfaces><nc:interfaces><nc:name>ge-0/0/4.0</nc' +# ':name></nc:interfaces><nc:screen>test_screen</nc:screen><nc:source-identity-log/><nc:tcp-rst/></nc:security-zone></nc:zones></nc' +# ':security>' + +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# security-zone test_sec_zone1 { +# description "test description"; +# tcp-rst; +# address-book { +# address test_adr1 { +# description "test desc"; +# 10.0.0.0/24; +# } +# address test_adr2 { +# dns-name 1.1.1.1 { +# ipv6-only; +# } +# } +# address test_adr3 { +# range-address 10.2.0.1 { +# to { +# 10.2.0.2; +# } +# } +# } +# address test_adr4 { +# wildcard-address 10.3.0.1/24; +# } +# address test_adr5 { +# description "test desc"; +# 10.1.0.0/24; +# } +# address-set test_adrset1 { +# address test_adr1; +# address test_adr2; +# } +# address-set test_adrset2 { +# address test_adr3; +# address test_adr4; +# } +# address-set test_adrset3 { +# description "test description"; +# address test_adr5; +# address-set test_adrset1; +# address-set test_adrset2; +# } +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# interfaces { +# ge-0/0/3.0; +# ge-0/0/4.0; +# } +# application-tracking; +# source-identity-log; +# advance-policy-based-routing-profile { +# test_profile; +# } +# enable-reverse-reroute; +# } +# +# +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx# show security zones +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# security-zone test_sec_zone1 { +# description "test description"; +# tcp-rst; +# address-book { +# address test_adr1 { +# description "test desc"; +# 10.0.0.0/24; +# } +# address test_adr2 { +# dns-name 1.1.1.1 { +# ipv6-only; +# } +# } +# address test_adr3 { +# range-address 10.2.0.1 { +# to { +# 10.2.0.2; +# } +# } +# } +# address test_adr4 { +# wildcard-address 10.3.0.1/24; +# } +# address test_adr5 { +# description "test desc"; +# 10.1.0.0/24; +# } +# address-set test_adrset1 { +# address test_adr1; +# address test_adr2; +# } +# address-set test_adrset2 { +# address test_adr3; +# address test_adr4; +# } +# address-set test_adrset3 { +# description "test description"; +# address test_adr5; +# address-set test_adrset1; +# address-set test_adrset2; +# } +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# interfaces { +# ge-0/0/3.0; +# ge-0/0/4.0; +# } +# application-tracking; +# source-identity-log; +# advance-policy-based-routing-profile { +# test_profile; +# } +# enable-reverse-reroute; +# } +# +# + +- name: Replaced running security zones configuration with provided configuration + junipernetworks.junos.junos_security_zones: + config: + functional_zone_management: + description: test description + host_inbound_traffic: + protocols: + - name: all + - name: bgp + except: true + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/1.0 + - ge-0/0/2.0 + screen: test_screen + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# } +# }, +# "before": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# }, +# "security_zones": [ +# { +# "address_book": { +# "address_sets": [ +# { +# "addresses": [ +# "test_adr1", +# "test_adr2" +# ], +# "name": "test_adrset1" +# }, +# { +# "addresses": [ +# "test_adr3", +# "test_adr4" +# ], +# "name": "test_adrset2" +# }, +# { +# "address_sets": [ +# "test_adrset1", +# "test_adrset2" +# ], +# "addresses": [ +# "test_adr5" +# ], +# "description": "test description", +# "name": "test_adrset3" +# } +# ], +# "addresses": [ +# { +# "description": "test desc", +# "ip_prefix": "10.0.0.0/24", +# "name": "test_adr1" +# }, +# { +# "dns_name": { +# "ipv6_only": true, +# "name": "1.1.1.1" +# }, +# "name": "test_adr2" +# }, +# { +# "name": "test_adr3", +# "range_address": { +# "from": "10.2.0.1", +# "to": "10.2.0.2" +# } +# }, +# { +# "name": "test_adr4", +# "wildcard_address": "10.3.0.1/24" +# }, +# { +# "description": "test desc", +# "ip_prefix": "10.1.0.0/24", +# "name": "test_adr5" +# } +# ] +# }, +# "advance_policy_based_routing_profile": "test_profile", +# "application_tracking": true, +# "description": "test description", +# "enable_reverse_reroute": true, +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/3.0", +# "ge-0/0/4.0" +# ], +# "name": "test_sec_zone1", +# "screen": "test_screen", +# "source_identity_log": true, +# "tcp_rst": true +# } +# ] +# }, +# "changed": true, +# "commands": +# '<nc:security xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:zones delete=\"delete\"/><nc:zones><nc:functional-zone><nc' +# ':management><nc:description>test description</nc:description><nc:host-inbound-traffic><nc:protocols><nc:name>all</nc:name></nc:p' +# 'rotocols><nc:protocols><nc:name>bgp</nc:name><nc:except/></nc:protocols><nc:system-services><nc:name>all</nc:name></nc:system-se' +# 'rvices><nc:system-services><nc:name>dhcp</nc:name><nc:except/></nc:system-services></nc:host-inbound-traffic><nc:interfaces><nc:' +# 'name>ge-0/0/1.0</nc:name></nc:interfaces><nc:interfaces><nc:name>ge-0/0/2.0</nc:name></nc:interfaces><nc:screen>test_screen</nc:' +# 'screen></nc:management></nc:functional-zone></nc:zones></nc:security>' +# +# +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# +# +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show security zones +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# security-zone test_sec_zone1 { +# description "test description"; +# tcp-rst; +# address-book { +# address test_adr1 { +# description "test desc"; +# 10.0.0.0/24; +# } +# address test_adr2 { +# dns-name 1.1.1.1 { +# ipv6-only; +# } +# } +# address test_adr3 { +# range-address 10.2.0.1 { +# to { +# 10.2.0.2; +# } +# } +# } +# address test_adr4 { +# wildcard-address 10.3.0.1/24; +# } +# address test_adr5 { +# description "test desc"; +# 10.1.0.0/24; +# } +# address-set test_adrset1 { +# address test_adr1; +# address test_adr2; +# } +# address-set test_adrset2 { +# address test_adr3; +# address test_adr4; +# } +# address-set test_adrset3 { +# description "test description"; +# address test_adr5; +# address-set test_adrset1; +# address-set test_adrset2; +# } +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# interfaces { +# ge-0/0/3.0; +# ge-0/0/4.0; +# } +# application-tracking; +# source-identity-log; +# advance-policy-based-routing-profile { +# test_profile; +# } +# enable-reverse-reroute; +# } +# +# + +- name: Override running security zones configuration with provided configuration + junipernetworks.junos.junos_security_zones: + config: + functional_zone_management: + description: test description + host_inbound_traffic: + protocols: + - name: all + - name: bgp + except: true + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/1.0 + - ge-0/0/2.0 + screen: test_screen + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# } +# }, +# "before": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# }, +# "security_zones": [ +# { +# "address_book": { +# "address_sets": [ +# { +# "addresses": [ +# "test_adr1", +# "test_adr2" +# ], +# "name": "test_adrset1" +# }, +# { +# "addresses": [ +# "test_adr3", +# "test_adr4" +# ], +# "name": "test_adrset2" +# }, +# { +# "address_sets": [ +# "test_adrset1", +# "test_adrset2" +# ], +# "addresses": [ +# "test_adr5" +# ], +# "description": "test description", +# "name": "test_adrset3" +# } +# ], +# "addresses": [ +# { +# "description": "test desc", +# "ip_prefix": "10.0.0.0/24", +# "name": "test_adr1" +# }, +# { +# "dns_name": { +# "ipv6_only": true, +# "name": "1.1.1.1" +# }, +# "name": "test_adr2" +# }, +# { +# "name": "test_adr3", +# "range_address": { +# "from": "10.2.0.1", +# "to": "10.2.0.2" +# } +# }, +# { +# "name": "test_adr4", +# "wildcard_address": "10.3.0.1/24" +# }, +# { +# "description": "test desc", +# "ip_prefix": "10.1.0.0/24", +# "name": "test_adr5" +# } +# ] +# }, +# "advance_policy_based_routing_profile": "test_profile", +# "application_tracking": true, +# "description": "test description", +# "enable_reverse_reroute": true, +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/3.0", +# "ge-0/0/4.0" +# ], +# "name": "test_sec_zone1", +# "screen": "test_screen", +# "source_identity_log": true, +# "tcp_rst": true +# } +# ] +# }, +# "changed": true, +# "commands": +# '<nc:security xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:zones delete=\"delete\"/><nc:zones><nc:functional-zone><nc' +# ':management><nc:description>test description</nc:description><nc:host-inbound-traffic><nc:protocols><nc:name>all</nc:name></nc:p' +# 'rotocols><nc:protocols><nc:name>bgp</nc:name><nc:except/></nc:protocols><nc:system-services><nc:name>all</nc:name></nc:system-se' +# 'rvices><nc:system-services><nc:name>dhcp</nc:name><nc:except/></nc:system-services></nc:host-inbound-traffic><nc:interfaces><nc:' +# 'name>ge-0/0/1.0</nc:name></nc:interfaces><nc:interfaces><nc:name>ge-0/0/2.0</nc:name></nc:interfaces><nc:screen>test_screen</nc:' +# 'screen></nc:management></nc:functional-zone></nc:zones></nc:security>' +# +# +# After state +# ----------- +# +# vagrant@vsrx# show system ntp +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show security zones +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# +# +- name: Delete running security zones configuration + junipernetworks.junos.junos_security_zones: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# } +# }, +# "changed": true, +# "commands": +# "<nc:security xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:zones delete=\"delete\"/></nc:security>" +# +# +# After state +# ----------- +# +# vagrant@vsrx# show security zones +# +# [edit] +# Using gathered +# +# Before state +# ------------ +# +# vagrant@vsrx# show system ntp +# functional-zone management { +# interfaces { +# ge-0/0/1.0; +# ge-0/0/2.0; +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# description "test description"; +# } +# security-zone test_sec_zone1 { +# description "test description"; +# tcp-rst; +# address-book { +# address test_adr1 { +# description "test desc"; +# 10.0.0.0/24; +# } +# address test_adr2 { +# dns-name 1.1.1.1 { +# ipv6-only; +# } +# } +# address test_adr3 { +# range-address 10.2.0.1 { +# to { +# 10.2.0.2; +# } +# } +# } +# address test_adr4 { +# wildcard-address 10.3.0.1/24; +# } +# address test_adr5 { +# description "test desc"; +# 10.1.0.0/24; +# } +# address-set test_adrset1 { +# address test_adr1; +# address test_adr2; +# } +# address-set test_adrset2 { +# address test_adr3; +# address test_adr4; +# } +# address-set test_adrset3 { +# description "test description"; +# address test_adr5; +# address-set test_adrset1; +# address-set test_adrset2; +# } +# } +# screen test_screen; +# host-inbound-traffic { +# system-services { +# all; +# dhcp { +# except; +# } +# } +# protocols { +# all; +# bgp { +# except; +# } +# } +# } +# interfaces { +# ge-0/0/3.0; +# ge-0/0/4.0; +# } +# application-tracking; +# source-identity-log; +# advance-policy-based-routing-profile { +# test_profile; +# } +# enable-reverse-reroute; +# } +- name: Gather running security zones configuration + junipernetworks.junos.junos_security_zones: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "functional_zone_management": { +# "description": "test description", +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/1.0", +# "ge-0/0/2.0" +# ], +# "screen": "test_screen" +# }, +# "security_zones": [ +# { +# "address_book": { +# "address_sets": [ +# { +# "addresses": [ +# "test_adr1", +# "test_adr2" +# ], +# "name": "test_adrset1" +# }, +# { +# "addresses": [ +# "test_adr3", +# "test_adr4" +# ], +# "name": "test_adrset2" +# }, +# { +# "address_sets": [ +# "test_adrset1", +# "test_adrset2" +# ], +# "addresses": [ +# "test_adr5" +# ], +# "description": "test description", +# "name": "test_adrset3" +# } +# ], +# "addresses": [ +# { +# "description": "test desc", +# "ip_prefix": "10.0.0.0/24", +# "name": "test_adr1" +# }, +# { +# "dns_name": { +# "ipv6_only": true, +# "name": "1.1.1.1" +# }, +# "name": "test_adr2" +# }, +# { +# "name": "test_adr3", +# "range_address": { +# "from": "10.2.0.1", +# "to": "10.2.0.2" +# } +# }, +# { +# "name": "test_adr4", +# "wildcard_address": "10.3.0.1/24" +# }, +# { +# "description": "test desc", +# "ip_prefix": "10.1.0.0/24", +# "name": "test_adr5" +# } +# ] +# }, +# "advance_policy_based_routing_profile": "test_profile", +# "application_tracking": true, +# "description": "test description", +# "enable_reverse_reroute": true, +# "host_inbound_traffic": { +# "protocols": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "bgp" +# } +# ], +# "system_services": [ +# { +# "name": "all" +# }, +# { +# "except": true, +# "name": "dhcp" +# } +# ] +# }, +# "interfaces": [ +# "ge-0/0/3.0", +# "ge-0/0/4.0" +# ], +# "name": "test_sec_zone1", +# "screen": "test_screen", +# "source_identity_log": true, +# "tcp_rst": true +# } +# ] +# } +# "changed": false, +# +# +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_security_zones: + config: + functional_zone_management: + description: test description + host_inbound_traffic: + protocols: + - name: all + - name: bgp + except: true + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/1.0 + - ge-0/0/2.0 + screen: test_screen + security_zones: + - address_book: + address_sets: + - addresses: + - test_adr1 + - test_adr2 + name: test_adrset1 + - addresses: + - test_adr3 + - test_adr4 + name: test_adrset2 + - address_sets: + - test_adrset1 + - test_adrset2 + addresses: + - test_adr5 + description: test description + name: test_adrset3 + addresses: + - description: test desc + ip_prefix: 10.0.0.0/24 + name: test_adr1 + - dns_name: + ipv6_only: true + name: 1.1.1.1 + name: test_adr2 + - name: test_adr3 + range_address: + from: 10.2.0.1 + to: 10.2.0.2 + - name: test_adr4 + wildcard_address: 10.3.0.1/24 + - description: test desc + ip_prefix: 10.1.0.0/24 + name: test_adr5 + advance_policy_based_routing_profile: test_profile + application_tracking: true + description: test description + enable_reverse_reroute: true + host_inbound_traffic: + protocols: + - name: all + - except: true + name: bgp + system_services: + - name: all + - except: true + name: dhcp + interfaces: + - ge-0/0/3.0 + - ge-0/0/4.0 + name: test_sec_zone1 + screen: test_screen + source_identity_log: true + tcp_rst: true + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": +# '<nc:security xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:zones><nc:functional-zone><nc:management><nc:description>t' +# 'est description</nc:description><nc:host-inbound-traffic><nc:protocols><nc:name>all</nc:name></nc:protocols><nc:protocols><nc:na' +# 'me>bgp</nc:name><nc:except/></nc:protocols><nc:system-services><nc:name>all</nc:name></nc:system-services><nc:system-services><n' +# 'c:name>dhcp</nc:name><nc:except/></nc:system-services></nc:host-inbound-traffic><nc:interfaces><nc:name>ge-0/0/1.0</nc:name></nc' +# ':interfaces><nc:interfaces><nc:name>ge-0/0/2.0</nc:name></nc:interfaces><nc:screen>test_screen</nc:screen></nc:management></nc:f' +# 'unctional-zone><nc:security-zone><nc:name>test_sec_zone1</nc:name><nc:address-book><nc:address><nc:name>test_adr1</nc:name><nc:i' +# 'p-prefix>10.0.0.0/24</nc:ip-prefix><nc:description>test desc</nc:description></nc:address><nc:address><nc:name>test_adr2</nc:nam' +# 'e><nc:dns-name><nc:name>1.1.1.1</nc:name><nc:ipv6-only/></nc:dns-name></nc:address><nc:address><nc:name>test_adr3</nc:name><nc:r' +# 'ange-address><nc:name>10.2.0.1</nc:name><nc:to><nc:range-high>10.2.0.2</nc:range-high></nc:to></nc:range-address></nc:address><n' +# 'c:address><nc:name>test_adr4</nc:name><nc:wildcard-address><nc:name>10.3.0.1/24</nc:name></nc:wildcard-address></nc:address><nc:' +# 'address><nc:name>test_adr5</nc:name><nc:ip-prefix>10.1.0.0/24</nc:ip-prefix><nc:description>test desc</nc:description></nc:addre' +# 'ss><nc:address-set><nc:name>test_adrset1</nc:name><nc:address><nc:name>test_adr1</nc:name></nc:address><nc:address><nc:name>test' +# '_adr2</nc:name></nc:address></nc:address-set><nc:address-set><nc:name>test_adrset2</nc:name><nc:address><nc:name>test_adr3</nc:n' +# 'ame></nc:address><nc:address><nc:name>test_adr4</nc:name></nc:address></nc:address-set><nc:address-set><nc:name>test_adrset3</nc' +# ':name><nc:address><nc:name>test_adr5</nc:name></nc:address><nc:address-set><nc:name>test_adrset1</nc:name></nc:address-set><nc:a' +# 'ddress-set><nc:name>test_adrset2</nc:name></nc:address-set><nc:description>test description</nc:description></nc:address-set></n' +# 'c:address-book><nc:advance-policy-based-routing-profile><nc:profile>test_profile</nc:profile></nc:advance-policy-based-routing-p' +# 'rofile><nc:application-tracking/><nc:description>test description</nc:description><nc:enable-reverse-reroute/><nc:host-inbound-t' +# 'raffic><nc:protocols><nc:name>all</nc:name></nc:protocols><nc:protocols><nc:name>bgp</nc:name><nc:except/></nc:protocols><nc:sys' +# 'tem-services><nc:name>all</nc:name></nc:system-services><nc:system-services><nc:name>dhcp</nc:name><nc:except/></nc:system-servi' +# 'ces></nc:host-inbound-traffic><nc:interfaces><nc:name>ge-0/0/3.0</nc:name></nc:interfaces><nc:interfaces><nc:name>ge-0/0/4.0</nc' +# ':name></nc:interfaces><nc:screen>test_screen</nc:screen><nc:source-identity-log/><nc:tcp-rst/></nc:security-zone></nc:zones></nc' +# ':security>' +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <security> +# <zones> +# <functional-zone> +# <management> +# <description>test description</description> +# <host-inbound-traffic> +# <protocols> +# <name>all</name> +# </protocols> +# <protocols> +# <name>bgp</name> +# <except /> +# </protocols> +# <system-services> +# <name>all</name> +# </system-services> +# <system-services> +# <name>dhcp</name> +# <except /> +# </system-services> +# </host-inbound-traffic> +# <interfaces> +# <name>ge-0/0/1.0</name> +# </interfaces> +# <interfaces> +# <name>ge-0/0/2.0</name> +# </interfaces> +# <screen>test_screen</screen> +# </management> +# </functional-zone> +# <security-zone> +# <name>test_sec_zone1</name> +# <address-book> +# <address> +# <name>test_adr1</name> +# <ip-prefix>10.0.0.0/24</ip-prefix> +# <description>test desc</description> +# </address> +# <address> +# <name>test_adr2</name> +# <dns-name> +# <name>1.1.1.1</name> +# <ipv6-only /> +# </dns-name> +# </address> +# <address> +# <name>test_adr3</name> +# <range-address> +# <name>10.2.0.1</name> +# <to> +# <range-high>10.2.0.2</range-high> +# </to> +# </range-address> +# </address> +# <address> +# <name>test_adr4</name> +# <wildcard-address> +# <name>10.3.0.1/24</name> +# </wildcard-address> +# </address> +# <address> +# <name>test_adr5</name> +# <ip-prefix>10.1.0.0/24</ip-prefix> +# <description>test desc</description> +# </address> +# <address-set> +# <name>test_adrset1</name> +# <address> +# <name>test_adr1</name> +# </address> +# <address> +# <name>test_adr2</name> +# </address> +# </address-set> +# <address-set> +# <name>test_adrset2</name> +# <address> +# <name>test_adr3</name> +# </address> +# <address> +# <name>test_adr4</name> +# </address> +# </address-set> +# <address-set> +# <name>test_adrset3</name> +# <address> +# <name>test_adr5</name> +# </address> +# <address-set> +# <name>test_adrset1</name> +# </address-set> +# <address-set> +# <name>test_adrset2</name> +# </address-set> +# <description>test description</description> +# </address-set> +# </address-book> +# <advance-policy-based-routing-profile> +# <profile>test_profile</profile> +# </advance-policy-based-routing-profile> +# <application-tracking /> +# <description>test description</description> +# <enable-reverse-reroute /> +# <host-inbound-traffic> +# <protocols> +# <name>all</name> +# </protocols> +# <protocols> +# <name>bgp</name> +# <except /> +# </protocols> +# <system-services> +# <name>all</name> +# </system-services> +# <system-services> +# <name>dhcp</name> +# <except /> +# </system-services> +# </host-inbound-traffic> +# <interfaces> +# <name>ge-0/0/3.0</name> +# </interfaces> +# <interfaces> +# <name>ge-0/0/4.0</name> +# </interfaces> +# <screen>test_screen</screen> +# <source-identity-log /> +# <tcp-rst /> +# </security-zone> +# </zones> +# </security> +# </configuration> +# </rpc-reply> +# +- name: Parse security zones running config + junipernetworks.junos.junos_security_zones: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "functional_zone_management": { +# "description": "test description 2", +# "host_inbound_traffic": { +# "protocols": [{"name": "all"}, {"except": True, "name": "bgp"}, {"except": True, "name": "bfd"}], +# "system_services": [{"name": "all"}, {"except": True, "name": "dhcp"}, {"except": True, "name": "dhcpv6"}], +# }, +# "interfaces": ["ge-0/0/1.0", "ge-0/0/2.0"], +# "screen": "test_screen", +# }, +# "security_zones": [ +# { +# "address_book": { +# "address_sets": [ +# {"addresses": ["test_adr1", "test_adr2"], "name": "test_adrset1"}, +# {"addresses": ["test_adr3", "test_adr4"], "name": "test_adrset2"}, +# { +# "address_sets": ["test_adrset1", "test_adrset2"], +# "addresses": ["test_adr5"], +# "description": "test description", +# "name": "test_adrset3", +# }, +# ], +# "addresses": [ +# {"description": "test desc", "ip_prefix": "10.0.0.0/24", "name": "test_adr1"}, +# {"dns_name": {"ipv6_only": True, "name": "1.1.1.1"}, "name": "test_adr2"}, +# {"name": "test_adr3", "range_address": {"from": "10.2.0.1", "to": "10.2.0.2"}}, +# {"name": "test_adr4", "wildcard_address": "10.3.0.1/24"}, +# {"description": "test desc", "ip_prefix": "10.1.0.0/24", "name": "test_adr5"}, +# ], +# }, +# "advance_policy_based_routing_profile": "test_profile", +# "application_tracking": True, +# "description": "test description", +# "enable_reverse_reroute": True, +# "host_inbound_traffic": { +# "protocols": [{"name": "all"}, {"except": True, "name": "bgp"}], +# "system_services": [{"name": "all"}, {"except": True, "name": "dhcp"}], +# }, +# "interfaces": ["ge-0/0/3.0", "ge-0/0/4.0"], +# "name": "test_sec_zone1", +# "screen": "test_screen", +# "source_identity_log": True, +# "tcp_rst": True, +# }, +# {"name": "test_sec_zone2", "source_identity_log": True, "tcp_rst": True}, +# ], +# } +# +# +""" +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) + type: list + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <global> + <policy> + <name>test_glob_1</name> + <match> + <source-address>any-ipv6</source-address> + <destination-address>any-ipv6</destination-address> + <application>any</application> + </match> + <then> + <deny /> + </then> + </policy> + </global> + </policies> + </security> + </configuration> + </rpc-reply>" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when state is I(rendered) + type: dict + sample: + - "<rpc-reply> + <configuration> + <security> + <policies> + <global> + <policy> + <name>test_glob_1</name> + <match> + <source-address>any-ipv6</source-address> + <destination-address>any-ipv6</destination-address> + <application>any</application> + </match> + <then> + <deny /> + </then> + </policy> + </global> + </policies> + </security> + </configuration> + </rpc-reply>" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when state is I(gathered) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when state is I(parsed) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.security_zones.security_zones import ( + Security_zonesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.security_zones.security_zones import ( + Security_zones, +) + + +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=Security_zonesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Security_zones(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_snmp_server.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_snmp_server.py new file mode 100644 index 000000000..491298327 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_snmp_server.py @@ -0,0 +1,1584 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# 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 junos_snmp_server +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: junos_snmp_server +version_added: 2.9.0 +short_description: Manage SNMP server configuration on Junos devices. +description: This module manages SNMP server configuration on devices running Junos. +author: Rohit Thakur (@rohitthakur2590) +requirements: + - ncclient (>=v0.6.4) + - xmltodict (>=0.12.0) +notes: + - This module requires the netconf system service be enabled on the device being managed. + - This module works with connection C(netconf). + - See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). + - Tested against JunOS v18.4R1 +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 Junos device + by executing the command B(show system snmp). + - 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 SNMP server configuration. + type: dict + suboptions: + arp: + description: Specify JVision arp setting. + type: dict + suboptions: + set: + description: Set JVision arp. + type: bool + host_name_resolution: + description: Enable host name resolution. + type: bool + client_lists: + description: Specify client list. + type: list + elements: dict + suboptions: + name: + description: Specify client list name. + type: str + addresses: + description: Specify list of addresses/prefixes. + type: list + elements: dict + suboptions: + address: + description: Specify address or prefix. + type: str + restrict: + description: Deny access. + type: bool + routing_instance_access: &routing_instance_access + description: SNMP routing-instance options. + type: dict + suboptions: + set: + description: Set routing_instance_access. + type: bool + access_lists: + description: Allow/Deny SNMP access to routing-instances. + type: list + elements: str + communities: + description: Specify list of community string. + type: list + elements: dict + suboptions: + name: + description: Specify name of the community. + type: str + authorization: + description: Specify Authorization type. + type: str + choices: ["read-only", "read-write"] + client_list_name: &client_list_name + description: Specify the name of client list or prefix list. + type: str + clients: &clients + description: Specify List of source address prefix ranges to accept. + type: list + elements: dict + suboptions: + address: + description: Specify address or prefix. + type: str + restrict: + description: Deny access. + type: bool + routing_instances: &routing_instances + description: Use routing-instance name for v1/v2c clients. + type: list + elements: dict + suboptions: + name: + description: Specify routing-instances. + type: str + client_list_name: *client_list_name + clients: *clients + logical_system: &logical_system + description: Use logical-system name for v1/v2c clients. + type: list + elements: str + view: + description: Specify view name. + type: str + contact: + description: Specify contact information for administrator. + type: str + customization: + description: Customize SNMP behaviour based on knob. + type: dict + suboptions: + ether_stats_ifd_only: + description: To stop exposing IFLs as part of etherStatsTable. + type: bool + description: + description: System description. + type: str + engine_id: + description: Specify SNMPv3 engine ID + type: dict + suboptions: + local: + description: Local engine ID. + type: str + use_default_ip_address: + description: Use default IP address for the engine ID. + type: bool + use_mac_address: + description: Uses management interface MAC Address for the engine ID. + type: bool + filter_duplicates: + description: Filter requests with duplicate source address/port and request ID. + type: bool + filter_interfaces: + description: List of interfaces that needs to be filtered. + type: dict + suboptions: + set: + description: Set filter-interfaces. + type: bool + all_internal_interfaces: + description: Filter all internal interfaces. + type: bool + interfaces: + description: Specify filter specified interfaces. + type: list + elements: str + health_monitor: + description: Specify health monitoring configuration. + type: dict + suboptions: + set: + description: Set health-monitor configuration. + type: bool + falling_threshold: + description: Falling threshold applied to all monitored objects. + type: int + rising_threshold: + description: Rising threshold applied to all monitored objects. + type: int + idp: + description: IDP health monitor configuration. + type: bool + interval: + description: Interval between samples. + type: int + if_count_with_filter_interfaces: + description: Filter interfaces config for ifNumber and ipv6Interfaces. + type: bool + interfaces: + description: Restrict SNMP requests to interfaces. + type: list + elements: str + location: + description: Specify physical location of system. + type: str + logical_system_trap_filter: + description: Allow only logical-system specific traps. + type: bool + name: + description: System name override. + type: str + nonvolatile: + description: Configure the handling of nonvolatile SNMP Set requests. + type: dict + suboptions: + commit_delay: + description: Delay between affirmative SNMP Set reply and start of commit (seconds). + type: int + proxies: + description: SNMP proxy configuration. + type: list + elements: dict + suboptions: + name: + description: Specify proxy name. + type: str + device_name: + description: Satellite/Proxied Device name or IP address. + type: str + logical_system: *logical_system + routing_instances: *routing_instances + version_v1: + description: Specify For v1 proxy configuration define snmp-community. + type: dict + suboptions: + no_default_comm_to_v3_config: + description: Specify No default snmp-community and v3 configuration. + type: bool + snmp_community: + description: Specify community name. + type: str + version_v2c: + description: For v2c proxy configuration define snmp-community. + type: dict + suboptions: + no_default_comm_to_v3_config: + description: Specify No default snmp-community and v3 configuration. + type: bool + snmp_community: + description: Specify community name. + type: str + version_v3: + description: For v3 proxy configuration define security-name. + type: dict + suboptions: + context: + description: pecify context name associated to this security-name. + type: bool + security_name: + description: Specify v3 security-name. + type: str + rmon: + description: Specify Remote Monitoring configuration. + type: dict + suboptions: + set: + description: Set Remote monitoring configuration. + type: bool + alarms: + description: RMON alarm entries. + type: list + elements: dict + suboptions: + id: + description: Specify alarm ID. + type: str + description: + description: General description of alarm (stored in alarmOwner). + type: str + falling_event_index: + description: Event triggered after falling threshold is crossed. + type: int + falling_threshold: + description: Specify falling-threshold. + type: int + falling_threshold_interval: + description: Interval between samples during falling-threshold test. + type: int + interval: + description: Interval between samples. + type: int + request_type: + description: Type of SNMP request to issue for alarm. + type: str + choices: ["get-next-request", "get-request", "walk-request"] + rising_event_index: + description: Event triggered after rising threshold is crossed. + type: int + rising_threshold: + description: The rising threshold. + type: int + sample_type: + description: Method of sampling the selected variable. + type: str + choices: ["absolute-value", "delta-value"] + startup_alarm: + description: The alarm that may be sent upon entry startup. + type: str + choices: ["falling-alarm", "rising-alarm", "rising-or-falling-alarm"] + syslog_subtag: + description: Tag to be added to syslog messages. + type: str + variable: + description: OID of MIB variable to be monitored. + type: str + events: + description: RMON event entries. + type: list + elements: dict + suboptions: + id: + description: Specify event ID. + type: int + community: + description: The community (trap group) for outgoing traps. + type: str + description: + description: General description of event. + type: str + type: + description: The type of notification for this event. + type: str + choices: ["log", "log-and-trap", "none", "snmptrap"] + subagent: + description: SNMP subagent configuration. + type: dict + suboptions: + tcp: + description: Allow SNMP subagent tcp connection. + type: dict + suboptions: + set: + description: Set SNMP subagent TCP. + type: bool + routing_instances_default: + description: Specify routing-instance name for tcp connection. + type: bool + traceoptions: + description: Configure trace options for SNMP. + type: dict + suboptions: + file: + description: Specify trace file options. + type: dict + suboptions: + match: + description: Regular expression for lines to be logged. + type: str + files: + description: Specify maximum number of trace files. + type: int + no_world_readable: + description: Don't allow any user to read the log file. + type: bool + world_readable: + description: Allow any user to read the log file. + type: bool + size: + description: Specify maximum trace file size. + type: int + flag: + description: Specify flag traceoptions. + type: dict + suboptions: + all: + description: Trace everything. + type: bool + general: + description: Trace general events. + type: bool + interface_stats: + description: Trace interface statistics (logical and physical). + type: bool + nonvolatile_sets: + description: Nonvolatile SNMP set request handling. + type: bool + pdu: + description: Dump SNMP request/response packets. + type: bool + protocol_timeouts: + description: Trace SNMP request timeouts. + type: bool + routing_socket: + description: Trace routing socket calls. + type: bool + subagent: + description: Trace master-agent interations with sub-agents. + type: bool + timer: + description: Trace internal timer events. + type: bool + varbind_error: + description: Trace varbind errors. + type: bool + memory_trace: + description: Memory tracing information. + type: dict + suboptions: + set: + description: set memory traceoptions. + type: bool + size: + description: Specify Memory size reserved for tracing. + type: int + no_remote_trace: + description: Disable remote tracing. + type: bool + trap_groups: + description: Specify SNMP trap options. + type: list + elements: dict + suboptions: + name: + description: Specify trap group name. + type: str + categories: + description: Specify Trap categories. + type: dict + suboptions: + authentication: + description: Specify Authentication failures. + type: bool + chassis: + description: Specify Chassis or environment notifications. + type: bool + chassis_cluster: + description: Specify Clustering notifications. + type: bool + configuration: + description: Configuration notifications. + type: bool + dot3oam_events: + description: Specify 802.3ah notifications. + type: bool + link: + description: Link up-down transitions. + type: bool + otn_alarms: + description: OTN alarm trap subcategories. + type: dict + suboptions: + set: + description: Set otn_alarms. + type: bool + oc_lof: + description: Loss of frame alarm notifications. + type: bool + oc_lom: + description: Loss of multiframe alarm notification. + type: bool + oc_los: + description: Loss of signal alarm notification. + type: bool + odu_ais: + description: ODU Alarm indication signal alarm notification. + type: bool + odu_bbe_threshold: + description: ODU Background block error threshold alarm notification. + type: bool + odu_bdi: + description: ODU Backward defect indication alarm notification. + type: bool + odu_bdodu_es_threshold: + description: ODU Errored Second threshold alarm notification. + type: bool + odu_lck: + description: ODU Locked alarm notification. + type: bool + odu_oci: + description: ODU Open connection indicator alarm notifications. + type: bool + odu_rx_aps_change: + description: ODU Receive APS change notifications. + type: bool + odu_sd: + description: ODU Signal degrade alarm notifications. + type: bool + odu_ses_threshold: + description: ODU Severely Errored Second threshold alarm notification. + type: bool + odu_sf: + description: ODU Signal fail alarm notification. + type: bool + odu_ttim: + description: ODU Trace identification mismatch alarm notification. + type: bool + odu_uas_threshold: + description: ODU Unavailable Second threshold alarm notification. + type: bool + opu_ptm: + description: ODU Payload Type Mismatch alarm notification. + type: bool + otu_ais: + description: OTU Alarm indication signal alarm notification. + type: bool + otu_bbe_threshold: + description: OTU Background block error threshold alarm notification. + type: bool + otu_bdi: + description: OTU Backward defect indication alarm notification. + type: bool + otu_es_threshold: + description: OTU Errored Second threshold alarm notification. + type: bool + otu_fec_deg: + description: OTU Fec degraded errors alarm notification. + type: bool + otu_fec_exe: + description: OTU Fec excessive errors alarm notification. + type: bool + otu_iae: + description: OTU Incoming alignment error alarm notification. + type: bool + otu_sd: + description: OTU Signal degrade alarm notification. + type: bool + otu_ses_threshold: + description: OTU Severely Errored Second threshold alarm notification. + type: bool + otu_sf: + description: OTU Signal fail alarm notification. + type: bool + otu_ttim: + description: OTU Trace identification mismatch alarm notification. + type: bool + otu_uas_threshold: + description: OTU Unavailable Second threshold alarm notification. + type: bool + wavelength_lock: + description: Wavelength lock alarm notification. + type: bool + remote_operations: + description: Remote operations. + type: bool + rmon_alarm: + description: RMON rising and falling alarms. + type: bool + routing: + description: Routing protocol notifications. + type: bool + services: + description: Services notifications. + type: bool + startup: + description: System warm and cold starts. + type: bool + vrrp_events: + description: VRRP notifications. + type: bool + destination_port: + description: SNMP trap receiver port number + type: int + logical_system: *logical_system + routing_instance: + description: Routing instance for trap destination. + type: str + targets: + description: Targets for trap messages + type: list + elements: str + version: + description: SNMP version. + type: str + choices: ["all", "v1", "v2"] + trap_options: + description: SNMP trap options. + type: dict + suboptions: + set: + description: Set trap options. + type: bool + agent_address: + description: Agent address for v1 trap PDUs. + type: dict + suboptions: + outgoing_interface: + description: Use address on outgoing interfaces. + type: bool + context_oid: + description: Add context oid in varbind of all traps at the end. + type: bool + enterprise_oid: + description: Add snmpTrapEnterprise oid in varbind of all traps. + type: bool + logical_system: *logical_system + routing_instance: + description: Specify routing-instance. + type: str + source_address: + description: IPv4/IPv6 source address for trap PDUs. + type: dict + suboptions: + address: + description: Use specified address. + type: str + lowest_loopback: + description: Use lowest address on loopback interfaces. + type: bool + snmp_v3: + description: SNMPv3 configuration information. + type: dict + suboptions: + notify: + description: Used to select management targets for notifications as well as the type of notifications. + type: list + elements: dict + suboptions: + name: + description: Specify notify name. + type: str + tag: + description: Notifications will be sent to all targets configured with this tag. + type: str + type: + description: Notification type. + type: str + notify_filter: + description: Filters to apply to SNMP notifications. + type: list + elements: dict + suboptions: + name: + description: Specify notify filter name. + type: str + oids: + description: OID to include/exclude from notify filter. + type: list + elements: dict + suboptions: + oid: + description: Specify OID. + type: str + exclude: + description: Exclude this OID from the notify filtered. + type: bool + include: + description: Include this OID in the notify filter. + type: bool + snmp_community: + description: SNMP community and view-based access control model configuration. + type: list + elements: dict + suboptions: + community_index: + description: Unique index value in this community table entry. + type: str + security_name: + description: Security name used when performing access control. + type: str + community_name: + description: SNMPv1/v2c community name (default is same as community-index). + type: str + context: + description: Context used when performing access control. + type: str + tag: + description: Tag identifier for set of targets allowed to use this community string. + type: str + target_addresses: + description: Identifies notification targets as well as allowed management stations. + type: list + elements: dict + suboptions: + name: + description: SNMP target address name. + type: str + address: + description: SNMP target address. + type: str + address_mask: + description: Mask range of addresses for community string access control. + type: str + logical_system: + description: Logical-system name for trap destination. + type: str + port: + description: SNMP target port number. + type: int + retry_count: + description: Maximum retry count for confirmed SNMP notifications. + type: int + routing_instance: + description: Routing instance for trap destination. + type: str + tag_list: + description: SNMP tag list used to select target addresses. + type: str + target_parameters: + description: SNMPv3 target parameter name in the target parameters table. + type: str + timeout: + description: Acknowledgment timeout for confirmed SNMP notifications (seconds). + type: int + target_parameters: + description: SNMPv3 target parameter name in the target parameters table. + type: list + elements: dict + suboptions: + name: + description: SNMPv3 target parameters name. + type: str + notify_filter: + description: Notify filter with filter name to apply to notifications. + type: str + parameters: + description: Parameters used when sending notifications. + type: dict + suboptions: + message_processing_model: + description: The message processing model to be used when generating SNMP notifications. + type: str + choices: ["v1", "v2c", "v3"] + security_level: + description: Security-level used when generating SNMP notifications. + type: str + choices: ["authentication", "none", "privacy"] + security_model: + description: Security-model used when generating SNMP notifications. + type: str + choices: ["usm", "v1", "v2c"] + security_name: + description: Security name used when generating SNMP notifications. + type: str + usm: + description: User-based security model (USM) information. + type: dict + suboptions: + local_engine: + description: Local engine user configuration. + type: dict + suboptions: + users: &users + description: SNMPv3 USM user information. + type: list + elements: dict + suboptions: + name: + description: User name. + type: str + authentication_md5: + description: Configure MD5 authentication. + type: dict + suboptions: + key: + description: Encrypted key used for user authentication. + type: str + password: + description: User's authentication password + type: str + authentication_none: + description: Set no authentication for the user. + type: bool + authentication_sha: + description: Configure SHA authentication. + type: dict + suboptions: + key: + description: Encrypted key used for user authentication. + type: str + password: + description: User's authentication password + type: str + privacy_3des: + description: Configure Triple DES privacy. + type: dict + suboptions: + key: + description: Encrypted key used for user privacy. + type: str + password: + description: User's privacy password + type: str + privacy_aes128: + description: Configure AES128 privacy. + type: dict + suboptions: + key: + description: Encrypted key used for user privacy. + type: str + password: + description: User's privacy password + type: str + privacy_des: + description: Configure DES privacy. + type: dict + suboptions: + key: + description: Encrypted key used for user privacy. + type: str + password: + description: User's privacy password + type: str + privacy_none: + description: Set no privacy for the user. + type: bool + remote_engine: + description: Remote engine user configuration. + type: list + elements: dict + suboptions: + id: + description: Remote engine id. + type: str + users: *users + views: + description: Define MIB views. + type: list + elements: dict + suboptions: + name: + description: MIB view name. + type: str + oids: + description: OID include/exclude list + type: list + elements: dict + suboptions: + oid: + description: OID to include or exclude from view. + type: str + exclude: + description: Exclude this OID from the view. + type: bool + include: + description: Include this OID from the view. + type: bool + state: + description: + - The state the configuration should be left in. + - Refer to examples for more details. + type: str + choices: + - merged + - replaced + - deleted + - overridden + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +- name: Merge provided SNMP configuration into running configuration. + junipernetworks.junos.junos_snmp_server: + config: + arp: + set: true + host_name_resolution: true + client_lists: # ATTR-----2 + - name: cl1 + addresses: + - address: "192.16.1.0/24" + - address: "192.16.2.0/24" + - address: "11.11.11.11" + restrict: true + - name: cl2 + addresses: + - address: "192.16.4.0/24" + routing_instance_access: # ATTR-----3 + set: true + access_lists: + - "clv1" + - "clv2" + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "arp": { +# "host_name_resolution": true +# }, +# "client_lists": [ +# { +# "addresses": [ +# { +# "address": "192.16.1.0/24" +# }, +# { +# "address": "192.16.2.0/24" +# }, +# { +# "address": "11.11.11.11/32", +# "restrict": true +# } +# ], +# "name": "cl1" +# }, +# { +# "addresses": [ +# { +# "address": "192.16.4.0/24" +# } +# ], +# "name": "cl2" +# } +# ], +# "routing_instance_access": { +# "access_lists": [ +# "clv1", +# "clv2" +# ] +# } +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:arp><nc:host-name-resolution/></nc:arp><nc:client-list><nc:name>cl1</nc:name>" +# "<nc:client-address-list><nc:name>192.16.1.0/24</nc:name></nc:client-address-list>" +# "<nc:client-address-list><nc:name>192.16.2.0/24</nc:name></nc:client-address-list><nc:client-address-list>" +# "<nc:name>11.11.11.11</nc:name><nc:restrict/></nc:client-address-list></nc:client-list><nc:client-list>" +# "<nc:name>cl2</nc:name><nc:client-address-list><nc:name>192.16.4.0/24</nc:name></nc:client-address-list>" +# "</nc:client-list><nc:routing-instance-access><nc:access-list><nc:name>clv1</nc:name></nc:access-list>" +# "<nc:access-list><nc:name>clv2</nc:name></nc:access-list></nc:routing-instance-access></nc:snmp>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show snmp +# client-list cl1 { +# 192.16.1.0/24; +# 192.16.2.0/24; +# 11.11.11.11/32 { +# restrict; +# } +# } +# client-list cl2 { +# 192.16.4.0/24; +# } +# routing-instance-access { +# access-list { +# clv1; +# clv2; +# } +# } +# arp { +# host-name-resolution; +# } +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +# +# Using Replaced +# Before state +# ------------ +# +# vagrant@vsrx# show snmp +# client-list cl1 { +# 192.16.1.0/24; +# 192.16.2.0/24; +# 11.11.11.11/32 { +# restrict; +# } +# } +# client-list cl2 { +# 192.16.4.0/24; +# } +# routing-instance-access { +# access-list { +# clv1; +# clv2; +# } +# } +# arp { +# host-name-resolution; +# } +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } + +- name: Replaced running SNMP server configuration with provided configuration + junipernetworks.junos.junos_snmp_server: + config: + contact: "ansiblesupport11@redhat.com" + customization: + ether_stats_ifd_only: True + description: "Local SNMP Server" + engine_id: + local: "local1" + use_default_ip_address: True + use_mac_address: True + filter_duplicates: True + filter_interfaces: + set: True + all_internal_interfaces: True + interfaces: + - "eth1" + - "eth2" + state: replaced +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "contact": "ansiblesupport11@redhat.com", +# "customization": { +# "ether_stats_ifd_only": true +# }, +# "description": "Local SNMP Server", +# "engine_id": { +# "use_mac_address": true +# }, +# "filter_duplicates": true, +# "filter_interfaces": { +# "all_internal_interfaces": true, +# "interfaces": [ +# "eth1", +# "eth2" +# ] +# } +# }, +# "before": +# { +# "arp": { +# "host_name_resolution": true +# }, +# "client_lists": [ +# { +# "addresses": [ +# { +# "address": "192.16.1.0/24" +# }, +# { +# "address": "192.16.2.0/24" +# }, +# { +# "address": "11.11.11.11/32", +# "restrict": true +# } +# ], +# "name": "cl1" +# }, +# { +# "addresses": [ +# { +# "address": "192.16.4.0/24" +# } +# ], +# "name": "cl2" +# } +# ], +# "routing_instance_access": { +# "access_lists": [ +# "clv1", +# "clv2" +# ] +# } +# }, +# "changed": true, +# "commands": [ +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"/>", +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" delete=\"delete\"/>", +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:contact>ansiblesupport11@redhat.com</nc:contact><nc:customization>" +# "<nc:ether-stats-ifd-only/></nc:customization><nc:description>Local SNMP Server</nc:description>" +# "<nc:engine-id><nc:local>local1</nc:local><nc:use-default-ip-address/><nc:use-mac-address/>" +# "</nc:engine-id><nc:filter-duplicates/><nc:filter-interfaces><nc:all-internal-interfaces/><nc:interfaces>" +# "<nc:name>eth1</nc:name></nc:interfaces><nc:interfaces><nc:name>eth2</nc:name></nc:interfaces>" +# "</nc:filter-interfaces></nc:snmp>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +# vagrant@vsrx# show snmp +# description "Local SNMP Server"; +# contact "ansiblesupport11@redhat.com"; +# filter-interfaces { +# interfaces { +# eth1; +# eth2; +# } +# all-internal-interfaces; +# } +# filter-duplicates; +# engine-id { +# use-mac-address; +# } +# customization { +# ether-stats-ifd-only; +# } + +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show snmp +# client-list cl1 { +# 192.16.1.0/24; +# 192.16.2.0/24; +# 11.11.11.11/32 { +# restrict; +# } +# } +# client-list cl2 { +# 192.16.4.0/24; +# } +# routing-instance-access { +# access-list { +# clv1; +# clv2; +# } +# } +# arp { +# host-name-resolution; +# } +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +- name: Override running SNMP server configuration with provided configuration + junipernetworks.junos.junos_snmp_server: + config: + contact: "ansiblesupport11@redhat.com" + customization: + ether_stats_ifd_only: True + description: "Local SNMP Server" + engine_id: + local: "local1" + use_default_ip_address: True + use_mac_address: True + filter_duplicates: True + filter_interfaces: + set: True + all_internal_interfaces: True + interfaces: + - "eth1" + - "eth2" + state: overridden +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": { +# "contact": "ansiblesupport11@redhat.com", +# "customization": { +# "ether_stats_ifd_only": true +# }, +# "description": "Local SNMP Server", +# "engine_id": { +# "use_mac_address": true +# }, +# "filter_duplicates": true, +# "filter_interfaces": { +# "all_internal_interfaces": true, +# "interfaces": [ +# "eth1", +# "eth2" +# ] +# } +# }, +# "before": +# { +# "arp": { +# "host_name_resolution": true +# }, +# "client_lists": [ +# { +# "addresses": [ +# { +# "address": "192.16.1.0/24" +# }, +# { +# "address": "192.16.2.0/24" +# }, +# { +# "address": "11.11.11.11/32", +# "restrict": true +# } +# ], +# "name": "cl1" +# }, +# { +# "addresses": [ +# { +# "address": "192.16.4.0/24" +# } +# ], +# "name": "cl2" +# } +# ], +# "routing_instance_access": { +# "access_lists": [ +# "clv1", +# "clv2" +# ] +# } +# }, +# "changed": true, +# "commands": [ +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"/>", +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" delete=\"delete\"/>", +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:contact>ansiblesupport11@redhat.com</nc:contact><nc:customization>" +# "<nc:ether-stats-ifd-only/></nc:customization><nc:description>Local SNMP Server</nc:description>" +# "<nc:engine-id><nc:local>local1</nc:local><nc:use-default-ip-address/><nc:use-mac-address/>" +# "</nc:engine-id><nc:filter-duplicates/><nc:filter-interfaces><nc:all-internal-interfaces/><nc:interfaces>" +# "<nc:name>eth1</nc:name></nc:interfaces><nc:interfaces><nc:name>eth2</nc:name></nc:interfaces>" +# "</nc:filter-interfaces></nc:snmp>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +# vagrant@vsrx# show snmp +# description "Local SNMP Server"; +# contact "ansiblesupport11@redhat.com"; +# filter-interfaces { +# interfaces { +# eth1; +# eth2; +# } +# all-internal-interfaces; +# } +# filter-duplicates; +# engine-id { +# use-mac-address; +# } +# customization { +# ether-stats-ifd-only; +# } +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +# vagrant@vsrx# show snmp +# description "Local SNMP Server"; +# contact "ansiblesupport11@redhat.com"; +# filter-interfaces { +# interfaces { +# eth1; +# eth2; +# } +# all-internal-interfaces; +# } +# filter-duplicates; +# engine-id { +# use-mac-address; +# } +# customization { +# ether-stats-ifd-only; +# } +# +- name: Delete running SNMP server configuration + junipernetworks.junos.junos_snmp_server: + config: + state: deleted +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": {}, +# "before": { +# "contact": "ansiblesupport11@redhat.com", +# "customization": { +# "ether_stats_ifd_only": true +# }, +# "description": "Local SNMP Server", +# "engine_id": { +# "use_mac_address": true +# }, +# "filter_duplicates": true, +# "filter_interfaces": { +# "all_internal_interfaces": true, +# "interfaces": [ +# "eth1", +# "eth2" +# ] +# } +# }, +# "changed": true, +# "commands": [ +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"/>", +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" delete=\"delete\"/>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show routing-instances +# clv1 { +# description clv1; +# } +# clv2 { +# description clv2; +# } +# vagrant@vsrx# show snmp +# description "Local SNMP Server"; +# contact "ansiblesupport11@redhat.com"; +# filter-interfaces { +# interfaces { +# eth1; +# eth2; +# } +# all-internal-interfaces; +# } +# filter-duplicates; +# engine-id { +# use-mac-address; +# } +# customization { +# ether-stats-ifd-only; +# } +# +- name: Gather running SNMP server configuration + junipernetworks.junos.junos_snmp_server: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": { +# "contact": "ansiblesupport11@redhat.com", +# "customization": { +# "ether_stats_ifd_only": true +# }, +# "description": "Local SNMP Server", +# "engine_id": { +# "use_mac_address": true +# }, +# "filter_duplicates": true, +# "filter_interfaces": { +# "all_internal_interfaces": true, +# "interfaces": [ +# "eth1", +# "eth2" +# ] +# } +# }, +# "changed": false, +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_snmp_server: + config: + arp: + set: true + host_name_resolution: true + routing_instance_access: # ATTR-----3 + set: true + access_lists: + - "clv1" + - "clv2" + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +# "<nc:arp><nc:host-name-resolution/></nc:arp><nc:routing-instance-access>" +# "<nc:access-list><nc:name>clv1</nc:name></nc:access-list><nc:access-list><nc:name>clv2</nc:name>" +# "</nc:access-list></nc:routing-instance-access></nc:snmp>" +# ] +# +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <system xmlns="http://yang.juniper.net/junos-es/conf/system"> +# <snmp> +# <client-list> +# <name>cl1</name> +# <client-address-list> +# <name>192.16.1.0/24</name> +# </client-address-list> +# <client-address-list> +# <name>192.16.2.0/24</name> +# </client-address-list> +# <client-address-list> +# <name>11.11.11.11/32</name> +# <restrict/> +# </client-address-list> +# </client-list> +# <client-list> +# <name>cl2</name> +# <client-address-list> +# <name>192.16.4.0/24</name> +# </client-address-list> +# </client-list> +# <routing-instance-access> +# <access-list> +# <name>clv1</name> +# </access-list> +# <access-list> +# <name>clv2</name> +# </access-list> +# </routing-instance-access> +# <arp> +# <host-name-resolution/> +# </arp> +# </snmp> +# </system> +# </configuration> +# </rpc-reply> +# +- name: Parse SNMP server running config + junipernetworks.junos.junos_snmp_server: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "arp": { +# "host_name_resolution": true +# }, +# "client_lists": [ +# { +# "addresses": [ +# { +# "address": "192.16.1.0/24" +# }, +# { +# "address": "192.16.2.0/24" +# }, +# { +# "address": "11.11.11.11/32", +# "restrict": true +# } +# ], +# "name": "cl1" +# }, +# { +# "addresses": [ +# { +# "address": "192.16.4.0/24" +# } +# ], +# "name": "cl2" +# } +# ], +# "routing_instance_access": { +# "access_lists": [ +# "clv1", +# "clv2" +# ] +# } +# } +# +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:arp><nc:host-name-resolution/></nc:arp><nc:routing-instance-access>"', + '<nc:snmp xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.snmp_server.snmp_server import ( + Snmp_serverArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.snmp_server.snmp_server import ( + Snmp_server, +) + + +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=Snmp_serverArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Snmp_server(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_static_routes.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_static_routes.py new file mode 100644 index 000000000..7efe112a7 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_static_routes.py @@ -0,0 +1,308 @@ +#!/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 junos_static_routes +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_static_routes +short_description: Static routes resource module +description: This module provides declarative management of static routes on Juniper + JUNOS devices +version_added: 1.0.0 +author: Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +- xmltodict (>=0.12) +notes: +- This module requires the netconf system service be enabled on the device being managed. +- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- Tested against JunOS v18.4R1 +options: + config: + description: A dictionary of static routes options + type: list + elements: dict + suboptions: + vrf: + description: + - Virtual Routing and Forwarding (VRF) name + type: str + address_families: + description: + - Address family to use for the static routes + elements: dict + type: list + suboptions: + afi: + description: + - afi to use for the static routes + type: str + required: true + choices: + - ipv4 + - ipv6 + routes: + description: + - Static route configuration + elements: dict + type: list + suboptions: + dest: + description: + - Static route destination including prefix + type: str + next_hop: + elements: dict + type: list + description: + - Next hop to destination + suboptions: + forward_router_address: + description: + - List of next hops + type: str + metric: + description: + - Metric value for the static route + 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 Junos device + by executing the command B(show routing-options). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result + type: str + state: + description: + - The state the configuration should be left in + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using deleted + +# Before state +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# route 10.200.16.75/24 next-hop 10.200.16.2; +# } + +- name: Delete provided configuration (default operation is merge) + junipernetworks.junos.junos_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 10.200.16.75/24 + next_hop: + - forward_router_address: 10.200.16.2 + state: deleted + +# After state: +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# } + +# Using merged + +# Before state +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# } + +- name: Merge provided configuration with device configuration (default operation + is merge) + junipernetworks.junos.junos_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 10.200.16.75/24 + next_hop: + - forward_router_address: 10.200.16.2 + state: merged + +# After state: +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# route 10.200.16.75/24 next-hop 10.200.16.2; +# } + +# Using overridden + +# Before state +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.0.1; +# } + +- name: Override provided configuration with device configuration (default operation + is merge) + junipernetworks.junos.junos_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 10.200.16.75/24 + next_hop: + - forward_router_address: 10.200.16.2 + state: overridden + +# After state: +# ------------ +# +# admin# show routing-options +# static { +# route 10.200.16.75/24 next-hop 10.200.16.2; +# } + +# Using replaced + +# Before state +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 172.16.1.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# } + +- name: Replace provided configuration with device configuration (default operation + is merge) + junipernetworks.junos.junos_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 192.168.47.0/24 + next_hop: + - forward_router_address: 10.200.16.2 + state: replaced + +# After state: +# ------------ +# +# admin# show routing-options +# static { +# route 192.168.47.0/24 next-hop 10.200.16.2; +# route 192.168.16.0/24 next-hop 172.16.1.2; +# } + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: str + 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: str + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.static_routes.static_routes import ( + Static_routesArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.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", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + module = AnsibleModule( + argument_spec=Static_routesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + result = Static_routes(module).execute_module() + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_system.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_system.py new file mode 100644 index 000000000..c70ded66f --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_system.py @@ -0,0 +1,207 @@ +#!/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: junos_system +author: Ganesh Nalawade (@ganeshrn) +short_description: Manage the system attributes on Juniper JUNOS devices +description: +- This module provides declarative management of node system attributes on Juniper + JUNOS devices. It provides an option to configure host system parameters or remove + those parameters from the device active configuration. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + hostname: + description: + - Configure the device hostname parameter. This option takes an ASCII string value. + type: str + domain_name: + description: + - Configure the IP domain name on the remote device to the provided value. Value + should be in the dotted name form and will be appended to the C(hostname) to + create a fully-qualified domain name. + type: 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 + name_servers: + description: + - List of DNS name servers by IP address to use to perform name resolution lookups. This + argument accepts either a list of DNS servers See examples. + type: list + elements: 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 + type: str + default: present + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + default: true + type: bool +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: configure hostname and domain name + junipernetworks.junos.junos_system: + hostname: junos01 + domain_name: test.example.com + domain-search: + - ansible.com + - redhat.com + - juniper.net + +- name: remove configuration + junipernetworks.junos.junos_system: + state: absent + +- name: configure name servers + junipernetworks.junos.junos_system: + name_servers: + - 8.8.8.8 + - 8.8.4.4 +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: str + sample: > + [edit system] + + host-name test; + + domain-name ansible.com; + + domain-search redhat.com; + [edit system name-server] + 172.26.1.1 { ... } + + 8.8.8.8; +""" +import collections + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + map_obj_to_ele, + map_params_to_obj, + tostring, +) + + +USE_PERSISTENT_CONNECTION = True + + +def validate_param_values(module, obj): + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(module.params.get(key), module) + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + hostname=dict(), + domain_name=dict(), + domain_search=dict(type="list", elements="str"), + name_servers=dict(type="list", elements="str"), + state=dict(choices=["present", "absent"], default="present"), + active=dict(default=True, type="bool"), + ) + + params = ["hostname", "domain_name", "domain_search", "name_servers"] + required_if = [ + ("state", "present", params, True), + ("state", "absent", params, True), + ("state", "active", params, True), + ("state", "suspend", params, True), + ] + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + warnings = list() + result = {"changed": False} + + if warnings: + result["warnings"] = warnings + + top = "system" + + param_to_xpath_map = collections.OrderedDict() + param_to_xpath_map.update( + [ + ("hostname", {"xpath": "host-name", "leaf_only": True}), + ("domain_name", {"xpath": "domain-name", "leaf_only": True}), + ( + "domain_search", + { + "xpath": "domain-search", + "leaf_only": True, + "value_req": True, + }, + ), + ("name_servers", {"xpath": "name-server/name", "is_key": True}), + ], + ) + + validate_param_values(module, param_to_xpath_map) + + want = map_params_to_obj(module, param_to_xpath_map) + ele = map_obj_to_ele(module, want, top) + + with locked_config(module): + diff = load_config(module, tostring(ele), warnings, action="merge") + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_user.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_user.py new file mode 100644 index 000000000..0e9ee8ad9 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_user.py @@ -0,0 +1,458 @@ +#!/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: junos_user +author: Peter Sprygada (@privateip) +short_description: Manage local user accounts on Juniper JUNOS devices +description: +- This module manages locally configured user accounts on remote network devices running + the JUNOS operating system. It provides a set of arguments for creating, removing + and updating locally defined accounts +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + aggregate: + description: + - The C(aggregate) argument defines a list of users to be configured on the remote + device. The list of users will be compared against the current users and only + changes will be added or removed from the device configuration. This argument + is mutually exclusive with the name argument. + aliases: + - users + - collection + type: list + elements: dict + suboptions: + name: + description: + - The C(name) argument defines the username of the user to be created on the system. This + argument must follow appropriate usernaming conventions for the target device + running JUNOS. This argument is mutually exclusive with the C(aggregate) argument. + required: true + type: str + full_name: + description: + - The C(full_name) argument provides the full name of the user account to be created + on the remote device. This argument accepts any text string value. + type: str + role: + description: + - The C(role) argument defines the role of the user account on the remote system. User + accounts can have more than one role configured. + type: str + choices: + - operator + - read-only + - super-user + - unauthorized + sshkey: + description: + - The C(sshkey) argument defines the public SSH key to be configured for the user + account on the remote system. This argument must be a valid SSH key + type: str + encrypted_password: + description: + - The C(encrypted_password) argument set already hashed password for the user + account on the remote system. + type: str + purge: + description: + - The C(purge) argument instructs the module to consider the users definition + absolute. It will remove any previously configured users on the device with + the exception of the current defined set of aggregate. + type: bool + default: false + state: + description: + - The C(state) argument configures the state of the user definitions as it relates + to the device operational configuration. When set to I(present), the user should + be configured in the device active configuration and when set to I(absent) the + user should not be in the device active configuration + type: str + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + type: bool + name: + description: + - The C(name) argument defines the username of the user to be created on the system. This + argument must follow appropriate usernaming conventions for the target device + running JUNOS. This argument is mutually exclusive with the C(aggregate) argument. + type: str + full_name: + description: + - The C(full_name) argument provides the full name of the user account to be created + on the remote device. This argument accepts any text string value. + type: str + role: + description: + - The C(role) argument defines the role of the user account on the remote system. User + accounts can have more than one role configured. + type: str + choices: + - operator + - read-only + - super-user + - unauthorized + sshkey: + description: + - The C(sshkey) argument defines the public SSH key to be configured for the user + account on the remote system. This argument must be a valid SSH key + type: str + encrypted_password: + description: + - The C(encrypted_password) argument set already hashed password for the user + account on the remote system. + type: str + purge: + description: + - The C(purge) argument instructs the module to consider the users definition + absolute. It will remove any previously configured users on the device with + the exception of the current defined set of aggregate. + type: bool + default: no + state: + description: + - The C(state) argument configures the state of the user definitions as it relates + to the device operational configuration. When set to I(present), the user should + be configured in the device active configuration and when set to I(absent) the + user should not be in the device active configuration + type: str + default: present + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + type: bool + default: yes +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: create new user account + junipernetworks.junos.junos_user: + name: ansible + role: super-user + sshkey: "{{ lookup('file', '~/.ssh/ansible.pub') }}" + state: present + +- name: remove a user account + junipernetworks.junos.junos_user: + name: ansible + state: absent + +- name: remove all user accounts except ansible + junipernetworks.junos.junos_user: + aggregate: + - name: ansible + purge: yes + +- name: set user password + junipernetworks.junos.junos_user: + name: ansible + role: super-user + encrypted_password: "{{ 'my-password' | password_hash('sha512') }}" + state: present + +- name: Create list of users + junipernetworks.junos.junos_user: + aggregate: + - {name: test_user1, full_name: test_user2, role: operator, state: present} + - {name: test_user2, full_name: test_user2, role: read-only, state: present} + +- name: Delete list of users + junipernetworks.junos.junos_user: + aggregate: + - {name: test_user1, full_name: test_user2, role: operator, state: absent} + - {name: test_user2, full_name: test_user2, role: read-only, state: absent} +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: str + sample: > + [edit system login] + + user test-user { + + uid 2005; + + class read-only; + + } +""" +from copy import deepcopy +from functools import partial + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + get_connection, + load_config, + locked_config, + tostring, +) + + +try: + from lxml.etree import Element, SubElement +except ImportError: + from xml.etree.ElementTree import Element, SubElement + +ROLES = ["operator", "read-only", "super-user", "unauthorized"] +USE_PERSISTENT_CONNECTION = True + + +def handle_purge(module, want): + want_users = [item["name"] for item in want] + element = Element("system") + login = SubElement(element, "login") + + conn = get_connection(module) + try: + reply = conn.execute_rpc( + tostring(Element("get-configuration")), + ignore_warning=False, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + users = reply.xpath("configuration/system/login/user/name") + if users: + for item in users: + name = item.text + if name not in want_users and name != "root": + user = SubElement(login, "user", {"operation": "delete"}) + SubElement(user, "name").text = name + if element.xpath("/system/login/user/name"): + return element + + +def map_obj_to_ele(module, want): + element = Element("system") + login = SubElement(element, "login") + + for item in want: + if item["state"] != "present": + if item["name"] == "root": + module.fail_json(msg="cannot delete the 'root' account.") + operation = "delete" + else: + operation = "merge" + + if item["name"] != "root": + user = SubElement(login, "user", {"operation": operation}) + SubElement(user, "name").text = item["name"] + else: + user = auth = SubElement( + element, + "root-authentication", + {"operation": operation}, + ) + + if operation == "merge": + if item["name"] == "root" and (not item["active"] or item["role"] or item["full_name"]): + module.fail_json( + msg="'root' account cannot be deactivated or be assigned a role and a full name", + ) + + if item["active"]: + user.set("active", "active") + else: + user.set("inactive", "inactive") + + if item["role"]: + SubElement(user, "class").text = item["role"] + + if item.get("full_name"): + SubElement(user, "full-name").text = item["full_name"] + + if item.get("sshkey"): + auth = SubElement(user, "authentication") + if "ssh-rsa" in item["sshkey"]: + ssh_rsa = SubElement(auth, "ssh-rsa") + elif "ssh-dss" in item["sshkey"]: + ssh_rsa = SubElement(auth, "ssh-dsa") + elif "ecdsa-sha2" in item["sshkey"]: + ssh_rsa = SubElement(auth, "ssh-ecdsa") + elif "ssh-ed25519" in item["sshkey"]: + ssh_rsa = SubElement(auth, "ssh-ed25519") + SubElement(ssh_rsa, "name").text = item["sshkey"] + + if item.get("encrypted_password"): + auth = SubElement(user, "authentication") + SubElement(auth, "encrypted-password").text = item["encrypted_password"] + + return element + + +def get_param_value(key, item, module): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = module.params[key] + + # if key does exist, do a type check on it to validate it + else: + value_type = module.argument_spec[key].get("type", "str") + type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] + type_checker(item[key]) + value = item[key] + + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if all((value, validator)): + validator(value, module) + + return value + + +def map_params_to_obj(module): + aggregate = module.params["aggregate"] + if not aggregate: + if not module.params["name"] and module.params["purge"]: + return list() + elif not module.params["name"]: + module.fail_json(msg="missing required argument: name") + else: + collection = [{"name": module.params["name"]}] + else: + collection = list() + for item in aggregate: + if not isinstance(item, dict): + collection.append({"username": item}) + elif "name" not in item: + module.fail_json(msg="missing required argument: name") + else: + collection.append(item) + + objects = list() + + for item in collection: + get_value = partial(get_param_value, item=item, module=module) + item.update( + { + "full_name": get_value("full_name"), + "role": get_value("role"), + "encrypted_password": get_value("encrypted_password"), + "sshkey": get_value("sshkey"), + "state": get_value("state"), + "active": get_value("active"), + }, + ) + + for key, value in iteritems(item): + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if all((value, validator)): + validator(value, module) + + objects.append(item) + + return objects + + +def main(): + """main entry point for module execution""" + element_spec = dict( + name=dict(), + full_name=dict(), + role=dict(choices=ROLES), + encrypted_password=dict(no_log=True), + sshkey=dict(no_log=False), + state=dict(choices=["present", "absent"], default="present"), + purge=dict(type="bool", default=False), + active=dict(type="bool", default=True), + ) + + aggregate_spec = deepcopy(element_spec) + aggregate_spec["name"] = dict(required=True) + + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + aliases=["collection", "users"], + ), + purge=dict(default=False, type="bool"), + ) + aggregate_spec["purge"] = dict(type="bool", default=False) + + argument_spec.update(element_spec) + + mutually_exclusive = [["aggregate", "name"]] + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + warnings = list() + result = {"changed": False, "warnings": warnings} + + want = map_params_to_obj(module) + ele = map_obj_to_ele(module, want) + + purge_request = None + if module.params["purge"]: + purge_request = handle_purge(module, want) + + with locked_config(module): + if purge_request: + load_config( + module, + tostring(purge_request), + warnings, + action="replace", + ) + diff = load_config(module, tostring(ele), warnings, action="merge") + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_vlans.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_vlans.py new file mode 100644 index 000000000..32f974df6 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_vlans.py @@ -0,0 +1,467 @@ +#!/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 junos_vlans +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: junos_vlans +short_description: VLANs resource module +description: +- This module creates and manages VLAN configurations on Junos OS. +version_added: 1.0.0 +author: Daniel Mellado (@dmellado) +requirements: +- ncclient (>=v0.6.4) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed +- Tested against Junos OS 18.4R1 +- This module works with connection C(netconf). +- See L(the Junos OS Platform Options,https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html). +options: + config: + description: A dictionary of Vlan options + type: list + elements: dict + suboptions: + vlan_id: + description: + - IEEE 802.1q VLAN identifier for VLAN (1..4094). + type: int + name: + description: + - Name of VLAN. + type: str + required: true + description: + description: + - Text description of VLANs + type: str + l3_interface: + description: + - Name of logical layer 3 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 Junos device + by executing the command B(show vlans). + - 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 + - gathered + - parsed + - rendered + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state +# ------------ +# +# vagrant@vsrx# show vlans +# +# [edit] + +- name: Merge provided Junos vlans config with running-config + junipernetworks.junos.junos_vlans: + config: + - name: vlan1 + vlan_id: 1 + - name: vlan2 + vlan_id: 2 + l3_interface: irb.12 + state: merged +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": [ +# { +# "name": "vlan1", +# "vlan_id": 1 +# }, +# { +# "l3_interface": "irb.12", +# "name": "vlan2", +# "vlan_id": 2 +# } +# ], +# "before": [], +# "changed": true, +# "commands": [ +# "<nc:vlans xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">" +# "<nc:vlan><nc:name>vlan1</nc:name><nc:vlan-id>1</nc:vlan-id></nc:vlan>" +# "<nc:vlan><nc:name>vlan2</nc:name><nc:vlan-id>2</nc:vlan-id><nc:l3-interface>irb.12</nc:l3-interface>" +# "</nc:vlan></nc:vlans>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show vlans +# vlan1 { +# vlan-id 1; +# } +# vlan2 { +# vlan-id 2; +# l3-interface irb.12; +# } + +# Using replaced +# +# Before state +# ------------ +# +# vagrant@vsrx# show vlans +# vlan1 { +# vlan-id 1; +# } +# vlan2 { +# vlan-id 2; +# l3-interface irb.12; +# } + +- name: Replace Junos vlans running-config with the provided config + junipernetworks.junos.junos_vlans: + config: + - name: vlan1 + vlan_id: 11 + l3_interface: irb.10 + + - name: vlan2 + vlan_id: 2 + state: replaced +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": [ +# { +# "l3_interface": "irb.10", +# "name": "vlan1", +# "vlan_id": 11 +# }, +# { +# "name": "vlan2", +# "vlan_id": 2 +# } +# ], +# "before": [ +# { +# "name": "vlan1", +# "vlan_id": 1 +# }, +# { +# "l3_interface": "irb.12", +# "name": "vlan2", +# "vlan_id": 2 +# } +# ], +# "changed": true, +# "commands": [ +# "<nc:vlans xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">" +# "<nc:vlan delete="delete"><nc:name>vlan1</nc:name></nc:vlan>" +# "<nc:vlan delete="delete"><nc:name>vlan2</nc:name></nc:vlan>" +# "<nc:vlan><nc:name>vlan1</nc:name><nc:vlan-id>11</nc:vlan-id>" +# "<nc:l3-interface>irb.10</nc:l3-interface></nc:vlan><nc:vlan>" +# "<nc:name>vlan2</nc:name><nc:vlan-id>2</nc:vlan-id></nc:vlan></nc:vlans>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show vlans +# vlan1 { +# vlan-id 11; +# l3-interface irb.10; +# } +# vlan2 { +# vlan-id 2; +# } +# +# Using overridden +# +# Before state +# ------------ +# +# vagrant@vsrx# show vlans +# vlan1 { +# vlan-id 11; +# l3-interface irb.10; +# } +# vlan2 { +# vlan-id 2; +# } +- name: Override Junos running-config with provided config + junipernetworks.junos.junos_vlans: + config: + - name: vlan3 + vlan_id: 3 + l3_interface: irb.13 + state: overridden +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": [ +# { +# "l3_interface": "irb.13", +# "name": "vlan3", +# "vlan_id": 3 +# } +# ], +# "before": [ +# { +# "l3_interface": "irb.10", +# "name": "vlan1", +# "vlan_id": 11 +# }, +# { +# "name": "vlan2", +# "vlan_id": 2 +# } +# ], +# "changed": true, +# "commands": [ +# "<nc:vlans xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">" +# "<nc:vlan delete="delete"><nc:name>vlan1</nc:name></nc:vlan><nc:vlan delete="delete">" +# "<nc:name>vlan2</nc:name></nc:vlan><nc:vlan><nc:name>vlan3</nc:name><nc:vlan-id>3</nc:vlan-id>" +# "<nc:l3-interface>irb.13</nc:l3-interface></nc:vlan></nc:vlans>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show vlans +# vlan3 { +# vlan-id 3; +# l3-interface irb.13; +# } +# +# Using deleted +# +# Before state +# ------------ +# +# vagrant@vsrx# show vlans +# vlan3 { +# vlan-id 3; +# l3-interface irb.13; +# } +- name: Delete specific vlan + junipernetworks.junos.junos_vlans: + config: + - name: vlan3 + state: deleted +# ------------------------- +# Module Execution Result +# ------------------------- +# "after": [], +# "changed": true, +# "commands": [ +# "<nc:vlans xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> +# "<nc:vlan delete="delete"><nc:name>vlan3</nc:name></nc:vlan></nc:vlans>" +# ] +# After state +# ----------- +# +# vagrant@vsrx# show vlans +# vlan1 { +# vlan-id 11; +# l3-interface irb.10; +# } +# vlan2 { +# vlan-id 2; +# } + + +- name: Gather running vlans configuration + junipernetworks.junos.junos_vlans: + state: gathered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "gathered": [ +# { +# "l3_interface": "irb.10", +# "name": "vlan1", +# "vlan_id": 11 +# }, +# { +# "name": "vlan2", +# "vlan_id": 2 +# } +# ], +# "changed": false, +# +# Using rendered +# +# Before state +# ------------ +# +- name: Render xml for provided facts. + junipernetworks.junos.junos_vlans: + config: + - name: vlan1 + vlan_id: 1 + + - name: vlan2 + vlan_id: 2 + l3_interface: irb.12 + state: rendered +# +# ------------------------- +# Module Execution Result +# ------------------------- +# "rendered": [ +# "<nc:vlans xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">" +# "<nc:vlan><nc:name>vlan1</nc:name><nc:vlan-id>1</nc:vlan-id></nc:vlan>" +# "<nc:vlan><nc:name>vlan2</nc:name><nc:vlan-id>2</nc:vlan-id><nc:l3-interface>irb.12</nc:l3-interface>" +# "</nc:vlan></nc:vlans>" +# ] +# Using parsed +# parsed.cfg +# ------------ +# <?xml version="1.0" encoding="UTF-8"?> +# <rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f"> +# <configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC"> +# <version>18.4R1-S2.4</version> +# <vlans> +# <vlan> +# <name>vlan1</name> +# <vlan-id>1</vlan-id> +# </vlan> +# <vlan> +# <name>vlan2</name> +# <vlan-id>2</vlan-id> +# <l3-interface>irb.12</l3-interface> +# </vlan> +# </vlans> +# </configuration> +# </rpc-reply> + +- name: Parse routing instance running config + junipernetworks.junos.junos_vlans: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "name": "vlan1", +# "vlan_id": 1 +# }, +# { +# "l3_interface": "irb.12", +# "name": "vlan2", +# "vlan_id": 2 +# } +# ] +# +""" + +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The configuration as structured data after module completion. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['<nc:vlans xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <nc:vlan><nc:name>vlan1</nc:name><nc:vlan-id>1</nc:vlan-id> + </nc:vlan><nc:vlan><nc:name>vlan2</nc:name><nc:vlan-id>2</nc:vlan-id> + <nc:l3-interface>irb.12</nc:l3-interface></nc:vlan></nc:vlans>', 'xml 2', 'xml 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.argspec.vlans.vlans import ( + VlansArgs, +) +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.config.vlans.vlans import ( + Vlans, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + module = AnsibleModule( + argument_spec=VlansArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Vlans(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/modules/junos_vrf.py b/ansible_collections/junipernetworks/junos/plugins/modules/junos_vrf.py new file mode 100644 index 000000000..e2332d1c5 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/modules/junos_vrf.py @@ -0,0 +1,342 @@ +#!/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: junos_vrf +author: Ganesh Nalawade (@ganeshrn) +short_description: Manage the VRF definitions on Juniper JUNOS devices +description: +- This module provides declarative management of VRF definitions on Juniper JUNOS + devices. It allows playbooks to manage individual or the entire VRF collection. +version_added: 1.0.0 +extends_documentation_fragment: +- junipernetworks.junos.junos +options: + name: + description: + - The name of the VRF definition to be managed on the remote IOS device. The + VRF definition name is an ASCII string name used to uniquely identify the VRF. This + argument is mutually exclusive with the C(aggregate) argument + type: str + description: + description: + - Provides a short description of the VRF definition in the current active configuration. The + VRF definition value accepts alphanumeric characters used to provide additional + information about the VRF. + type: str + rd: + description: + - The router-distinguisher value uniquely identifies the VRF to routing processes + on the remote IOS system. The RD value takes the form of C(A:B) where C(A) + and C(B) are both numeric values. + type: list + elements: str + interfaces: + description: + - Identifies the set of interfaces that should be configured in the VRF. Interfaces + must be routed interfaces in order to be placed into a VRF. + type: list + elements: str + target: + description: + - It configures VRF target community configuration. The target value takes the + form of C(target:A:B) where C(A) and C(B) are both numeric values. + type: list + elements: str + table_label: + description: + - Causes JUNOS to allocate a VPN label per VRF rather than per VPN FEC. This allows + for forwarding of traffic to directly connected subnets, COS Egress filtering + etc. + default: true + type: bool + aggregate: + description: + - The set of VRF definition objects to be configured on the remote JUNOS device. Ths + list entries can either be the VRF name or a hash of VRF definitions and attributes. This + argument is mutually exclusive with the C(name) argument. + type: list + elements: dict + suboptions: + name: + description: + - The name of the VRF definition to be managed on the remote IOS device. The + VRF definition name is an ASCII string name used to uniquely identify the VRF. This + argument is mutually exclusive with the C(aggregate) argument + required: true + type: str + description: + description: + - Provides a short description of the VRF definition in the current active configuration. The + VRF definition value accepts alphanumeric characters used to provide additional + information about the VRF. + type: str + rd: + description: + - The router-distinguisher value uniquely identifies the VRF to routing processes + on the remote IOS system. The RD value takes the form of C(A:B) where C(A) + and C(B) are both numeric values. + type: list + elements: str + interfaces: + description: + - Identifies the set of interfaces that should be configured in the VRF. Interfaces + must be routed interfaces in order to be placed into a VRF. + type: list + elements: str + target: + description: + - It configures VRF target community configuration. The target value takes the + form of C(target:A:B) where C(A) and C(B) are both numeric values. + type: list + elements: str + table_label: + description: + - Causes JUNOS to allocate a VPN label per VRF rather than per VPN FEC. This allows + for forwarding of traffic to directly connected subnets, COS Egress filtering + etc. + type: bool + state: + description: + - Configures the state of the VRF definition as it relates to the device operational + configuration. When set to I(present), the VRF should be configured in the + device active configuration and when set to I(absent) the VRF should not be + in the device active configuration + type: str + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + type: bool + state: + description: + - Configures the state of the VRF definition as it relates to the device operational + configuration. When set to I(present), the VRF should be configured in the + device active configuration and when set to I(absent) the VRF should not be + in the device active configuration + type: str + default: present + choices: + - present + - absent + active: + description: + - Specifies whether or not the configuration is active or deactivated + default: true + type: bool +requirements: +- ncclient (>=v0.5.2) +notes: +- This module requires the netconf system service be enabled on the remote device + being managed. +- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4. +- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html). +- This module also works with C(local) connections for legacy playbooks. +""" + +EXAMPLES = """ +- name: Configure vrf configuration + junipernetworks.junos.junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 192.0.2.1:10 + target: target:65514:113 + state: present + +- name: Remove vrf configuration + junipernetworks.junos.junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 192.0.2.1:10 + target: target:65514:113 + state: absent + +- name: Deactivate vrf configuration + junipernetworks.junos.junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 192.0.2.1:10 + target: target:65514:113 + active: false + +- name: Activate vrf configuration + junipernetworks.junos.junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 192.0.2.1:10 + target: target:65514:113 + active: true + +- name: Create vrf using aggregate + junipernetworks.junos.junos_vrf: + aggregate: + - name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 - ge-0/0/2 + rd: 192.0.2.1:10 + target: target:65514:113 + - name: test-2 + description: test-vrf-2 + interfaces: + - ge-0/0/4 + - ge-0/0/5 + rd: 192.0.2.2:10 + target: target:65515:114 + state: present +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: str + sample: > + [edit routing-instances] + + test-1 { + + description test-vrf-1; + + instance-type vrf; + + interface ge-0/0/2.0; + + interface ge-0/0/3.0; + + route-distinguisher 192.0.2.1:10; + + vrf-target target:65514:113; + + } +""" +import collections + +from copy import deepcopy + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + +from ansible_collections.junipernetworks.junos.plugins.module_utils.network.junos.junos import ( + commit_configuration, + discard_changes, + load_config, + locked_config, + map_obj_to_ele, + map_params_to_obj, + to_param_list, + tostring, +) + + +USE_PERSISTENT_CONNECTION = True + + +def main(): + """main entry point for module execution""" + element_spec = dict( + name=dict(), + description=dict(), + rd=dict(type="list", elements="str"), + interfaces=dict(type="list", elements="str"), + target=dict(type="list", elements="str"), + state=dict(default="present", choices=["present", "absent"]), + active=dict(default=True, type="bool"), + table_label=dict(default=True, type="bool"), + ) + + 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) + + required_one_of = [["aggregate", "name"]] + mutually_exclusive = [["aggregate", "name"]] + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + ) + + warnings = list() + result = {"changed": False} + + if warnings: + result["warnings"] = warnings + + top = "routing-instances/instance" + + param_to_xpath_map = collections.OrderedDict() + param_to_xpath_map.update( + [ + ("name", {"xpath": "name", "is_key": True}), + ("description", "description"), + ("type", "instance-type"), + ("rd", "route-distinguisher/rd-type"), + ("interfaces", "interface/name"), + ("target", "vrf-target/community"), + ("table_label", {"xpath": "vrf-table-label", "tag_only": True}), + ], + ) + + params = to_param_list(module) + requests = list() + + for param in params: + # if key doesn't exist in the item, get it from module.params + for key in param: + if param.get(key) is None: + param[key] = module.params[key] + + item = param.copy() + item["type"] = "vrf" + + want = map_params_to_obj(module, param_to_xpath_map, param=item) + requests.append(map_obj_to_ele(module, want, top, param=item)) + + with locked_config(module): + for req in requests: + diff = load_config(module, tostring(req), warnings, action="merge") + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result["changed"] = True + + if module._diff: + result["diff"] = {"prepared": diff} + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/junipernetworks/junos/plugins/netconf/__init__.py b/ansible_collections/junipernetworks/junos/plugins/netconf/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/netconf/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/netconf/junos.py b/ansible_collections/junipernetworks/junos/plugins/netconf/junos.py new file mode 100644 index 000000000..9c7986a87 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/netconf/junos.py @@ -0,0 +1,283 @@ +# +# (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 (@ansible-network) +name: junos +short_description: Use junos netconf plugin to run netconf commands on Juniper JUNOS + platform +description: +- This junos plugin provides low level abstraction apis for sending and receiving + netconf commands from Juniper JUNOS network devices. +version_added: 1.0.0 +options: + ncclient_device_handler: + type: str + default: junos + description: + - Specifies the ncclient device handler name for Juniper junos network os. To + identify the ncclient device handler name refer ncclient library documentation. +""" + +import json +import re + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_native, to_text +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.plugin_utils.netconf_base import ( + NetconfBase, + ensure_ncclient, +) + + +try: + from ncclient import manager + from ncclient.operations import RPCError + from ncclient.transport.errors import SSHUnknownHostError + from ncclient.xml_ import new_ele, sub_ele, to_ele, 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_text(self, ele, tag): + try: + return to_text( + ele.find(tag).text, + errors="surrogate_then_replace", + ).strip() + except AttributeError: + pass + + @ensure_ncclient + def get_device_info(self): + device_info = dict() + device_info["network_os"] = "junos" + ele = new_ele("get-software-information") + data = self.execute_rpc(to_xml(ele)) + reply = to_ele(data) + sw_info = reply.find(".//software-information") + + device_info["network_os_version"] = self.get_text( + sw_info, + "junos-version", + ) + device_info["network_os_hostname"] = self.get_text( + sw_info, + "host-name", + ) + device_info["network_os_model"] = self.get_text( + sw_info, + "product-model", + ) + + return device_info + + def execute_rpc(self, name): + """ + RPC to be execute on remote device + :param name: Name of rpc in string format + :return: Received rpc response from remote host + """ + return self.rpc(name) + + @ensure_ncclient + def load_configuration( + self, + format="xml", + action="merge", + target="candidate", + config=None, + ): + """ + Load given configuration on device + :param format: Format of configuration (xml, text, set) + :param action: Action to be performed (merge, replace, override, update) + :param target: The name of the configuration datastore being edited + :param config: The configuration to be loaded on remote host in string format + :return: Received rpc response from remote host in string format + """ + if config: + if format == "xml": + config = to_ele(config) + + try: + return self.m.load_configuration( + format=format, + action=action, + target=target, + config=config, + ).data_xml + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def get_capabilities(self): + result = dict() + result["rpc"] = self.get_base_rpc() + [ + "commit", + "discard_changes", + "validate", + "lock", + "unlock", + "copy_copy", + "execute_rpc", + "load_configuration", + "get_configuration", + "command", + "reboot", + "halt", + ] + 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("junos", c): + guessed_os = "junos" + + m.close_session() + return guessed_os + + def get_configuration(self, format="xml", filter=None): + """ + Retrieve all or part of a specified configuration. + :param format: format in which configuration should be retrieved + :param filter: specifies the portion of the configuration to retrieve + as either xml string rooted in <configuration> element + :return: Received rpc response from remote host in string format + """ + if filter is not None: + if not isinstance(filter, string_types): + raise AnsibleConnectionFailure( + "get configuration filter should be of type string," + " received value '%s' is of type '%s'" % (filter, type(filter)), + ) + filter = to_ele(filter) + + return self.m.get_configuration(format=format, filter=filter).data_xml + + def compare_configuration(self, rollback=0): + """ + Compare the candidate configuration with running configuration + by default. The candidate configuration can be compared with older + committed configuration by providing rollback id. + :param rollback: Rollback id of previously commited configuration + :return: Received rpc response from remote host in string format + """ + return self.m.compare_configuration(rollback=rollback).data_xml + + def halt(self): + """reboot the device""" + return self.m.halt().data_xml + + def reboot(self): + """reboot the device""" + return self.m.reboot().data_xml + + # Due to issue in ncclient commit() method for Juniper (https://github.com/ncclient/ncclient/issues/238) + # below commit() is a workaround which build's raw `commit-configuration` xml with required tags and uses + # ncclient generic rpc() method to execute rpc on remote host. + # Remove below method after the issue in ncclient is fixed. + @ensure_ncclient + def commit( + self, + confirmed=False, + timeout=None, + persist=None, + check=False, + comment=None, + synchronize=False, + at_time=None, + ): + """ + Commit the candidate configuration as the device's new current configuration. + Depends on the `:candidate` capability. + A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no + followup commit within the *timeout* interval. If no timeout is specified the + confirm timeout defaults to 600 seconds (10 minutes). + A confirming commit may have the *confirmed* parameter but this is not required. + Depends on the `:confirmed-commit` capability. + :param confirmed: whether this is a confirmed commit + :param check: Check correctness of syntax + :param timeout: specifies the confirm timeout in seconds + :param comment: Message to write to commit log + :param synchronize: Synchronize commit on remote peers + :param at_time: Time at which to activate configuration changes + :return: Received rpc response from remote host + """ + obj = new_ele("commit-configuration") + if confirmed: + sub_ele(obj, "confirmed") + if check: + sub_ele(obj, "check") + if synchronize: + sub_ele(obj, "synchronize") + if at_time: + subele = sub_ele(obj, "at-time") + subele.text = str(at_time) + if comment: + subele = sub_ele(obj, "log") + subele.text = str(comment) + if timeout: + subele = sub_ele(obj, "confirm-timeout") + subele.text = str(timeout) + return self.rpc(obj) diff --git a/ansible_collections/junipernetworks/junos/plugins/terminal/__init__.py b/ansible_collections/junipernetworks/junos/plugins/terminal/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/terminal/__init__.py diff --git a/ansible_collections/junipernetworks/junos/plugins/terminal/junos.py b/ansible_collections/junipernetworks/junos/plugins/terminal/junos.py new file mode 100644 index 000000000..574e80cc2 --- /dev/null +++ b/ansible_collections/junipernetworks/junos/plugins/terminal/junos.py @@ -0,0 +1,68 @@ +# +# (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.errors import AnsibleConnectionFailure +from ansible.module_utils.common.text.converters import to_bytes +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.plugin_utils.terminal_base import TerminalBase + + +display = Display() + + +class TerminalModule(TerminalBase): + + terminal_stdout_re = [ + re.compile( + to_bytes(r"({primary:node\d+})?[\r\n]?[\w@+\-\.:\/\[\]]+[>#%] ?$"), + ), + ] + + terminal_stderr_re = [ + re.compile(to_bytes(r"unknown command")), + re.compile(to_bytes(r"syntax error")), + re.compile(to_bytes(r"[\r\n]error:")), + ] + + terminal_config_prompt = re.compile(r"^.+#$") + + def on_open_shell(self): + try: + prompt = self._get_prompt() + if prompt.strip().endswith(b"%"): + display.vvv( + "starting cli", + self._connection._play_context.remote_addr, + ) + self._exec_cli_command(b"cli") + for c in ( + b"set cli timestamp disable", + b"set cli screen-length 0", + b"set cli screen-width 1024", + ): + self._exec_cli_command(c) + except AnsibleConnectionFailure: + raise AnsibleConnectionFailure("unable to set terminal parameters") |