diff --git a/plugins/action/vyos.py b/plugins/action/vyos.py
index 5c310c3..cab2f3f 100644
--- a/plugins/action/vyos.py
+++ b/plugins/action/vyos.py
@@ -1,130 +1,129 @@
#
# (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 .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import sys
import copy
from ansible_collections.ansible.netcommon.plugins.action.network import (
ActionModule as ActionNetworkModule,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
load_provider,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_provider_spec,
)
from ansible.utils.display import Display
display = Display()
class ActionModule(ActionNetworkModule):
def run(self, tmp=None, task_vars=None):
del tmp # tmp no longer has any effect
module_name = self._task.action.split(".")[-1]
self._config_module = True if module_name == "vyos_config" else False
persistent_connection = self._play_context.connection.split(".")[-1]
warnings = []
if persistent_connection == "network_cli":
provider = self._task.args.get("provider", {})
if any(provider.values()):
display.warning(
"provider is unnecessary when using network_cli and will be ignored"
)
del self._task.args["provider"]
elif self._play_context.connection == "local":
provider = load_provider(vyos_provider_spec, self._task.args)
pc = copy.deepcopy(self._play_context)
pc.connection = "ansible.netcommon.network_cli"
pc.network_os = "vyos.vyos.vyos"
pc.remote_addr = provider["host"] or self._play_context.remote_addr
pc.port = int(provider["port"] or self._play_context.port or 22)
pc.remote_user = (
provider["username"] or self._play_context.connection_user
)
pc.password = provider["password"] or self._play_context.password
pc.private_key_file = (
provider["ssh_keyfile"] or self._play_context.private_key_file
)
connection = self._shared_loader_obj.connection_loader.get(
"ansible.netcommon.persistent",
pc,
sys.stdin,
task_uuid=self._task._uuid,
)
# TODO: Remove below code after ansible minimal is cut out
if connection is None:
pc.connection = "network_cli"
pc.network_os = "vyos"
connection = self._shared_loader_obj.connection_loader.get(
"persistent", pc, sys.stdin, task_uuid=self._task._uuid
)
display.vvv(
"using connection plugin %s (was local)" % pc.connection,
pc.remote_addr,
)
command_timeout = (
int(provider["timeout"])
if provider["timeout"]
else connection.get_option("persistent_command_timeout")
)
connection.set_options(
direct={"persistent_command_timeout": command_timeout}
)
socket_path = connection.run()
display.vvvv("socket_path: %s" % socket_path, pc.remote_addr)
if not socket_path:
return {
"failed": True,
"msg": "unable to open shell. Please see: "
+ "https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell",
}
task_vars["ansible_socket"] = socket_path
warnings.append(
[
"connection local support for this module is deprecated and will be removed in version 2.14, use connection %s"
% pc.connection
]
)
else:
return {
"failed": True,
"msg": "Connection type %s is not valid for this module"
% self._play_context.connection,
}
result = super(ActionModule, self).run(task_vars=task_vars)
if warnings:
if "warnings" in result:
result["warnings"].extend(warnings)
else:
result["warnings"] = warnings
return result
diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py
index 3ae1841..de9e93d 100644
--- a/plugins/cliconf/vyos.py
+++ b/plugins/cliconf/vyos.py
@@ -1,342 +1,341 @@
#
# (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 .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
author: Ansible Networking Team
cliconf: vyos
short_description: Use vyos cliconf to run command on VyOS platform
description:
- This vyos plugin provides low level abstraction apis for sending and receiving CLI
commands from VyOS network devices.
version_added: 1.0.0
"""
import re
import json
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text
from ansible.module_utils.common._collections_compat import Mapping
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
NetworkConfig,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
)
from ansible.plugins.cliconf import CliconfBase
class Cliconf(CliconfBase):
def get_device_info(self):
device_info = {}
device_info["network_os"] = "vyos"
reply = self.get("show version")
data = to_text(reply, errors="surrogate_or_strict").strip()
match = re.search(r"Version:\s*(.*)", data)
if match:
device_info["network_os_version"] = match.group(1)
match = re.search(r"HW model:\s*(\S+)", data)
if match:
device_info["network_os_model"] = match.group(1)
reply = self.get("show host name")
device_info["network_os_hostname"] = to_text(
reply, errors="surrogate_or_strict"
).strip()
return device_info
def get_config(self, flags=None, format=None):
if format:
option_values = self.get_option_values()
if format not in option_values["format"]:
raise ValueError(
"'format' value %s is invalid. Valid values of format are %s"
% (format, ", ".join(option_values["format"]))
)
if not flags:
flags = []
if format == "text":
command = "show configuration"
else:
command = "show configuration commands"
command += " ".join(to_list(flags))
command = command.strip()
out = self.send_command(command)
return out
def edit_config(
self, candidate=None, commit=True, replace=None, comment=None
):
resp = {}
operations = self.get_device_operations()
self.check_edit_config_capability(
operations, candidate, commit, replace, comment
)
results = []
requests = []
self.send_command("configure")
for cmd in to_list(candidate):
if not isinstance(cmd, Mapping):
cmd = {"command": cmd}
results.append(self.send_command(**cmd))
requests.append(cmd["command"])
out = self.get("compare")
out = to_text(out, errors="surrogate_or_strict")
diff_config = out if not out.startswith("No changes") else None
if diff_config:
if commit:
try:
self.commit(comment)
except AnsibleConnectionFailure as e:
msg = "commit failed: %s" % e.message
self.discard_changes()
raise AnsibleConnectionFailure(msg)
else:
self.send_command("exit")
else:
self.discard_changes()
else:
self.send_command("exit")
if (
to_text(
self._connection.get_prompt(), errors="surrogate_or_strict"
)
.strip()
.endswith("#")
):
self.discard_changes()
if diff_config:
resp["diff"] = diff_config
resp["response"] = results
resp["request"] = requests
return resp
def get(
self,
command=None,
prompt=None,
answer=None,
sendonly=False,
output=None,
newline=True,
check_all=False,
):
if not command:
raise ValueError("must provide value of command to execute")
if output:
raise ValueError(
"'output' value %s is not supported for get" % output
)
return self.send_command(
command=command,
prompt=prompt,
answer=answer,
sendonly=sendonly,
newline=newline,
check_all=check_all,
)
def commit(self, comment=None):
if comment:
command = 'commit comment "{0}"'.format(comment)
else:
command = "commit"
self.send_command(command)
def discard_changes(self):
self.send_command("exit discard")
def get_diff(
self,
candidate=None,
running=None,
diff_match="line",
diff_ignore_lines=None,
path=None,
diff_replace=None,
):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
if candidate is None and device_operations["supports_generate_diff"]:
raise ValueError(
"candidate configuration is required to generate diff"
)
if diff_match not in option_values["diff_match"]:
raise ValueError(
"'match' value %s in invalid, valid values are %s"
% (diff_match, ", ".join(option_values["diff_match"]))
)
if diff_replace:
raise ValueError("'replace' in diff is not supported")
if diff_ignore_lines:
raise ValueError("'diff_ignore_lines' in diff is not supported")
if path:
raise ValueError("'path' in diff is not supported")
set_format = candidate.startswith("set") or candidate.startswith(
"delete"
)
candidate_obj = NetworkConfig(indent=4, contents=candidate)
if not set_format:
config = [c.line for c in candidate_obj.items]
commands = list()
# this filters out less specific lines
for item in config:
for index, entry in enumerate(commands):
if item.startswith(entry):
del commands[index]
break
commands.append(item)
candidate_commands = [
"set %s" % cmd.replace(" {", "") for cmd in commands
]
else:
candidate_commands = str(candidate).strip().split("\n")
if diff_match == "none":
diff["config_diff"] = list(candidate_commands)
return diff
running_commands = [
str(c).replace("'", "") for c in running.splitlines()
]
updates = list()
visited = set()
for line in candidate_commands:
item = str(line).replace("'", "")
if not item.startswith("set") and not item.startswith("delete"):
raise ValueError(
"line must start with either `set` or `delete`"
)
elif item.startswith("set") and item not in running_commands:
updates.append(line)
elif item.startswith("delete"):
if not running_commands:
updates.append(line)
else:
item = re.sub(r"delete", "set", item)
for entry in running_commands:
if entry.startswith(item) and line not in visited:
updates.append(line)
visited.add(line)
diff["config_diff"] = list(updates)
return diff
def run_commands(self, commands=None, check_rc=True):
if commands is None:
raise ValueError("'commands' value is required")
responses = list()
for cmd in to_list(commands):
if not isinstance(cmd, Mapping):
cmd = {"command": cmd}
output = cmd.pop("output", None)
if output:
raise ValueError(
"'output' value %s is not supported for run_commands"
% output
)
try:
out = self.send_command(**cmd)
except AnsibleConnectionFailure as e:
if check_rc:
raise
out = getattr(e, "err", e)
responses.append(out)
return responses
def get_device_operations(self):
return {
"supports_diff_replace": False,
"supports_commit": True,
"supports_rollback": False,
"supports_defaults": False,
"supports_onbox_diff": True,
"supports_commit_comment": True,
"supports_multiline_delimiter": False,
"supports_diff_match": True,
"supports_diff_ignore_lines": False,
"supports_generate_diff": False,
"supports_replace": False,
}
def get_option_values(self):
return {
"format": ["text", "set"],
"diff_match": ["line", "none"],
"diff_replace": [],
"output": [],
}
def get_capabilities(self):
result = super(Cliconf, self).get_capabilities()
result["rpc"] += [
"commit",
"discard_changes",
"get_diff",
"run_commands",
]
result["device_operations"] = self.get_device_operations()
result.update(self.get_option_values())
return json.dumps(result)
def set_cli_prompt_context(self):
"""
Make sure we are in the operational cli mode
:return: None
"""
if self._connection.connected:
self._update_cli_prompt_context(
config_context="#", exit_command="exit discard"
)
diff --git a/plugins/module_utils/network/vyos/argspec/facts/facts.py b/plugins/module_utils/network/vyos/argspec/facts/facts.py
index 1f0cca7..46fabaa 100644
--- a/plugins/module_utils/network/vyos/argspec/facts/facts.py
+++ b/plugins/module_utils/network/vyos/argspec/facts/facts.py
@@ -1,23 +1,22 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The arg spec for the vyos facts module.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class FactsArgs(object): # pylint: disable=R0903
""" The arg spec for the vyos facts module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"gather_subset": dict(default=["!config"], type="list"),
"gather_network_resources": dict(type="list"),
}
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
index 9871f9c..4c26773 100644
--- a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
@@ -1,153 +1,152 @@
#
# -*- 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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_firewall_global module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Firewall_globalArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_firewall_global module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"options": {
"config_trap": {"type": "bool"},
"group": {
"options": {
"address_group": {
"elements": "dict",
"options": {
"description": {"type": "str"},
"members": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
"network_group": {
"elements": "dict",
"options": {
"description": {"type": "str"},
"members": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
"port_group": {
"elements": "dict",
"options": {
"description": {"type": "str"},
"members": {
"elements": "dict",
"options": {"port": {"type": "str"}},
"type": "list",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
},
"type": "dict",
},
"log_martians": {"type": "bool"},
"ping": {
"options": {
"all": {"type": "bool"},
"broadcast": {"type": "bool"},
},
"type": "dict",
},
"route_redirects": {
"elements": "dict",
"options": {
"afi": {
"choices": ["ipv4", "ipv6"],
"required": True,
"type": "str",
},
"icmp_redirects": {
"options": {
"receive": {"type": "bool"},
"send": {"type": "bool"},
},
"type": "dict",
},
"ip_src_route": {"type": "bool"},
},
"type": "list",
},
"state_policy": {
"elements": "dict",
"options": {
"action": {
"choices": ["accept", "drop", "reject"],
"type": "str",
},
"connection_type": {
"choices": ["established", "invalid", "related"],
"type": "str",
},
"log": {"type": "bool"},
},
"type": "list",
},
"syn_cookies": {"type": "bool"},
"twa_hazards_protection": {"type": "bool"},
"validation": {
"choices": ["strict", "loose", "disable"],
"type": "str",
},
},
"type": "dict",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"deleted",
"gathered",
"rendered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py
index c49110c..f083485 100644
--- a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py
@@ -1,86 +1,85 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_firewall_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Firewall_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_firewall_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"access_rules": {
"elements": "dict",
"options": {
"afi": {
"choices": ["ipv4", "ipv6"],
"required": True,
"type": "str",
},
"rules": {
"elements": "dict",
"options": {
"direction": {
"choices": ["in", "local", "out"],
"required": True,
"type": "str",
},
"name": {"type": "str"},
},
"type": "list",
},
},
"type": "list",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"parsed",
"rendered",
"gathered",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
index 2f23d91..a018cc0 100644
--- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
@@ -1,264 +1,263 @@
#
# -*- 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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_firewall_rules module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Firewall_rulesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_firewall_rules module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"afi": {
"choices": ["ipv4", "ipv6"],
"required": True,
"type": "str",
},
"rule_sets": {
"elements": "dict",
"options": {
"default_action": {
"choices": ["drop", "reject", "accept"],
"type": "str",
},
"description": {"type": "str"},
"enable_default_log": {"type": "bool"},
"name": {"type": "str"},
"rules": {
"elements": "dict",
"options": {
"action": {
"choices": [
"drop",
"reject",
"accept",
"inspect",
],
"type": "str",
},
"description": {"type": "str"},
"destination": {
"options": {
"address": {"type": "str"},
"group": {
"options": {
"address_group": {
"type": "str"
},
"network_group": {
"type": "str"
},
"port_group": {"type": "str"},
},
"type": "dict",
},
"port": {"type": "str"},
},
"type": "dict",
},
"disabled": {"type": "bool"},
"fragment": {
"choices": [
"match-frag",
"match-non-frag",
],
"type": "str",
},
"icmp": {
"options": {
"code": {"type": "int"},
"type": {"type": "int"},
"type_name": {
"choices": [
"any",
"echo-reply",
"destination-unreachable",
"network-unreachable",
"host-unreachable",
"protocol-unreachable",
"port-unreachable",
"fragmentation-needed",
"source-route-failed",
"network-unknown",
"host-unknown",
"network-prohibited",
"host-prohibited",
"TOS-network-unreachable",
"TOS-host-unreachable",
"communication-prohibited",
"host-precedence-violation",
"precedence-cutoff",
"source-quench",
"redirect",
"network-redirect",
"host-redirect",
"TOS-network-redirect",
"TOS-host-redirect",
"echo-request",
"router-advertisement",
"router-solicitation",
"time-exceeded",
"ttl-zero-during-transit",
"ttl-zero-during-reassembly",
"parameter-problem",
"ip-header-bad",
"required-option-missing",
"timestamp-request",
"timestamp-reply",
"address-mask-request",
"address-mask-reply",
"ping",
"pong",
"ttl-exceeded",
],
"type": "str",
},
},
"type": "dict",
},
"ipsec": {
"choices": ["match-ipsec", "match-none"],
"type": "str",
},
"limit": {
"options": {
"burst": {"type": "int"},
"rate": {
"options": {
"number": {"type": "int"},
"unit": {"type": "str"},
},
"type": "dict",
},
},
"type": "dict",
},
"number": {"required": True, "type": "int"},
"p2p": {
"elements": "dict",
"options": {
"application": {
"choices": [
"all",
"applejuice",
"bittorrent",
"directconnect",
"edonkey",
"gnutella",
"kazaa",
],
"type": "str",
}
},
"type": "list",
},
"protocol": {"type": "str"},
"recent": {
"options": {
"count": {"type": "int"},
"time": {"type": "int"},
},
"type": "dict",
},
"source": {
"options": {
"address": {"type": "str"},
"group": {
"options": {
"address_group": {
"type": "str"
},
"network_group": {
"type": "str"
},
"port_group": {"type": "str"},
},
"type": "dict",
},
"mac_address": {"type": "str"},
"port": {"type": "str"},
},
"type": "dict",
},
"state": {
"options": {
"established": {"type": "bool"},
"invalid": {"type": "bool"},
"new": {"type": "bool"},
"related": {"type": "bool"},
},
"type": "dict",
},
"tcp": {
"options": {"flags": {"type": "str"}},
"type": "dict",
},
"time": {
"options": {
"monthdays": {"type": "str"},
"startdate": {"type": "str"},
"starttime": {"type": "str"},
"stopdate": {"type": "str"},
"stoptime": {"type": "str"},
"utc": {"type": "bool"},
"weekdays": {"type": "str"},
},
"type": "dict",
},
},
"type": "list",
},
},
"type": "list",
},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"gathered",
"rendered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
index b45c80f..fd8b612 100644
--- a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py
@@ -1,79 +1,78 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class InterfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"description": {"type": "str"},
"duplex": {"choices": ["full", "half", "auto"]},
"enabled": {"default": True, "type": "bool"},
"mtu": {"type": "int"},
"name": {"required": True, "type": "str"},
"speed": {
"choices": ["auto", "10", "100", "1000", "2500", "10000"],
"type": "str",
},
"vifs": {
"elements": "dict",
"options": {
"vlan_id": {"type": "int"},
"description": {"type": "str"},
"enabled": {"default": True, "type": "bool"},
"mtu": {"type": "int"},
},
"type": "list",
},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"rendered",
"parsed",
"gathered",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
index 820d7f9..2f1dfe4 100644
--- a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py
@@ -1,90 +1,89 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_l3_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class L3_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_l3_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"ipv4": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"ipv6": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"name": {"required": True, "type": "str"},
"vifs": {
"elements": "dict",
"options": {
"ipv4": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"ipv6": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"vlan_id": {"type": "int"},
},
"type": "list",
},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"rendered",
"gathered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
index 970f81b..6cfdabf 100644
--- a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py
@@ -1,89 +1,88 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_lag_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lag_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lag_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"arp_monitor": {
"options": {
"interval": {"type": "int"},
"target": {"type": "list"},
},
"type": "dict",
},
"hash_policy": {
"choices": ["layer2", "layer2+3", "layer3+4"],
"type": "str",
},
"members": {
"elements": "dict",
"options": {"member": {"type": "str"}},
"type": "list",
},
"mode": {
"choices": [
"802.3ad",
"active-backup",
"broadcast",
"round-robin",
"transmit-load-balance",
"adaptive-load-balance",
"xor-hash",
],
"type": "str",
},
"name": {"required": True, "type": "str"},
"primary": {"type": "str"},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"rendered",
"gathered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
index c9e4f88..6205fd7 100644
--- a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
+++ b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py
@@ -1,64 +1,63 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_lldp_global module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lldp_globalArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lldp_global module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"options": {
"address": {"type": "str"},
"enable": {"type": "bool"},
"legacy_protocols": {
"choices": ["cdp", "edp", "fdp", "sonmp"],
"type": "list",
},
"snmp": {"type": "str"},
},
"type": "dict",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"deleted",
"rendered",
"parsed",
"gathered",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
index f80b272..109ea43 100644
--- a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
+++ b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py
@@ -1,99 +1,98 @@
#
# -*- 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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_lldp_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lldp_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lldp_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"enable": {"default": True, "type": "bool"},
"location": {
"options": {
"civic_based": {
"options": {
"ca_info": {
"elements": "dict",
"options": {
"ca_type": {"type": "int"},
"ca_value": {"type": "str"},
},
"type": "list",
},
"country_code": {
"required": True,
"type": "str",
},
},
"type": "dict",
},
"coordinate_based": {
"options": {
"altitude": {"type": "int"},
"datum": {
"choices": ["WGS84", "NAD83", "MLLW"],
"type": "str",
},
"latitude": {"required": True, "type": "str"},
"longitude": {"required": True, "type": "str"},
},
"type": "dict",
},
"elin": {"type": "str"},
},
"type": "dict",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"rendered",
"gathered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
index af64d57..b4cdadf 100644
--- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
+++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py
@@ -1,274 +1,273 @@
#
# -*- 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)
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_ospfv2 module
"""
class Ospfv2Args(object): # pylint: disable=R0903
"""The arg spec for the vyos_ospfv2 module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"options": {
"auto_cost": {
"options": {"reference_bandwidth": {"type": "int"}},
"type": "dict",
},
"default_information": {
"options": {
"originate": {
"options": {
"always": {"type": "bool"},
"metric": {"type": "int"},
"metric_type": {"type": "int"},
"route_map": {"type": "str"},
},
"type": "dict",
}
},
"type": "dict",
},
"default_metric": {"type": "int"},
"distance": {
"options": {
"global": {"type": "int"},
"ospf": {
"options": {
"external": {"type": "int"},
"inter_area": {"type": "int"},
"intra_area": {"type": "int"},
},
"type": "dict",
},
},
"type": "dict",
},
"log_adjacency_changes": {
"choices": ["detail"],
"type": "str",
},
"max_metric": {
"options": {
"router_lsa": {
"options": {
"administrative": {"type": "bool"},
"on_shutdown": {"type": "int"},
"on_startup": {"type": "int"},
},
"type": "dict",
}
},
"type": "dict",
},
"mpls_te": {
"options": {
"enabled": {"type": "bool"},
"router_address": {"type": "str"},
},
"type": "dict",
},
"neighbor": {
"elements": "dict",
"options": {
"neighbor_id": {"type": "str"},
"poll_interval": {"type": "int"},
"priority": {"type": "int"},
},
"type": "list",
},
"areas": {
"elements": "dict",
"options": {
"area_id": {"type": "str"},
"area_type": {
"options": {
"normal": {"type": "bool"},
"nssa": {
"options": {
"default_cost": {"type": "int"},
"no_summary": {"type": "bool"},
"set": {"type": "bool"},
"translate": {
"choices": [
"always",
"candidate",
"never",
],
"type": "str",
},
},
"type": "dict",
},
"stub": {
"options": {
"default_cost": {"type": "int"},
"no_summary": {"type": "bool"},
"set": {"type": "bool"},
},
"type": "dict",
},
},
"type": "dict",
},
"authentication": {
"choices": ["plaintext-password", "md5"],
"type": "str",
},
"network": {
"elements": "dict",
"options": {
"address": {"required": True, "type": "str"}
},
"type": "list",
},
"range": {
"elements": "dict",
"options": {
"address": {"type": "str"},
"cost": {"type": "int"},
"not_advertise": {"type": "bool"},
"substitute": {"type": "str"},
},
"type": "list",
},
"shortcut": {
"choices": ["default", "disable", "enable"],
"type": "str",
},
"virtual_link": {
"elements": "dict",
"options": {
"address": {"type": "str"},
"authentication": {
"options": {
"md5": {
"elements": "dict",
"options": {
"key_id": {"type": "int"},
"md5_key": {"type": "str"},
},
"type": "list",
},
"plaintext_password": {"type": "str"},
},
"type": "dict",
},
"dead_interval": {"type": "int"},
"hello_interval": {"type": "int"},
"retransmit_interval": {"type": "int"},
"transmit_delay": {"type": "int"},
},
"type": "list",
},
},
"type": "list",
},
"parameters": {
"options": {
"abr_type": {
"choices": [
"cisco",
"ibm",
"shortcut",
"standard",
],
"type": "str",
},
"opaque_lsa": {"type": "bool"},
"rfc1583_compatibility": {"type": "bool"},
"router_id": {"type": "str"},
},
"type": "dict",
},
"passive_interface": {"type": "list"},
"passive_interface_exclude": {"type": "list"},
"redistribute": {
"elements": "dict",
"options": {
"metric": {"type": "int"},
"metric_type": {"type": "int"},
"route_map": {"type": "str"},
"route_type": {
"choices": [
"bgp",
"connected",
"kernel",
"rip",
"static",
],
"type": "str",
},
},
"type": "list",
},
"route_map": {"type": "list"},
"timers": {
"options": {
"refresh": {
"options": {"timers": {"type": "int"}},
"type": "dict",
},
"throttle": {
"options": {
"spf": {
"options": {
"delay": {"type": "int"},
"initial_holdtime": {"type": "int"},
"max_holdtime": {"type": "int"},
},
"type": "dict",
}
},
"type": "dict",
},
},
"type": "dict",
},
},
"type": "dict",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"deleted",
"parsed",
"gathered",
"rendered",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py
index 1631c51..25f979c 100644
--- a/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py
+++ b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py
@@ -1,99 +1,98 @@
#
# -*- 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)
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_ospfv3 module
"""
class Ospfv3Args(object): # pylint: disable=R0903
"""The arg spec for the vyos_ospfv3 module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"options": {
"areas": {
"elements": "dict",
"options": {
"area_id": {"type": "str"},
"export_list": {"type": "str"},
"import_list": {"type": "str"},
"range": {
"elements": "dict",
"options": {
"address": {"type": "str"},
"advertise": {"type": "bool"},
"not_advertise": {"type": "bool"},
},
"type": "list",
},
},
"type": "list",
},
"parameters": {
"options": {"router_id": {"type": "str"}},
"type": "dict",
},
"redistribute": {
"elements": "dict",
"options": {
"route_map": {"type": "str"},
"route_type": {
"choices": [
"bgp",
"connected",
"kernel",
"ripng",
"static",
],
"type": "str",
},
},
"type": "list",
},
},
"type": "dict",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"deleted",
"parsed",
"gathered",
"rendered",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py
index d2dc617..8ecd955 100644
--- a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py
+++ b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py
@@ -1,100 +1,99 @@
#
# -*- 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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the vyos_static_routes module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Static_routesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_static_routes module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"address_families": {
"elements": "dict",
"options": {
"afi": {
"choices": ["ipv4", "ipv6"],
"required": True,
"type": "str",
},
"routes": {
"elements": "dict",
"options": {
"blackhole_config": {
"options": {
"distance": {"type": "int"},
"type": {"type": "str"},
},
"type": "dict",
},
"dest": {"required": True, "type": "str"},
"next_hops": {
"elements": "dict",
"options": {
"admin_distance": {"type": "int"},
"enabled": {"type": "bool"},
"forward_router_address": {
"required": True,
"type": "str",
},
"interface": {"type": "str"},
},
"type": "list",
},
},
"type": "list",
},
},
"type": "list",
}
},
"type": "list",
},
"running_config": {"type": "str"},
"state": {
"choices": [
"merged",
"replaced",
"overridden",
"deleted",
"gathered",
"rendered",
"parsed",
],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
index 5480a56..afc9853 100644
--- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
@@ -1,811 +1,810 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_firewall_global class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
list_diff_want_only,
)
class Firewall_global(ConfigBase):
"""
The vyos_firewall_global class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"firewall_global",
]
def __init__(self, module):
super(Firewall_global, self).__init__(module)
def get_firewall_global_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
firewall_global_facts = facts["ansible_network_resources"].get(
"firewall_global"
)
if not firewall_global_facts:
return []
return firewall_global_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_firewall_global_facts = self.get_firewall_global_facts()
else:
existing_firewall_global_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_firewall_global_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_firewall_global_facts = self.get_firewall_global_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_firewall_global_facts(
data=running_config
)
else:
changed_firewall_global_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_firewall_global_facts
if result["changed"]:
result["after"] = changed_firewall_global_facts
elif self.state == "gathered":
result["gathered"] = changed_firewall_global_facts
result["warnings"] = warnings
return result
def set_config(self, existing_firewall_global_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_firewall_global_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, w, h):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if self.state in ("merged", "replaced", "rendered") and not w:
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "deleted":
commands.extend(self._state_deleted(want=None, have=h))
elif w:
if self.state == "merged" or self.state == "rendered":
commands.extend(self._state_merged(w, h))
elif self.state == "replaced":
commands.extend(self._state_replaced(w, h))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._state_deleted(have, want))
commands.extend(self._state_merged(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
commands.extend(self._add_global_attr(want, have))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
b_set = (
"config_trap",
"validation",
"log_martians",
"syn_cookies",
"twa_hazards_protection",
)
if want:
for key, val in iteritems(want):
if val and key in b_set and not have:
commands.append(self._form_attr_cmd(attr=key, opr=False))
elif (
val
and key in b_set
and have
and key in have
and have[key] != val
):
commands.append(self._form_attr_cmd(attr=key, opr=False))
else:
commands.extend(self._render_attr_config(want, have, key))
elif not want and have:
commands.append(self._compute_command(opr=False))
elif have:
for key, val in iteritems(have):
if val and key in b_set:
commands.append(self._form_attr_cmd(attr=key, opr=False))
else:
commands.extend(self._render_attr_config(want, have, key))
return commands
def _render_attr_config(self, w, h, key, opr=False):
"""
This function invoke the function to extend commands
based on the key.
:param w: the desired configuration.
:param h: the current configuration.
:param key: attribute name
:param opr: operation
:return: list of commands
"""
commands = []
if key == "ping":
commands.extend(self._render_ping(key, w, h, opr=opr))
elif key == "group":
commands.extend(self._render_group(key, w, h, opr=opr))
elif key == "state_policy":
commands.extend(self._render_state_policy(key, w, h, opr=opr))
elif key == "route_redirects":
commands.extend(self._render_route_redirects(key, w, h, opr=opr))
return commands
def _add_global_attr(self, w, h, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for firewall_global attributes.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
w_fg = deepcopy(remove_empties(w))
l_set = (
"config_trap",
"validation",
"log_martians",
"syn_cookies",
"twa_hazards_protection",
)
if w_fg:
for key, val in iteritems(w_fg):
if (
opr
and key in l_set
and not (h and self._is_w_same(w_fg, h, key))
):
commands.append(
self._form_attr_cmd(
attr=key, val=self._bool_to_str(val), opr=opr
)
)
elif not opr:
if key and self._is_del(l_set, h):
commands.append(
self._form_attr_cmd(
attr=key, key=self._bool_to_str(val), opr=opr
)
)
continue
elif (
key in l_set
and not (h and self._in_target(h, key))
and not self._is_del(l_set, h)
):
commands.append(
self._form_attr_cmd(
attr=key, val=self._bool_to_str(val), opr=opr
)
)
else:
commands.extend(
self._render_attr_config(w_fg, h, key, opr)
)
return commands
def _render_ping(self, attr, w, h, opr):
"""
This function forms the commands for 'ping' attributes based on the 'opr'.
:param attr: attribute name.
:param w: the desired configuration.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h_ping = {}
l_set = ("all", "broadcast")
if h:
h_ping = h.get(attr) or {}
if self._is_root_del(w[attr], h_ping, attr):
for item, value in iteritems(h[attr]):
if not opr and item in l_set:
commands.append(self._form_attr_cmd(attr=item, opr=opr))
elif w[attr]:
if h and attr in h.keys():
h_ping = h.get(attr) or {}
for item, value in iteritems(w[attr]):
if (
opr
and item in l_set
and not (h_ping and self._is_w_same(w[attr], h_ping, item))
):
commands.append(
self._form_attr_cmd(
attr=item, val=self._bool_to_str(value), opr=opr
)
)
elif (
not opr
and item in l_set
and not (h_ping and self._is_w_same(w[attr], h_ping, item))
):
commands.append(self._form_attr_cmd(attr=item, opr=opr))
return commands
def _render_group(self, attr, w, h, opr):
"""
This function forms the commands for 'group' attribute based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h_grp = {}
if not opr and self._is_root_del(h, w, attr):
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
else:
if h:
h_grp = h.get("group") or {}
if w:
commands.extend(
self._render_grp_mem("port-group", w["group"], h_grp, opr)
)
commands.extend(
self._render_grp_mem(
"address_group", w["group"], h_grp, opr
)
)
commands.extend(
self._render_grp_mem(
"network_group", w["group"], h_grp, opr
)
)
return commands
def _render_grp_mem(self, attr, w, h, opr):
"""
This function forms the commands for group list/members attributes based on the 'opr'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h_grp = []
w_grp = []
l_set = ("name", "description")
if w:
w_grp = w.get(attr) or []
if h:
h_grp = h.get(attr) or []
if w_grp:
for want in w_grp:
cmd = self._compute_command(key="group", attr=attr, opr=opr)
h = self.search_attrib_in_have(h_grp, want, "name")
for key, val in iteritems(want):
if val:
if (
opr
and key in l_set
and not (h and self._is_w_same(want, h, key))
):
if key == "name":
commands.append(cmd + " " + str(val))
else:
commands.append(
cmd
+ " "
+ want["name"]
+ " "
+ key
+ " '"
+ str(want[key])
+ "'"
)
elif not opr and key in l_set:
if key == "name" and self._is_grp_del(
h, want, key
):
commands.append(cmd + " " + want["name"])
continue
elif not (
h and self._in_target(h, key)
) and not self._is_grp_del(h, want, key):
commands.append(
cmd + " " + want["name"] + " " + key
)
elif key == "members":
commands.extend(
self._render_ports_addrs(
key, want, h, opr, cmd, want["name"], attr
)
)
return commands
def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type):
"""
This function forms the commands for port/address/network group members
based on the 'opr'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param name: name of group.
:param type: group type.
:return: generated list of commands.
"""
commands = []
have = []
if w:
want = w.get(attr) or []
if h:
have = h.get(attr) or []
if want:
if opr:
members = list_diff_want_only(want, have)
for member in members:
commands.append(
cmd
+ " "
+ name
+ " "
+ self._grp_type(type)
+ " "
+ member[self._get_mem_type(type)]
)
elif not opr and have:
members = list_diff_want_only(want, have)
for member in members:
commands.append(
cmd
+ " "
+ name
+ " "
+ self._grp_type(type)
+ " "
+ member[self._get_mem_type(type)]
)
return commands
def _get_mem_type(self, group):
"""
This function returns the member type
based on the type of group.
"""
return "port" if group == "port_group" else "address"
def _render_state_policy(self, attr, w, h, opr):
"""
This function forms the commands for 'state-policy' attributes
based on the 'opr'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
have = []
l_set = ("log", "action", "connection_type")
if not opr and self._is_root_del(h, w, attr):
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
else:
w_sp = deepcopy(remove_empties(w))
want = w_sp.get(attr) or []
if h:
have = h.get(attr) or []
if want:
for w in want:
h = self.search_attrib_in_have(have, w, "connection_type")
for key, val in iteritems(w):
if val and key != "connection_type":
if (
opr
and key in l_set
and not (h and self._is_w_same(w, h, key))
):
commands.append(
self._form_attr_cmd(
key=attr + " " + w["connection_type"],
attr=key,
val=self._bool_to_str(val),
opr=opr,
)
)
elif not opr and key in l_set:
if not (
h and self._in_target(h, key)
) and not self._is_del(l_set, h):
if key == "action":
commands.append(
self._form_attr_cmd(
attr=attr
+ " "
+ w["connection_type"],
opr=opr,
)
)
else:
commands.append(
self._form_attr_cmd(
attr=attr
+ " "
+ w["connection_type"],
val=self._bool_to_str(val),
opr=opr,
)
)
return commands
def _render_route_redirects(self, attr, w, h, opr):
"""
This function forms the commands for 'route_redirects' attributes based on the 'opr'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
have = []
l_set = ("afi", "ip_src_route")
if w:
want = w.get(attr) or []
if h:
have = h.get(attr) or []
if want:
for w in want:
h = self.search_attrib_in_have(have, w, "afi")
for key, val in iteritems(w):
if val and key != "afi":
if (
opr
and key in l_set
and not (h and self._is_w_same(w, h, key))
):
commands.append(
self._form_attr_cmd(
attr=key,
val=self._bool_to_str(val),
opr=opr,
)
)
elif not opr and key in l_set:
if self._is_del(l_set, h):
commands.append(
self._form_attr_cmd(
attr=key,
val=self._bool_to_str(val),
opr=opr,
)
)
continue
elif not (
h and self._in_target(h, key)
) and not self._is_del(l_set, h):
commands.append(
self._form_attr_cmd(
attr=key,
val=self._bool_to_str(val),
opr=opr,
)
)
elif key == "icmp_redirects":
commands.extend(
self._render_icmp_redirects(key, w, h, opr)
)
return commands
def _render_icmp_redirects(self, attr, w, h, opr):
"""
This function forms the commands for 'icmp_redirects' attributes
based on the 'opr'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h_red = {}
l_set = ("send", "receive")
if w[attr]:
if h and attr in h.keys():
h_red = h.get(attr) or {}
for item, value in iteritems(w[attr]):
if (
opr
and item in l_set
and not (h_red and self._is_w_same(w[attr], h_red, item))
):
commands.append(
self._form_attr_cmd(
attr=item, val=self._bool_to_str(value), opr=opr
)
)
elif (
not opr
and item in l_set
and not (h_red and self._is_w_same(w[attr], h_red, item))
):
commands.append(self._form_attr_cmd(attr=item, opr=opr))
return commands
def search_attrib_in_have(self, have, want, attr):
"""
This function returns the attribute if it is present in target config.
:param have: the target config.
:param want: the desired config.
:param attr: attribute name .
:return: attribute/None
"""
if have:
for h in have:
if h[attr] == want[attr]:
return h
return None
def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
"""
This function forms the command for leaf attribute.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
:return: generated command.
"""
command = self._compute_command(
key=key, attr=self._map_attrib(attr), val=val, opr=opr
)
return command
def _compute_command(
self, key=None, attr=None, val=None, remove=False, opr=True
):
"""
This function construct the add/delete command based on passed attributes.
:param key: parent key.
:param attr: attribute name
:param value: value
:param remove: True/False.
:param opr: True/False.
:return: generated command.
"""
if remove or not opr:
cmd = "delete firewall "
else:
cmd = "set firewall "
if key:
cmd += key.replace("_", "-") + " "
if attr:
cmd += attr.replace("_", "-")
if val and opr:
cmd += " '" + str(val) + "'"
return cmd
def _bool_to_str(self, val):
"""
This function converts the bool value into string.
:param val: bool value.
:return: enable/disable.
"""
return (
"enable"
if str(val) == "True"
else "disable"
if str(val) == "False"
else val
)
def _grp_type(self, val):
"""
This function returns the group member type based on value argument.
:param val: value.
:return: member type.
"""
return (
"address"
if val == "address_group"
else "network"
if val == "network_group"
else "port"
)
def _is_w_same(self, w, h, key):
"""
This function checks whether the key value is same in desired and
target config dictionary.
:param w: base config.
:param h: target config.
:param key:attribute name.
:return: True/False.
"""
return True if h and key in h and h[key] == w[key] else False
def _in_target(self, h, key):
"""
This function checks whether the target exist and key present in target config.
:param h: target config.
:param key: attribute name.
:return: True/False.
"""
return True if h and key in h else False
def _is_grp_del(self, w, h, key):
"""
This function checks whether group needed to be deleted based on
desired and target configs.
:param w: the desired config.
:param h: the target config.
:param key: group name.
:return: True/False.
"""
return (
True
if h and key in h and (not w or key not in w or not w[key])
else False
)
def _is_root_del(self, w, h, key):
"""
This function checks whether a root attribute which can have
further child attributes needed to be deleted.
:param w: the desired config.
:param h: the target config.
:param key: attribute name.
:return: True/False.
"""
return (
True
if h and key in h and (not w or key not in w or not w[key])
else False
)
def _is_del(self, b_set, h, key="number"):
"""
This function checks whether attribute needs to be deleted
when operation is false and attribute present in present target config.
:param b_set: attribute set.
:param h: target config.
:param key: number.
:return: True/False.
"""
return key in b_set and not (h and self._in_target(h, key))
def _map_attrib(self, attrib, type=None):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
regex = attrib.replace("_", "-")
if attrib == "send":
if type == "ipv6":
regex = "ipv6-send-redirects"
else:
regex = "send-redirects"
elif attrib == "ip_src_route":
if type == "ipv6":
regex = "ipv6-src-route"
elif attrib == "receive":
if type == "ipv6":
regex = "ipv6-receive-redirects"
else:
regex = "receive-redirects"
elif attrib == "disabled":
regex = "disable"
elif attrib == "all":
regex = "all-ping"
elif attrib == "broadcast":
regex = "broadcast-ping"
elif attrib == "validation":
regex = "source-validation"
return regex
diff --git a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py
index 7e19cdf..bd2fbab 100644
--- a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py
@@ -1,455 +1,454 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_firewall_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
search_obj_in_list,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
class Firewall_interfaces(ConfigBase):
"""
The vyos_firewall_interfaces class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"firewall_interfaces",
]
def __init__(self, module):
super(Firewall_interfaces, self).__init__(module)
def get_firewall_interfaces_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
firewall_interfaces_facts = facts["ansible_network_resources"].get(
"firewall_interfaces"
)
if not firewall_interfaces_facts:
return []
return firewall_interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_firewall_interfaces_facts = (
self.get_firewall_interfaces_facts()
)
else:
existing_firewall_interfaces_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(
self.set_config(existing_firewall_interfaces_facts)
)
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_firewall_interfaces_facts = (
self.get_firewall_interfaces_facts()
)
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_firewall_interfaces_facts(
data=running_config
)
else:
changed_firewall_interfaces_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_firewall_interfaces_facts
if result["changed"]:
result["after"] = changed_firewall_interfaces_facts
elif self.state == "gathered":
result["gathered"] = changed_firewall_interfaces_facts
result["warnings"] = warnings
return result
def set_config(self, existing_firewall_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_firewall_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, w, h):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not w
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(w, h))
elif self.state == "deleted":
commands.extend(self._state_deleted(w, h))
elif w:
if self.state == "merged" or self.state == "rendered":
commands.extend(self._state_merged(w, h))
elif self.state == "replaced":
commands.extend(self._state_replaced(w, h))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
for h in have:
w = search_obj_in_list(h["name"], want)
commands.extend(self._render_access_rules(h, w, opr=False))
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
for h_ar in have:
w_ar = search_obj_in_list(h_ar["name"], want)
if not w_ar and "access_rules" in h_ar:
commands.append(
self._compute_command(name=h_ar["name"], opr=False)
)
else:
h_rules = h_ar.get("access_rules") or []
key = "direction"
if w_ar:
w_rules = w_ar.get("access_rules") or []
if not w_rules and h_rules:
commands.append(
self._compute_command(
name=h_ar["name"], opr=False
)
)
if h_rules:
for h_rule in h_rules:
w_rule = search_obj_in_list(
h_rule["afi"], w_rules, key="afi"
)
have_rules = h_rule.get("rules") or []
if w_rule:
want_rules = w_rule.get("rules") or []
for h in have_rules:
if key in h:
w = search_obj_in_list(
h[key], want_rules, key=key
)
if (
not w
or key not in w
or (
"name" in h
and w
and "name" not in w
)
):
commands.append(
self._compute_command(
afi=h_rule["afi"],
name=h_ar["name"],
attrib=h[key],
opr=False,
)
)
commands.extend(self._state_merged(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
for w in want:
h = search_obj_in_list(w["name"], have)
commands.extend(self._render_access_rules(w, h))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
for w in want:
h = search_obj_in_list(w["name"], have)
if h and "access_rules" in h:
commands.extend(self._delete_access_rules(w, h, opr=False))
elif have:
for h in have:
if "access_rules" in h:
commands.append(
self._compute_command(name=h["name"], opr=False)
)
return commands
def _delete_access_rules(self, want, have, opr=False):
"""
This function forms the delete commands based on the 'opr' type
for 'access_rules' attributes.
:param want: desired config.
:param have: target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_rules = {}
w_rs = deepcopy(remove_empties(want))
w_rules = w_rs.get("access_rules") or []
if have:
h_rs = deepcopy(remove_empties(have))
h_rules = h_rs.get("access_rules") or []
# if all firewall config needed to be deleted for specific interface
# when operation is delete.
if not w_rules and h_rules:
commands.append(self._compute_command(name=want["name"], opr=opr))
if w_rules:
for w in w_rules:
h = search_obj_in_list(w["afi"], h_rules, key="afi")
commands.extend(self._delete_rules(want["name"], w, h))
return commands
def _delete_rules(self, name, want, have, opr=False):
"""
This function forms the delete commands based on the 'opr' type
for rules attributes.
:param name: interface id/name.
:param want: desired config.
:param have: target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_rules = []
key = "direction"
w_rules = want.get("rules") or []
if have:
h_rules = have.get("rules") or []
# when rule set needed to be removed on
# (inbound|outbound|local interface)
if h_rules and not w_rules:
for h in h_rules:
if key in h:
commands.append(
self._compute_command(
afi=want["afi"], name=name, attrib=h[key], opr=opr
)
)
for w in w_rules:
h = search_obj_in_list(w[key], h_rules, key=key)
if (
key in w
and h
and key in h
and "name" in w
and "name" in h
and w["name"] == h["name"]
):
commands.append(
self._compute_command(
afi=want["afi"],
name=name,
attrib=w[key],
value=w["name"],
opr=opr,
)
)
return commands
def _render_access_rules(self, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for 'access_rules' attributes.
:param want: desired config.
:param have: target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_rules = {}
w_rs = deepcopy(remove_empties(want))
w_rules = w_rs.get("access_rules") or []
if have:
h_rs = deepcopy(remove_empties(have))
h_rules = h_rs.get("access_rules") or []
if w_rules:
for w in w_rules:
h = search_obj_in_list(w["afi"], h_rules, key="afi")
commands.extend(self._render_rules(want["name"], w, h, opr))
return commands
def _render_rules(self, name, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for rules attributes.
:param name: interface id/name.
:param want: desired config.
:param have: target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_rules = []
key = "direction"
w_rules = want.get("rules") or []
if have:
h_rules = have.get("rules") or []
for w in w_rules:
h = search_obj_in_list(w[key], h_rules, key=key)
if key in w:
if opr:
if "name" in w and not (
h and h[key] == w[key] and h["name"] == w["name"]
):
commands.append(
self._compute_command(
afi=want["afi"],
name=name,
attrib=w[key],
value=w["name"],
)
)
elif not (h and key in h):
commands.append(
self._compute_command(
afi=want["afi"], name=name, attrib=w[key]
)
)
elif not opr:
if (
not h
or key not in h
or ("name" in w and h and "name" not in h)
):
commands.append(
self._compute_command(
afi=want["afi"],
name=name,
attrib=w[key],
opr=opr,
)
)
return commands
def _compute_command(
self, afi=None, name=None, attrib=None, value=None, opr=True
):
"""
This function construct the add/delete command based on passed attributes.
:param afi: address type.
:param name: interface name.
:param attrib: attribute name.
:param value: attribute value.
:param opr: operation flag.
:return: generated command.
"""
if not opr:
cmd = "delete interfaces ethernet" + " " + name + " firewall"
else:
cmd = "set interfaces ethernet" + " " + name + " firewall"
if attrib:
cmd += " " + attrib
if afi:
cmd += " " + self._get_fw_type(afi)
if value:
cmd += " '" + str(value) + "'"
return cmd
def _get_fw_type(self, afi):
"""
This function returns the firewall rule-set type based on IP address.
:param afi: address type
:return: rule-set type.
"""
return "ipv6-name" if afi == "ipv6" else "name"
diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
index 1223e59..5c37741 100644
--- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
@@ -1,1018 +1,1017 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_firewall_rules class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
list_diff_want_only,
)
class Firewall_rules(ConfigBase):
"""
The vyos_firewall_rules class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"firewall_rules",
]
def __init__(self, module):
super(Firewall_rules, self).__init__(module)
def get_firewall_rules_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
firewall_rules_facts = facts["ansible_network_resources"].get(
"firewall_rules"
)
if not firewall_rules_facts:
return []
return firewall_rules_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_firewall_rules_facts = self.get_firewall_rules_facts()
else:
existing_firewall_rules_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_firewall_rules_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_firewall_rules_facts = self.get_firewall_rules_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_firewall_rules_facts(
data=running_config
)
else:
changed_firewall_rules_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_firewall_rules_facts
if result["changed"]:
result["after"] = changed_firewall_rules_facts
elif self.state == "gathered":
result["gathered"] = changed_firewall_rules_facts
result["warnings"] = warnings
return result
def set_config(self, existing_firewall_rules_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_firewall_rules_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, w, h):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not w
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(w, h))
elif self.state == "deleted":
commands.extend(self._state_deleted(w, h))
elif w:
if self.state == "merged" or self.state == "rendered":
commands.extend(self._state_merged(w, h))
elif self.state == "replaced":
commands.extend(self._state_replaced(w, h))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
for h in have:
r_sets = self._get_r_sets(h)
for rs in r_sets:
w = self.search_r_sets_in_have(want, rs["name"], "r_list")
commands.extend(
self._add_r_sets(h["afi"], rs, w, opr=False)
)
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
for h in have:
r_sets = self._get_r_sets(h)
for rs in r_sets:
w = self.search_r_sets_in_have(want, rs["name"], "r_list")
if not w:
commands.append(
self._compute_command(
h["afi"], rs["name"], remove=True
)
)
else:
commands.extend(
self._add_r_sets(h["afi"], rs, w, opr=False)
)
commands.extend(self._state_merged(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
for w in want:
r_sets = self._get_r_sets(w)
for rs in r_sets:
h = self.search_r_sets_in_have(have, rs["name"], "r_list")
commands.extend(self._add_r_sets(w["afi"], rs, h))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
for w in want:
r_sets = self._get_r_sets(w)
if r_sets:
for rs in r_sets:
h = self.search_r_sets_in_have(
have, rs["name"], "r_list"
)
if h:
commands.append(
self._compute_command(
w["afi"], h["name"], remove=True
)
)
elif have:
for h in have:
if h["afi"] == w["afi"]:
commands.append(
self._compute_command(w["afi"], remove=True)
)
elif have:
for h in have:
r_sets = self._get_r_sets(h)
if r_sets:
commands.append(
self._compute_command(afi=h["afi"], remove=True)
)
return commands
def _add_r_sets(self, afi, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for rule-sets attributes.
:param afi: address type.
:param want: desired config.
:param have: target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
l_set = ("description", "default_action", "enable_default_log")
h_rs = {}
h_rules = {}
w_rs = deepcopy(remove_empties(want))
w_rules = w_rs.pop("rules", None)
if have:
h_rs = deepcopy(remove_empties(have))
h_rules = h_rs.pop("rules", None)
if w_rs:
for key, val in iteritems(w_rs):
if (
opr
and key in l_set
and not (h_rs and self._is_w_same(w_rs, h_rs, key))
):
if key == "enable_default_log":
if val and (
not h_rs or key not in h_rs or not h_rs[key]
):
commands.append(
self._add_rs_base_attrib(
afi, want["name"], key, w_rs
)
)
else:
commands.append(
self._add_rs_base_attrib(
afi, want["name"], key, w_rs
)
)
elif not opr and key in l_set:
if (
key == "enable_default_log"
and val
and h_rs
and (key not in h_rs or not h_rs[key])
):
commands.append(
self._add_rs_base_attrib(
afi, want["name"], key, w_rs, opr
)
)
elif not (h_rs and self._in_target(h_rs, key)):
commands.append(
self._add_rs_base_attrib(
afi, want["name"], key, w_rs, opr
)
)
commands.extend(
self._add_rules(afi, want["name"], w_rules, h_rules, opr)
)
if h_rules:
have["rules"] = h_rules
if w_rules:
want["rules"] = w_rules
return commands
def _add_rules(self, afi, name, w_rules, h_rules, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for rules attributes.
:param want: desired config.
:param have: target config.
:return: generated commands list.
"""
commands = []
l_set = (
"ipsec",
"action",
"number",
"protocol",
"fragment",
"disabled",
"description",
)
if w_rules:
for w in w_rules:
cmd = self._compute_command(afi, name, w["number"], opr=opr)
h = self.search_r_sets_in_have(
h_rules, w["number"], type="rules"
)
for key, val in iteritems(w):
if val:
if (
opr
and key in l_set
and not (h and self._is_w_same(w, h, key))
):
if key == "disabled":
if not (
not val
and (not h or key not in h or not h[key])
):
commands.append(
self._add_r_base_attrib(
afi, name, key, w
)
)
else:
commands.append(
self._add_r_base_attrib(afi, name, key, w)
)
elif not opr:
if key == "number" and self._is_del(l_set, h):
commands.append(
self._add_r_base_attrib(
afi, name, key, w, opr=opr
)
)
continue
elif (
key == "disabled"
and val
and h
and (key not in h or not h[key])
):
commands.append(
self._add_r_base_attrib(
afi, name, key, w, opr=opr
)
)
elif (
key in l_set
and not (h and self._in_target(h, key))
and not self._is_del(l_set, h)
):
commands.append(
self._add_r_base_attrib(
afi, name, key, w, opr=opr
)
)
elif key == "p2p":
commands.extend(self._add_p2p(key, w, h, cmd, opr))
elif key == "tcp":
commands.extend(self._add_tcp(key, w, h, cmd, opr))
elif key == "time":
commands.extend(
self._add_time(key, w, h, cmd, opr)
)
elif key == "icmp":
commands.extend(
self._add_icmp(key, w, h, cmd, opr)
)
elif key == "state":
commands.extend(
self._add_state(key, w, h, cmd, opr)
)
elif key == "limit":
commands.extend(
self._add_limit(key, w, h, cmd, opr)
)
elif key == "recent":
commands.extend(
self._add_recent(key, w, h, cmd, opr)
)
elif key == "destination" or key == "source":
commands.extend(
self._add_src_or_dest(key, w, h, cmd, opr)
)
return commands
def _add_p2p(self, attr, w, h, cmd, opr):
"""
This function forms the set/delete commands based on the 'opr' type
for p2p applications attributes.
:param want: desired config.
:param have: target config.
:return: generated commands list.
"""
commands = []
have = []
if w:
want = w.get(attr) or []
if h:
have = h.get(attr) or []
if want:
if opr:
applications = list_diff_want_only(want, have)
for app in applications:
commands.append(
cmd + (" " + attr + " " + app["application"])
)
elif not opr and have:
applications = list_diff_want_only(want, have)
for app in applications:
commands.append(
cmd + (" " + attr + " " + app["application"])
)
return commands
def _add_state(self, attr, w, h, cmd, opr):
"""
This function forms the command for 'state' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
h_state = {}
commands = []
l_set = ("new", "invalid", "related", "established")
if w[attr]:
if h and attr in h.keys():
h_state = h.get(attr) or {}
for item, val in iteritems(w[attr]):
if (
opr
and item in l_set
and not (
h_state and self._is_w_same(w[attr], h_state, item)
)
):
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ item
+ " "
+ self._bool_to_str(val)
)
)
elif (
not opr
and item in l_set
and not (h_state and self._in_target(h_state, item))
):
commands.append(cmd + (" " + attr + " " + item))
return commands
def _add_recent(self, attr, w, h, cmd, opr):
"""
This function forms the command for 'recent' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
commands = []
h_recent = {}
l_set = ("count", "time")
if w[attr]:
if h and attr in h.keys():
h_recent = h.get(attr) or {}
for item, val in iteritems(w[attr]):
if (
opr
and item in l_set
and not (
h_recent and self._is_w_same(w[attr], h_recent, item)
)
):
commands.append(
cmd + (" " + attr + " " + item + " " + str(val))
)
elif (
not opr
and item in l_set
and not (h_recent and self._in_target(h_recent, item))
):
commands.append(cmd + (" " + attr + " " + item))
return commands
def _add_icmp(self, attr, w, h, cmd, opr):
"""
This function forms the commands for 'icmp' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
commands = []
h_icmp = {}
l_set = ("code", "type", "type_name")
if w[attr]:
if h and attr in h.keys():
h_icmp = h.get(attr) or {}
for item, val in iteritems(w[attr]):
if (
opr
and item in l_set
and not (h_icmp and self._is_w_same(w[attr], h_icmp, item))
):
if item == "type_name":
if "ipv6-name" in cmd:
commands.append(
cmd
+ (" " + "icmpv6" + " " + "type" + " " + val)
)
else:
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ item.replace("_", "-")
+ " "
+ val
)
)
else:
commands.append(
cmd + (" " + attr + " " + item + " " + str(val))
)
elif (
not opr
and item in l_set
and not (h_icmp and self._in_target(h_icmp, item))
):
commands.append(cmd + (" " + attr + " " + item))
return commands
def _add_time(self, attr, w, h, cmd, opr):
"""
This function forms the commands for 'time' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
commands = []
h_time = {}
l_set = (
"utc",
"stopdate",
"stoptime",
"weekdays",
"monthdays",
"startdate",
"starttime",
)
if w[attr]:
if h and attr in h.keys():
h_time = h.get(attr) or {}
for item, val in iteritems(w[attr]):
if (
opr
and item in l_set
and not (h_time and self._is_w_same(w[attr], h_time, item))
):
if item == "utc":
if not (
not val and (not h_time or item not in h_time)
):
commands.append(cmd + (" " + attr + " " + item))
else:
commands.append(
cmd + (" " + attr + " " + item + " " + val)
)
elif (
not opr
and item in l_set
and not (h_time and self._is_w_same(w[attr], h_time, item))
):
commands.append(cmd + (" " + attr + " " + item))
return commands
def _add_tcp(self, attr, w, h, cmd, opr):
"""
This function forms the commands for 'tcp' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
h_tcp = {}
commands = []
if w[attr]:
key = "flags"
flags = w[attr].get(key) or {}
if flags:
if h and key in h[attr].keys():
h_tcp = h[attr].get(key) or {}
if flags:
if opr and not (
h_tcp and self._is_w_same(w[attr], h[attr], key)
):
commands.append(
cmd + (" " + attr + " " + key + " " + flags)
)
if not opr and not (
h_tcp and self._is_w_same(w[attr], h[attr], key)
):
commands.append(
cmd + (" " + attr + " " + key + " " + flags)
)
return commands
def _add_limit(self, attr, w, h, cmd, opr):
"""
This function forms the commands for 'limit' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
h_limit = {}
commands = []
if w[attr]:
key = "burst"
if (
opr
and key in w[attr].keys()
and not (
h
and attr in h.keys()
and self._is_w_same(w[attr], h[attr], key)
)
):
commands.append(
cmd
+ (" " + attr + " " + key + " " + str(w[attr].get(key)))
)
elif (
not opr
and key in w[attr].keys()
and not (
h and attr in h.keys() and self._in_target(h[attr], key)
)
):
commands.append(
cmd
+ (" " + attr + " " + key + " " + str(w[attr].get(key)))
)
key = "rate"
rate = w[attr].get(key) or {}
if rate:
if h and key in h[attr].keys():
h_limit = h[attr].get(key) or {}
if "unit" in rate and "number" in rate:
if opr and not (
h_limit
and self._is_w_same(rate, h_limit, "unit")
and self.is_w_same(rate, h_limit, "number")
):
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ key
+ " "
+ str(rate["number"])
+ "/"
+ rate["unit"]
)
)
if not opr and not (
h_limit
and self._is_w_same(rate, h_limit, "unit")
and self._is_w_same(rate, h_limit, "number")
):
commands.append(cmd + (" " + attr + " " + key))
return commands
def _add_src_or_dest(self, attr, w, h, cmd, opr=True):
"""
This function forms the commands for 'src/dest' attributes based on the 'opr'.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
commands = []
h_group = {}
g_set = ("port_group", "address_group", "network_group")
if w[attr]:
keys = ("address", "mac_address", "port")
for key in keys:
if (
opr
and key in w[attr].keys()
and not (
h
and attr in h.keys()
and self._is_w_same(w[attr], h[attr], key)
)
):
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ key.replace("_", "-")
+ " "
+ w[attr].get(key)
)
)
elif (
not opr
and key in w[attr].keys()
and not (
h
and attr in h.keys()
and self._in_target(h[attr], key)
)
):
commands.append(cmd + (" " + attr + " " + key))
key = "group"
group = w[attr].get(key) or {}
if group:
if h and key in h[attr].keys():
h_group = h[attr].get(key) or {}
for item, val in iteritems(group):
if val:
if (
opr
and item in g_set
and not (
h_group
and self._is_w_same(group, h_group, item)
)
):
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ key
+ " "
+ item.replace("_", "-")
+ " "
+ val
)
)
elif (
not opr
and item in g_set
and not (
h_group and self._in_target(h_group, item)
)
):
commands.append(
cmd
+ (
" "
+ attr
+ " "
+ key
+ " "
+ item.replace("_", "-")
)
)
return commands
def search_r_sets_in_have(self, have, w_name, type="rule_sets"):
"""
This function returns the rule-set/rule if it is present in target config.
:param have: target config.
:param w_name: rule-set name.
:param type: rule_sets/rule/r_list.
:return: rule-set/rule.
"""
if have:
key = "name"
if type == "rules":
key = "number"
for r in have:
if r[key] == w_name:
return r
elif type == "r_list":
for h in have:
r_sets = self._get_r_sets(h)
for rs in r_sets:
if rs[key] == w_name:
return rs
else:
for rs in have:
if rs[key] == w_name:
return rs
return None
def _get_r_sets(self, item, type="rule_sets"):
"""
This function returns the list of rule-sets/rules.
:param item: config dictionary.
:param type: rule_sets/rule/r_list.
:return: list of rule-sets/rules.
"""
rs_list = []
r_sets = item[type]
if r_sets:
for rs in r_sets:
rs_list.append(rs)
return rs_list
def _compute_command(
self,
afi,
name=None,
number=None,
attrib=None,
value=None,
remove=False,
opr=True,
):
"""
This function construct the add/delete command based on passed attributes.
:param afi: address type.
:param name: rule-set name.
:param number: rule-number.
:param attrib: attribute name.
:param value: value.
:param remove: True if delete command needed to be construct.
:param opr: opeeration flag.
:return: generated command.
"""
if remove or not opr:
cmd = "delete firewall " + self._get_fw_type(afi)
else:
cmd = "set firewall " + self._get_fw_type(afi)
if name:
cmd += " " + name
if number:
cmd += " rule " + str(number)
if attrib:
cmd += " " + attrib.replace("_", "-")
if (
value
and opr
and attrib != "enable_default_log"
and attrib != "disabled"
):
cmd += " '" + str(value) + "'"
return cmd
def _add_r_base_attrib(self, afi, name, attr, rule, opr=True):
"""
This function forms the command for 'rules' attributes which doesn't
have further sub attributes.
:param afi: address type.
:param name: rule-set name
:param attrib: attribute name
:param rule: rule config dictionary.
:param opr: True/False.
:return: generated command.
"""
if attr == "number":
command = self._compute_command(
afi=afi, name=name, number=rule["number"], opr=opr
)
else:
command = self._compute_command(
afi=afi,
name=name,
number=rule["number"],
attrib=attr,
value=rule[attr],
opr=opr,
)
return command
def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True):
"""
This function forms the command for 'rule-sets' attributes which doesn't
have further sub attributes.
:param afi: address type.
:param name: rule-set name
:param attrib: attribute name
:param rule: rule config dictionary.
:param opr: True/False.
:return: generated command.
"""
command = self._compute_command(
afi=afi, name=name, attrib=attrib, value=rule[attrib], opr=opr
)
return command
def _bool_to_str(self, val):
"""
This function converts the bool value into string.
:param val: bool value.
:return: enable/disable.
"""
return "enable" if val else "disable"
def _get_fw_type(self, afi):
"""
This function returns the firewall rule-set type based on IP address.
:param afi: address type
:return: rule-set type.
"""
return "ipv6-name" if afi == "ipv6" else "name"
def _is_del(self, l_set, h, key="number"):
"""
This function checks whether rule needs to be deleted based on
the rule number.
:param l_set: attribute set.
:param h: target config.
:param key: number.
:return: True/False.
"""
return key in l_set and not (h and self._in_target(h, key))
def _is_w_same(self, w, h, key):
"""
This function checks whether the key value is same in base and
target config dictionary.
:param w: base config.
:param h: target config.
:param key:attribute name.
:return: True/False.
"""
return True if h and key in h and h[key] == w[key] else False
def _in_target(self, h, key):
"""
This function checks whether the target nexist and key present in target config.
:param h: target config.
:param key: attribute name.
:return: True/False.
"""
return True if h and key in h else False
def _is_base_attrib(self, key):
"""
This function checks whether key is present in predefined
based attribute set.
:param key:
:return: True/False.
"""
r_set = (
"p2p",
"ipsec",
"action",
"fragment",
"protocol",
"disabled",
"description",
"mac_address",
"default_action",
"enable_default_log",
)
return True if key in r_set else False
diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
index ea0a05a..51bf98e 100644
--- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
@@ -1,352 +1,351 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
dict_diff,
remove_empties,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
search_obj_in_list,
get_interface_type,
dict_delete,
)
class Interfaces(ConfigBase):
"""
The vyos_interfaces class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = ["interfaces"]
def __init__(self, module):
super(Interfaces, self).__init__(module)
def get_interfaces_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
interfaces_facts = facts["ansible_network_resources"].get("interfaces")
if not interfaces_facts:
return []
return interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
commands = list()
warnings = list()
if self.state in self.ACTION_STATES:
existing_interfaces_facts = self.get_interfaces_facts()
else:
existing_interfaces_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_interfaces_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_interfaces_facts = self.get_interfaces_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_interfaces_facts(data=running_config)
else:
changed_interfaces_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_interfaces_facts
if result["changed"]:
result["after"] = changed_interfaces_facts
elif self.state == "gathered":
result["gathered"] = changed_interfaces_facts
result["warnings"] = warnings
return result
def set_config(self, existing_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not want
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
elif self.state == "deleted":
if not want:
for intf in have:
commands.extend(
self._state_deleted({"name": intf["name"]}, intf)
)
else:
for item in want:
obj_in_have = search_obj_in_list(item["name"], have)
commands.extend(self._state_deleted(item, obj_in_have))
else:
for item in want:
name = item["name"]
obj_in_have = search_obj_in_list(name, have)
if not obj_in_have:
obj_in_have = {"name": name}
if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(item, obj_in_have))
elif self.state == "replaced":
commands.extend(self._state_replaced(item, obj_in_have))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._state_deleted(want, have))
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for intf in have:
intf_in_want = search_obj_in_list(intf["name"], want)
if not intf_in_want:
commands.extend(
self._state_deleted({"name": intf["name"]}, intf)
)
for intf in want:
intf_in_have = search_obj_in_list(intf["name"], have)
commands.extend(self._state_replaced(intf, intf_in_have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(have)
want_vifs = want_copy.pop("vifs", [])
have_vifs = have_copy.pop("vifs", [])
updates = dict_diff(have_copy, want_copy)
if updates:
for key, value in iteritems(updates):
commands.append(
self._compute_commands(
key=key, value=value, interface=want_copy["name"]
)
)
if want_vifs:
for want_vif in want_vifs:
have_vif = search_obj_in_list(
want_vif["vlan_id"], have_vifs, key="vlan_id"
)
if not have_vif:
have_vif = {
"vlan_id": want_vif["vlan_id"],
"enabled": True,
}
vif_updates = dict_diff(have_vif, want_vif)
if vif_updates:
for key, value in iteritems(vif_updates):
commands.append(
self._compute_commands(
key=key,
value=value,
interface=want_copy["name"],
vif=want_vif["vlan_id"],
)
)
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(have)
want_vifs = want_copy.pop("vifs", [])
have_vifs = have_copy.pop("vifs", [])
for key in dict_delete(have_copy, want_copy).keys():
if key == "enabled":
continue
commands.append(
self._compute_commands(
key=key, interface=want_copy["name"], remove=True
)
)
if have_copy["enabled"] is False:
commands.append(
self._compute_commands(
key="enabled", value=True, interface=want_copy["name"]
)
)
if have_vifs:
for have_vif in have_vifs:
want_vif = search_obj_in_list(
have_vif["vlan_id"], want_vifs, key="vlan_id"
)
if not want_vif:
want_vif = {
"vlan_id": have_vif["vlan_id"],
"enabled": True,
}
for key in dict_delete(have_vif, want_vif).keys():
if key == "enabled":
continue
commands.append(
self._compute_commands(
key=key,
interface=want_copy["name"],
vif=want_vif["vlan_id"],
remove=True,
)
)
if have_vif["enabled"] is False:
commands.append(
self._compute_commands(
key="enabled",
value=True,
interface=want_copy["name"],
vif=want_vif["vlan_id"],
)
)
return commands
def _compute_commands(
self, interface, key, vif=None, value=None, remove=False
):
intf_context = "interfaces {0} {1}".format(
get_interface_type(interface), interface
)
set_cmd = "set {0}".format(intf_context)
del_cmd = "delete {0}".format(intf_context)
if vif:
set_cmd = set_cmd + (" vif {0}".format(vif))
del_cmd = del_cmd + (" vif {0}".format(vif))
if key == "enabled":
if not value:
command = "{0} disable".format(set_cmd)
else:
command = "{0} disable".format(del_cmd)
else:
if not remove:
command = "{0} {1} '{2}'".format(set_cmd, key, value)
else:
command = "{0} {1}".format(del_cmd, key)
return command
diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
index 45569d3..122cc1e 100644
--- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py
@@ -1,345 +1,344 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_l3_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
search_obj_in_list,
get_interface_type,
diff_list_of_dicts,
)
class L3_interfaces(ConfigBase):
"""
The vyos_l3_interfaces class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"l3_interfaces",
]
def __init__(self, module):
super(L3_interfaces, self).__init__(module)
def get_l3_interfaces_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
l3_interfaces_facts = facts["ansible_network_resources"].get(
"l3_interfaces"
)
if not l3_interfaces_facts:
return []
return l3_interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
else:
existing_l3_interfaces_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_l3_interfaces_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_l3_interfaces_facts = self.get_l3_interfaces_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_l3_interfaces_facts(
data=running_config
)
else:
changed_l3_interfaces_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_l3_interfaces_facts
if result["changed"]:
result["after"] = changed_l3_interfaces_facts
elif self.state == "gathered":
result["gathered"] = changed_l3_interfaces_facts
result["warnings"] = warnings
return result
def set_config(self, existing_l3_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_l3_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
state = self._module.params["state"]
if (
state in ("merged", "replaced", "overridden", "rendered")
and not want
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
state
)
)
if state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
elif state == "deleted":
if not want:
for intf in have:
commands.extend(
self._state_deleted({"name": intf["name"]}, intf)
)
else:
for item in want:
obj_in_have = search_obj_in_list(item["name"], have)
commands.extend(self._state_deleted(item, obj_in_have))
else:
for item in want:
name = item["name"]
obj_in_have = search_obj_in_list(name, have)
if not obj_in_have:
obj_in_have = {"name": item["name"]}
if state in ("merged", "rendered"):
commands.extend(self._state_merged(item, obj_in_have))
elif state == "replaced":
commands.extend(self._state_replaced(item, obj_in_have))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._state_deleted(want, have))
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for intf in have:
intf_in_want = search_obj_in_list(intf["name"], want)
if not intf_in_want:
commands.extend(
self._state_deleted({"name": intf["name"]}, intf)
)
for intf in want:
intf_in_have = search_obj_in_list(intf["name"], have)
commands.extend(self._state_replaced(intf, intf_in_have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(remove_empties(have))
want_vifs = want_copy.pop("vifs", [])
have_vifs = have_copy.pop("vifs", [])
for update in self._get_updates(want_copy, have_copy):
for key, value in iteritems(update):
commands.append(
self._compute_commands(
key=key, value=value, interface=want_copy["name"]
)
)
if want_vifs:
for want_vif in want_vifs:
have_vif = search_obj_in_list(
want_vif["vlan_id"], have_vifs, key="vlan_id"
)
if not have_vif:
have_vif = {}
for update in self._get_updates(want_vif, have_vif):
for key, value in iteritems(update):
commands.append(
self._compute_commands(
key=key,
value=value,
interface=want_copy["name"],
vif=want_vif["vlan_id"],
)
)
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(have)
want_vifs = want_copy.pop("vifs", [])
have_vifs = have_copy.pop("vifs", [])
for update in self._get_updates(have_copy, want_copy):
for key, value in iteritems(update):
commands.append(
self._compute_commands(
key=key,
value=value,
interface=want_copy["name"],
remove=True,
)
)
if have_vifs:
for have_vif in have_vifs:
want_vif = search_obj_in_list(
have_vif["vlan_id"], want_vifs, key="vlan_id"
)
if not want_vif:
want_vif = {"vlan_id": have_vif["vlan_id"]}
for update in self._get_updates(have_vif, want_vif):
for key, value in iteritems(update):
commands.append(
self._compute_commands(
key=key,
interface=want_copy["name"],
value=value,
vif=want_vif["vlan_id"],
remove=True,
)
)
return commands
def _compute_commands(
self, interface, key, vif=None, value=None, remove=False
):
intf_context = "interfaces {0} {1}".format(
get_interface_type(interface), interface
)
set_cmd = "set {0}".format(intf_context)
del_cmd = "delete {0}".format(intf_context)
if vif:
set_cmd = set_cmd + (" vif {0}".format(vif))
del_cmd = del_cmd + (" vif {0}".format(vif))
if remove:
command = "{0} {1} '{2}'".format(del_cmd, key, value)
else:
command = "{0} {1} '{2}'".format(set_cmd, key, value)
return command
def _get_updates(self, want, have):
updates = []
updates = diff_list_of_dicts(
want.get("ipv4", []), have.get("ipv4", [])
)
updates.extend(
diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", []))
)
return updates
diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
index 94c526c..452670f 100644
--- a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py
@@ -1,469 +1,468 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos_lag_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
dict_diff,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
search_obj_in_list,
get_lst_diff_for_dicts,
list_diff_want_only,
list_diff_have_only,
)
class Lag_interfaces(ConfigBase):
"""
The vyos_lag_interfaces class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"lag_interfaces",
]
params = [
"arp_monitor",
"hash_policy",
"members",
"mode",
"name",
"primary",
]
def __init__(self, module):
super(Lag_interfaces, self).__init__(module)
def get_lag_interfaces_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
lag_interfaces_facts = facts["ansible_network_resources"].get(
"lag_interfaces"
)
if not lag_interfaces_facts:
return []
return lag_interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
else:
existing_lag_interfaces_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_lag_interfaces_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_lag_interfaces_facts(
data=running_config
)
else:
changed_lag_interfaces_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_lag_interfaces_facts
if result["changed"]:
result["after"] = changed_lag_interfaces_facts
elif self.state == "gathered":
result["gathered"] = changed_lag_interfaces_facts
result["warnings"] = warnings
return result
def set_config(self, existing_lag_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_lag_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not want
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(want, have))
elif self.state == "deleted":
if want:
for want_item in want:
name = want_item["name"]
obj_in_have = search_obj_in_list(name, have)
commands.extend(self._state_deleted(obj_in_have))
else:
for have_item in have:
commands.extend(self._state_deleted(have_item))
else:
for want_item in want:
name = want_item["name"]
obj_in_have = search_obj_in_list(name, have)
if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want_item, obj_in_have))
elif self.state == "replaced":
commands.extend(
self._state_replaced(want_item, obj_in_have)
)
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._render_del_commands(want, have))
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for have_item in have:
lag_name = have_item["name"]
obj_in_want = search_obj_in_list(lag_name, want)
if not obj_in_want:
commands.extend(self._purge_attribs(have_item))
for want_item in want:
name = want_item["name"]
obj_in_have = search_obj_in_list(name, have)
commands.extend(self._state_replaced(want_item, obj_in_have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
if have:
commands.extend(self._render_updates(want, have))
else:
commands.extend(self._render_set_commands(want))
return commands
def _state_deleted(self, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if have:
commands.extend(self._purge_attribs(have))
return commands
def _render_updates(self, want, have):
commands = []
temp_have_members = have.pop("members", None)
temp_want_members = want.pop("members", None)
updates = dict_diff(have, want)
if temp_have_members:
have["members"] = temp_have_members
if temp_want_members:
want["members"] = temp_want_members
commands.extend(self._add_bond_members(want, have))
if updates:
for key, value in iteritems(updates):
if value:
if key == "arp_monitor":
commands.extend(
self._add_arp_monitor(updates, key, want, have)
)
else:
commands.append(
self._compute_command(
have["name"], key, str(value)
)
)
return commands
def _render_set_commands(self, want):
commands = []
have = []
params = Lag_interfaces.params
for attrib in params:
value = want[attrib]
if value:
if attrib == "arp_monitor":
commands.extend(
self._add_arp_monitor(want, attrib, want, have)
)
elif attrib == "members":
commands.extend(self._add_bond_members(want, have))
elif attrib != "name":
commands.append(
self._compute_command(
want["name"], attrib, value=str(value)
)
)
return commands
def _purge_attribs(self, have):
commands = []
for item in Lag_interfaces.params:
if have.get(item):
if item == "members":
commands.extend(self._delete_bond_members(have))
elif item != "name":
commands.append(
self._compute_command(
have["name"], attrib=item, remove=True
)
)
return commands
def _render_del_commands(self, want, have):
commands = []
params = Lag_interfaces.params
for attrib in params:
if attrib == "members":
commands.extend(self._update_bond_members(attrib, want, have))
elif attrib == "arp_monitor":
commands.extend(self._update_arp_monitor(attrib, want, have))
elif have.get(attrib) and not want.get(attrib):
commands.append(
self._compute_command(have["name"], attrib, remove=True)
)
return commands
def _add_bond_members(self, want, have):
commands = []
diff_members = get_lst_diff_for_dicts(want, have, "members")
if diff_members:
for key in diff_members:
commands.append(
self._compute_command(
key["member"],
"bond-group",
want["name"],
type="ethernet",
)
)
return commands
def _add_arp_monitor(self, updates, key, want, have):
commands = []
arp_monitor = updates.get(key) or {}
diff_targets = self._get_arp_monitor_target_diff(
want, have, key, "target"
)
if "interval" in arp_monitor:
commands.append(
self._compute_command(
key=want["name"] + " arp-monitor",
attrib="interval",
value=str(arp_monitor["interval"]),
)
)
if diff_targets:
for target in diff_targets:
commands.append(
self._compute_command(
key=want["name"] + " arp-monitor",
attrib="target",
value=target,
)
)
return commands
def _delete_bond_members(self, have):
commands = []
for member in have["members"]:
commands.append(
self._compute_command(
member["member"],
"bond-group",
have["name"],
remove=True,
type="ethernet",
)
)
return commands
def _update_arp_monitor(self, key, want, have):
commands = []
want_arp_target = []
have_arp_target = []
want_arp_monitor = want.get(key) or {}
have_arp_monitor = have.get(key) or {}
if want_arp_monitor and "target" in want_arp_monitor:
want_arp_target = want_arp_monitor["target"]
if have_arp_monitor and "target" in have_arp_monitor:
have_arp_target = have_arp_monitor["target"]
if "interval" in have_arp_monitor and not want_arp_monitor:
commands.append(
self._compute_command(
key=have["name"] + " arp-monitor",
attrib="interval",
remove=True,
)
)
if "target" in have_arp_monitor:
target_diff = list_diff_have_only(want_arp_target, have_arp_target)
if target_diff:
for target in target_diff:
commands.append(
self._compute_command(
key=have["name"] + " arp-monitor",
attrib="target",
value=target,
remove=True,
)
)
return commands
def _update_bond_members(self, key, want, have):
commands = []
want_members = want.get(key) or []
have_members = have.get(key) or []
members_diff = list_diff_have_only(want_members, have_members)
if members_diff:
for member in members_diff:
commands.append(
self._compute_command(
member["member"],
"bond-group",
have["name"],
True,
"ethernet",
)
)
return commands
def _get_arp_monitor_target_diff(
self, want_list, have_list, dict_name, lst
):
want_arp_target = []
have_arp_target = []
want_arp_monitor = want_list.get(dict_name) or {}
if want_arp_monitor and lst in want_arp_monitor:
want_arp_target = want_arp_monitor[lst]
if not have_list:
diff = want_arp_target
else:
have_arp_monitor = have_list.get(dict_name) or {}
if have_arp_monitor and lst in have_arp_monitor:
have_arp_target = have_arp_monitor[lst]
diff = list_diff_want_only(want_arp_target, have_arp_target)
return diff
def _compute_command(
self, key, attrib, value=None, remove=False, type="bonding"
):
if remove:
cmd = "delete interfaces " + type
else:
cmd = "set interfaces " + type
cmd += " " + key
if attrib == "arp_monitor":
attrib = "arp-monitor"
elif attrib == "hash_policy":
attrib = "hash-policy"
cmd += " " + attrib
if value:
cmd += " '" + value + "'"
return cmd
diff --git a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
index 284fe12..c70d27f 100644
--- a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
+++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py
@@ -1,287 +1,286 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos_lldp_global class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
dict_diff,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
get_lst_diff_for_dicts,
list_diff_have_only,
)
class Lldp_global(ConfigBase):
"""
The vyos_lldp_global class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"lldp_global",
]
params = ["enable", "address", "snmp", "legacy_protocols"]
def __init__(self, module):
super(Lldp_global, self).__init__(module)
def get_lldp_global_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
lldp_global_facts = facts["ansible_network_resources"].get(
"lldp_global"
)
if not lldp_global_facts:
return []
return lldp_global_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_lldp_global_facts = self.get_lldp_global_facts()
else:
existing_lldp_global_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_lldp_global_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_lldp_global_facts = self.get_lldp_global_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_lldp_global_facts(data=running_config)
else:
changed_lldp_global_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_lldp_global_facts
if result["changed"]:
result["after"] = changed_lldp_global_facts
elif self.state == "gathered":
result["gathered"] = changed_lldp_global_facts
result["warnings"] = warnings
return result
def set_config(self, existing_lldp_global_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_lldp_global_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if self.state in ("merged", "replaced", "rendered") and not want:
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "deleted":
commands.extend(self._state_deleted(want=None, have=have))
elif self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want=want, have=have))
elif self.state == "replaced":
commands.extend(self._state_replaced(want=want, have=have))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._state_deleted(want, have))
commands.extend(self._state_merged(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
commands.extend(self._render_updates(want, have))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
for item in Lldp_global.params:
if item == "legacy_protocols":
commands.extend(self._update_lldp_protocols(want, have))
elif (
have.get(item) and not want.get(item) and item != "enable"
):
commands.append(Lldp_global.del_cmd + item)
elif have:
for item in Lldp_global.params:
if have.get(item):
if item == "legacy_protocols":
commands.append(
self._compute_command(
"legacy-protocols", remove=True
)
)
elif item == "address":
commands.append(
self._compute_command(
"management-address", remove=True
)
)
elif item == "snmp":
commands.append(
self._compute_command(item, remove=True)
)
return commands
def _render_updates(self, want, have):
commands = []
if have:
temp_have_legacy_protos = have.pop("legacy_protocols", None)
else:
have = {}
temp_want_legacy_protos = want.pop("legacy_protocols", None)
updates = dict_diff(have, want)
if have and temp_have_legacy_protos:
have["legacy_protocols"] = temp_have_legacy_protos
if not have and temp_want_legacy_protos:
want["legacy_protocols"] = temp_want_legacy_protos
commands.extend(self._add_lldp_protocols(want, have))
if updates:
for key, value in iteritems(updates):
if value:
if key == "enable":
commands.append(self._compute_command())
elif key == "address":
commands.append(
self._compute_command(
"management-address", str(value)
)
)
elif key == "snmp":
if value == "disable":
commands.append(
self._compute_command(key, remove=True)
)
else:
commands.append(
self._compute_command(key, str(value))
)
return commands
def _add_lldp_protocols(self, want, have):
commands = []
diff_members = get_lst_diff_for_dicts(want, have, "legacy_protocols")
for key in diff_members:
commands.append(self._compute_command("legacy-protocols", key))
return commands
def _update_lldp_protocols(self, want_item, have_item):
commands = []
want_protocols = want_item.get("legacy_protocols") or []
have_protocols = have_item.get("legacy_protocols") or []
members_diff = list_diff_have_only(want_protocols, have_protocols)
if members_diff:
for member in members_diff:
commands.append(
self._compute_command(
"legacy-protocols", member, remove=True
)
)
return commands
def _compute_command(self, key=None, value=None, remove=False):
if remove:
cmd = "delete service lldp"
else:
cmd = "set service lldp"
if key:
cmd += " " + key
if value:
cmd += " '" + value + "'"
return cmd
diff --git a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
index 101e1d6..94e39c3 100644
--- a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py
@@ -1,460 +1,459 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_lldp_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
dict_diff,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
search_obj_in_list,
search_dict_tv_in_list,
key_value_in_dict,
is_dict_element_present,
)
class Lldp_interfaces(ConfigBase):
"""
The vyos_lldp_interfaces class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"lldp_interfaces",
]
params = ["enable", "location", "name"]
def __init__(self, module):
super(Lldp_interfaces, self).__init__(module)
def get_lldp_interfaces_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
lldp_interfaces_facts = facts["ansible_network_resources"].get(
"lldp_interfaces"
)
if not lldp_interfaces_facts:
return []
return lldp_interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
else:
existing_lldp_interfaces_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_lldp_interfaces_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_lldp_interfaces_facts(
data=running_config
)
else:
changed_lldp_interfaces_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_lldp_interfaces_facts
if result["changed"]:
result["after"] = changed_lldp_interfaces_facts
elif self.state == "gathered":
result["gathered"] = changed_lldp_interfaces_facts
result["warnings"] = warnings
return result
def set_config(self, existing_lldp_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_lldp_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not want
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
elif self.state == "deleted":
if want:
for item in want:
name = item["name"]
have_item = search_obj_in_list(name, have)
commands.extend(
self._state_deleted(want=None, have=have_item)
)
else:
for have_item in have:
commands.extend(
self._state_deleted(want=None, have=have_item)
)
else:
for want_item in want:
name = want_item["name"]
have_item = search_obj_in_list(name, have)
if self.state in ("merged", "rendered"):
commands.extend(
self._state_merged(want=want_item, have=have_item)
)
if self.state == "replaced":
commands.extend(
self._state_replaced(want=want_item, have=have_item)
)
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._state_deleted(want, have))
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for have_item in have:
lldp_name = have_item["name"]
lldp_in_want = search_obj_in_list(lldp_name, want)
if not lldp_in_want:
commands.append(
self._compute_command(have_item["name"], remove=True)
)
for want_item in want:
name = want_item["name"]
lldp_in_have = search_obj_in_list(name, have)
commands.extend(self._state_replaced(want_item, lldp_in_have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
if have:
commands.extend(self._render_updates(want, have))
else:
commands.extend(self._render_set_commands(want))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
params = Lldp_interfaces.params
for attrib in params:
if attrib == "location":
commands.extend(
self._update_location(have["name"], want, have)
)
elif have:
commands.append(self._compute_command(have["name"], remove=True))
return commands
def _render_updates(self, want, have):
commands = []
lldp_name = have["name"]
commands.extend(self._configure_status(lldp_name, want, have))
commands.extend(self._add_location(lldp_name, want, have))
return commands
def _render_set_commands(self, want):
commands = []
have = {}
lldp_name = want["name"]
params = Lldp_interfaces.params
for attrib in params:
value = want[attrib]
if value:
if attrib == "location":
commands.extend(self._add_location(lldp_name, want, have))
elif attrib == "enable":
if not value:
commands.append(
self._compute_command(lldp_name, value="disable")
)
else:
commands.append(self._compute_command(lldp_name))
return commands
def _configure_status(self, name, want_item, have_item):
commands = []
if is_dict_element_present(have_item, "enable"):
temp_have_item = False
else:
temp_have_item = True
if want_item["enable"] != temp_have_item:
if want_item["enable"]:
commands.append(
self._compute_command(name, value="disable", remove=True)
)
else:
commands.append(self._compute_command(name, value="disable"))
return commands
def _add_location(self, name, want_item, have_item):
commands = []
have_dict = {}
have_ca = {}
set_cmd = name + " location "
want_location_type = want_item.get("location") or {}
have_location_type = have_item.get("location") or {}
if want_location_type["coordinate_based"]:
want_dict = want_location_type.get("coordinate_based") or {}
if is_dict_element_present(have_location_type, "coordinate_based"):
have_dict = have_location_type.get("coordinate_based") or {}
location_type = "coordinate-based"
updates = dict_diff(have_dict, want_dict)
for key, value in iteritems(updates):
if value:
commands.append(
self._compute_command(
set_cmd + location_type, key, str(value)
)
)
elif want_location_type["civic_based"]:
location_type = "civic-based"
want_dict = want_location_type.get("civic_based") or {}
want_ca = want_dict.get("ca_info") or []
if is_dict_element_present(have_location_type, "civic_based"):
have_dict = have_location_type.get("civic_based") or {}
have_ca = have_dict.get("ca_info") or []
if want_dict["country_code"] != have_dict["country_code"]:
commands.append(
self._compute_command(
set_cmd + location_type,
"country-code",
str(want_dict["country_code"]),
)
)
else:
commands.append(
self._compute_command(
set_cmd + location_type,
"country-code",
str(want_dict["country_code"]),
)
)
commands.extend(self._add_civic_address(name, want_ca, have_ca))
elif want_location_type["elin"]:
location_type = "elin"
if is_dict_element_present(have_location_type, "elin"):
if want_location_type.get("elin") != have_location_type.get(
"elin"
):
commands.append(
self._compute_command(
set_cmd + location_type,
value=str(want_location_type["elin"]),
)
)
else:
commands.append(
self._compute_command(
set_cmd + location_type,
value=str(want_location_type["elin"]),
)
)
return commands
def _update_location(self, name, want_item, have_item):
commands = []
del_cmd = name + " location"
want_location_type = want_item.get("location") or {}
have_location_type = have_item.get("location") or {}
if want_location_type["coordinate_based"]:
want_dict = want_location_type.get("coordinate_based") or {}
if is_dict_element_present(have_location_type, "coordinate_based"):
have_dict = have_location_type.get("coordinate_based") or {}
location_type = "coordinate-based"
for key, value in iteritems(have_dict):
only_in_have = key_value_in_dict(key, value, want_dict)
if not only_in_have:
commands.append(
self._compute_command(
del_cmd + location_type, key, str(value), True
)
)
else:
commands.append(self._compute_command(del_cmd, remove=True))
elif want_location_type["civic_based"]:
want_dict = want_location_type.get("civic_based") or {}
want_ca = want_dict.get("ca_info") or []
if is_dict_element_present(have_location_type, "civic_based"):
have_dict = have_location_type.get("civic_based") or {}
have_ca = have_dict.get("ca_info")
commands.extend(
self._update_civic_address(name, want_ca, have_ca)
)
else:
commands.append(self._compute_command(del_cmd, remove=True))
else:
if is_dict_element_present(have_location_type, "elin"):
if want_location_type.get("elin") != have_location_type.get(
"elin"
):
commands.append(
self._compute_command(del_cmd, remove=True)
)
else:
commands.append(self._compute_command(del_cmd, remove=True))
return commands
def _add_civic_address(self, name, want, have):
commands = []
for item in want:
ca_type = item["ca_type"]
ca_value = item["ca_value"]
obj_in_have = search_dict_tv_in_list(
ca_type, ca_value, have, "ca_type", "ca_value"
)
if not obj_in_have:
commands.append(
self._compute_command(
key=name + " location civic-based ca-type",
attrib=str(ca_type) + " ca-value",
value=ca_value,
)
)
return commands
def _update_civic_address(self, name, want, have):
commands = []
for item in have:
ca_type = item["ca_type"]
ca_value = item["ca_value"]
in_want = search_dict_tv_in_list(
ca_type, ca_value, want, "ca_type", "ca_value"
)
if not in_want:
commands.append(
self._compute_command(
name,
"location civic-based ca-type",
str(ca_type),
remove=True,
)
)
return commands
def _compute_command(self, key, attrib=None, value=None, remove=False):
if remove:
cmd = "delete service lldp interface "
else:
cmd = "set service lldp interface "
cmd += key
if attrib:
cmd += " " + attrib
if value:
cmd += " '" + value + "'"
return cmd
diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
index b9f24f4..fd25c17 100644
--- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
+++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py
@@ -1,950 +1,949 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_ospfv2 class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
list_diff_want_only,
_in_target,
_is_w_same,
_bool_to_str,
)
class Ospfv2(ConfigBase):
"""
The vyos_ospfv2 class
"""
gather_subset = ["!all", "!min"]
gather_network_resources = ["ospfv2"]
def __init__(self, module):
super(Ospfv2, self).__init__(module)
def get_ospfv2_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
(facts, _warnings) = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
ospfv2_facts = facts["ansible_network_resources"].get("ospfv2", {})
return ospfv2_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_ospfv2_facts = self.get_ospfv2_facts()
else:
existing_ospfv2_facts = {}
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_ospfv2_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_ospfv2_facts = self.get_ospfv2_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_ospfv2_facts(data=running_config)
else:
changed_ospfv2_facts = {}
if self.state in self.ACTION_STATES:
result["before"] = existing_ospfv2_facts
if result["changed"]:
result["after"] = changed_ospfv2_facts
elif self.state == "gathered":
result["gathered"] = changed_ospfv2_facts
result["warnings"] = warnings
return result
def set_config(self, existing_ospfv2_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_ospfv2_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, w, h):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not w
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "deleted":
commands.extend(self._state_deleted(h))
elif self.state in ("merged", "rendered"):
commands.extend(self._state_merged(w, h))
elif self.state == "replaced":
commands.extend(self._state_replaced(w, h))
return commands
def search_obj_in_have(self, have, w_name, key):
"""
This function returns the rule-set/rule if it is present in target config.
:param have: target config.
:param w_name: rule-set name.
:param type: rule_sets/rule/r_list.
:return: rule-set/rule.
"""
if have:
for item in have:
if item[key] == w_name[key]:
return item
return None
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._render_ospf_param(have, want, opr=False))
commands.extend(self._render_ospf_param(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
commands.extend(self._render_ospf_param(want, have))
return commands
def _state_deleted(self, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if have:
commands.append("delete protocols ospf")
return commands
def _render_ospf_param(self, want, have, opr=True):
"""
This function forms the set/delete commands for ospf leaf attributes
and triggers the process for other child attributes.
for firewall_global attributes.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
w = deepcopy(remove_empties(want))
leaf = ("default_metric", "log_adjacency_changes")
if w:
for (key, val) in iteritems(w):
if opr and key in leaf and not _is_w_same(w, have, key):
commands.append(
self._form_attr_cmd(
attr=key, val=_bool_to_str(val), opr=opr
)
)
elif not opr and key in leaf and not _in_target(have, key):
commands.append(
self._form_attr_cmd(
attr=key, val=_bool_to_str(val), opr=opr
)
)
else:
commands.extend(
self._render_child_param(w, have, key, opr)
)
return commands
def _render_child_param(self, w, h, key, opr=True):
"""
This function invoke the function to extend commands
based on the key.
:param w: the desired configuration.
:param h: the current configuration.
:param key: attribute name.
:param opr: operation.
:return: list of commands.
"""
commands = []
if key in ("neighbor", "redistribute"):
commands.extend(self._render_list_dict_param(key, w, h, opr=opr))
elif key in ("default_information", "max_metric"):
commands.extend(self._render_nested_dict_param(key, w, h, opr=opr))
elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"):
commands.extend(self._render_dict_param(key, w, h, opr=opr))
elif key in (
"route_map",
"passive_interface",
"passive_interface_exclude",
):
commands.extend(self._render_list_param(key, w, h, opr=opr))
elif key == "areas":
commands.extend(self._render_areas(key, w, h, opr=opr))
elif key == "timers":
commands.extend(self._render_timers(key, w, h, opr=opr))
elif key == "distance":
commands.extend(self._render_distance(key, w, h, opr=opr))
return commands
def _render_dict_param(self, attr, want, have, opr=True):
"""
This function generate the commands for dictionary elements.
:param attr: attribute name.
:param w: the desired configuration.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h = {}
if have:
h = have.get(attr) or {}
if not opr and not h:
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
elif want[attr]:
leaf_dict = {
"auto_cost": "reference_bandwidth",
"mpls_te": ("enabled", "router_address"),
"parameters": (
"router_id",
"abr_type",
"opaque_lsa",
"rfc1583_compatibility",
),
}
leaf = leaf_dict[attr]
for (item, value) in iteritems(want[attr]):
if (
opr
and item in leaf
and not _is_w_same(want[attr], h, item)
):
if item == "enabled":
item = "enable"
if item in (
"opaque_lsa",
"enable",
"rfc1583_compatibility",
):
commands.append(
self._form_attr_cmd(key=attr, attr=item, opr=opr)
)
else:
commands.append(
self._form_attr_cmd(
key=attr, attr=item, val=value, opr=opr
)
)
elif not opr and item in leaf and not _in_target(h, item):
if item == "enabled":
commands.append(
self._form_attr_cmd(
key=attr, attr="enable", opr=opr
)
)
else:
commands.append(
self._form_attr_cmd(key=attr, attr=item, opr=opr)
)
return commands
def _render_list_param(self, attr, want, have, cmd=None, opr=True):
"""
This function forms the commands for passed target list attributes'.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h = []
if want:
w = want.get(attr) or []
if have:
h = have.get(attr) or []
if not cmd:
cmd = self._compute_command(opr=opr)
if w:
if opr:
members = list_diff_want_only(w, h)
for member in members:
command = cmd + attr.replace("_", "-") + " "
if attr == "network":
command += member["address"]
else:
command += member
commands.append(command)
elif not opr:
if h:
for member in w:
if attr == "network":
if not self.search_obj_in_have(
h, member, "address"
):
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ member["address"]
)
elif member not in h:
commands.append(
cmd + attr.replace("_", "-") + " " + member
)
else:
commands.append(cmd + " " + attr.replace("_", "-"))
return commands
def _render_vlink(self, attr, want, have, cmd=None, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for attributes with in desired list of dictionary.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h = []
name = {"virtual_link": "address"}
leaf_dict = {
"virtual_link": (
"address",
"dead_interval",
"transmit_delay",
"hello_interval",
"retransmit_interval",
)
}
leaf = leaf_dict[attr]
w = want.get(attr) or []
if have:
h = have.get(attr) or []
if not opr and not h:
commands.append(cmd + attr.replace("_", "-"))
elif w:
for w_item in w:
for (key, val) in iteritems(w_item):
if not cmd:
cmd = self._compute_command(opr=opr)
h_item = self.search_obj_in_have(h, w_item, name[attr])
if (
opr
and key in leaf
and not _is_w_same(w_item, h_item, key)
):
if key in "address":
commands.append(
cmd + attr.replace("_", "-") + " " + str(val)
)
else:
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ w_item[name[attr]]
+ " "
+ key.replace("_", "-")
+ " "
+ str(val)
)
elif (
not opr and key in leaf and not _in_target(h_item, key)
):
if key in "address":
commands.append(
cmd + attr.replace("_", "-") + " " + str(val)
)
else:
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ w_item[name[attr]]
+ " "
+ key
)
elif key == "authentication":
commands.extend(
self._render_vlink_auth(
attr,
key,
w_item,
h_item,
w_item["address"],
cmd,
opr,
)
)
return commands
def _render_vlink_auth(
self, attr, key, want, have, address, cmd=None, opr=True
):
"""
This function forms the set/delete commands based on the 'opr' type
for attributes with in desired list of dictionary.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h = []
w = want.get(key) or {}
if have:
h = have.get(key) or {}
cmd += attr.replace("_", "-") + " " + address + " " + key + " "
commands.extend(self._render_list_dict_param("md5", w, h, cmd, opr))
return commands
def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for attributes with in desired list of dictionary.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h = []
name = {
"redistribute": "route_type",
"neighbor": "neighbor_id",
"range": "address",
"md5": "key_id",
"vlink": "address",
}
leaf_dict = {
"md5": "md5_key",
"redistribute": (
"metric",
"route_map",
"route_type",
"metric_type",
),
"neighbor": ("priority", "poll_interval", "neighbor_id"),
"range": ("cost", "address", "substitute", "not_advertise"),
"vlink": (
"address",
"dead_interval",
"transmit_delay",
"hello_interval",
"retransmit_interval",
),
}
leaf = leaf_dict[attr]
w = want.get(attr) or []
if have:
h = have.get(attr) or []
if not opr and not h:
commands.append(self._compute_command(attr=attr, opr=opr))
elif w:
for w_item in w:
for (key, val) in iteritems(w_item):
if not cmd:
cmd = self._compute_command(opr=opr)
h_item = self.search_obj_in_have(h, w_item, name[attr])
if (
opr
and key in leaf
and not _is_w_same(w_item, h_item, key)
):
if key in (
"route_type",
"neighbor_id",
"address",
"key_id",
):
commands.append(cmd + attr + " " + str(val))
elif key == "cost":
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key
+ " "
+ str(val)
)
elif key == "not_advertise":
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key.replace("_", "-")
)
elif key == "md5_key":
commands.append(
cmd
+ attr
+ " "
+ "key-id"
+ " "
+ str(w_item[name[attr]])
+ " "
+ key.replace("_", "-")
+ " "
+ w_item[key]
)
else:
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key.replace("_", "-")
+ " "
+ str(val)
)
elif (
not opr and key in leaf and not _in_target(h_item, key)
):
if key in (
"route_type",
"neighbor_id",
"address",
"key_id",
):
commands.append(cmd + attr + " " + str(val))
else:
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key
)
return commands
def _render_nested_dict_param(self, attr, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for attributes with in desired nested dicts.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
attr_dict = {
"default_information": "originate",
"max_metric": "router_lsa",
}
leaf_dict = {
"default_information": (
"always",
"metric",
"metric_type",
"route_map",
),
"max_metric": ("administrative", "on_startup", "on_shutdown"),
}
h = {}
w = want.get(attr) or {}
if have:
h = have.get(attr) or {}
if not opr and not h:
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
elif w:
key = attr_dict[attr]
w_attrib = want[attr].get(key) or {}
cmd = self._compute_command(opr=opr)
h_attrib = {}
if w_attrib:
leaf = leaf_dict[attr]
if h and key in h.keys():
h_attrib = h.get(key) or {}
for (item, val) in iteritems(w[key]):
if (
opr
and item in leaf
and not _is_w_same(w[key], h_attrib, item)
):
if item in ("administrative", "always") and val:
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ key.replace("_", "-")
+ " "
+ item.replace("_", "-")
)
elif item not in ("administrative", "always"):
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ key.replace("_", "-")
+ " "
+ item.replace("_", "-")
+ " "
+ str(val)
)
elif (
not opr
and item in leaf
and not _in_target(h_attrib, item)
):
commands.append(cmd + attr + " " + item)
return commands
def _render_areas(self, attr, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for ospf area attributes.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_lst = {}
w_lst = want.get(attr) or []
l_set = ("area_id", "shortcut", "authentication")
if have:
h_lst = have.get(attr) or []
if not opr and not h_lst:
commands.append(self._form_attr_cmd(attr="area", opr=opr))
elif w_lst:
for w_area in w_lst:
cmd = (
self._compute_command(
key="area",
attr=_bool_to_str(w_area["area_id"]),
opr=opr,
)
+ " "
)
h_area = self.search_obj_in_have(h_lst, w_area, "area_id")
if not opr and not h_area:
commands.append(
self._form_attr_cmd(
key="area", attr=w_area["area_id"], opr=opr
)
)
else:
for (key, val) in iteritems(w_area):
if (
opr
and key in l_set
and not _is_w_same(w_area, h_area, key)
):
if key == "area_id":
commands.append(
self._form_attr_cmd(
attr="area",
val=_bool_to_str(val),
opr=opr,
)
)
else:
commands.append(
cmd
+ key
+ " "
+ _bool_to_str(val).replace("_", "-")
)
elif not opr and key in l_set:
if key == "area_id" and not _in_target(
h_area, key
):
commands.append(cmd)
continue
elif key != "area_id" and not _in_target(
h_area, key
):
commands.append(cmd + val + " " + key)
elif key == "area_type":
commands.extend(
self._render_area_type(
w_area, h_area, key, cmd, opr
)
)
elif key == "network":
commands.extend(
self._render_list_param(
key, w_area, h_area, cmd, opr
)
)
elif key == "range":
commands.extend(
self._render_list_dict_param(
key, w_area, h_area, cmd, opr
)
)
elif key == "virtual_link":
commands.extend(
self._render_vlink(
key, w_area, h_area, cmd, opr
)
)
return commands
def _render_area_type(self, want, have, attr, cmd, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for area_types attributes.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: command to prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_type = {}
w_type = want.get(attr) or []
if have:
h_type = have.get(attr) or {}
if not opr and not h_type:
commands.append(cmd + attr.replace("_", "-"))
elif w_type:
key = "normal"
if (
opr
and key in w_type.keys()
and not _is_w_same(w_type, h_type, key)
):
if not w_type[key] and h_type and h_type[key]:
commands.append(
cmd.replace("set", "delete")
+ attr.replace("_", "-")
+ " "
+ key
)
elif w_type[key]:
commands.append(cmd + attr.replace("_", "-") + " " + key)
elif (
not opr
and key in w_type.keys()
and not (h_type and key in h_type.keys())
):
commands.append(
cmd + want["area"] + " " + attr.replace("_", "-")
)
a_type = {
"nssa": ("set", "default_cost", "no_summary", "translate"),
"stub": ("set", "default_cost", "no_summary"),
}
for key in a_type:
w_area = want[attr].get(key) or {}
h_area = {}
if w_area:
if h_type and key in h_type.keys():
h_area = h_type.get(key) or {}
for (item, val) in iteritems(w_type[key]):
if (
opr
and item in a_type[key]
and not _is_w_same(w_type[key], h_area, item)
):
if item == "set" and val:
commands.append(
cmd + attr.replace("_", "-") + " " + key
)
elif not val and h_area and h_area[item]:
commands.append(
cmd.replace("set", "delete")
+ attr.replace("_", "-")
+ " "
+ key
)
elif item != "set":
commands.append(
cmd
+ attr.replace("_", "-")
+ " "
+ key
+ " "
+ item.replace("_", "-")
+ " "
+ str(val)
)
elif (
not opr
and item in a_type[key]
and not (h_type and key in h_type)
):
if item == "set":
commands.append(
cmd + attr.replace("_", "-") + " " + key
)
else:
commands.append(
cmd
+ want["area"]
+ " "
+ attr.replace("_", "-")
+ " "
+ key
+ " "
+ item.replace("_", "-")
)
return commands
def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
"""
This function forms the command for leaf attribute.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
:return: generated command.
"""
return self._compute_command(
key, attr=self._map_attrib(attr), val=val, opr=opr
)
def _compute_command(
self, key=None, attr=None, val=None, remove=False, opr=True
):
"""
This function construct the add/delete command based on passed attributes.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
:return: generated command.
"""
if remove or not opr:
cmd = "delete protocols ospf "
else:
cmd = "set protocols ospf "
if key:
cmd += key.replace("_", "-") + " "
if attr:
cmd += attr.replace("_", "-")
if val:
cmd += " '" + str(val) + "'"
return cmd
def _map_attrib(self, attrib):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
return "disable" if attrib == "disabled" else attrib.replace("_", "-")
diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py
index a254a93..acda380 100644
--- a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py
+++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py
@@ -1,465 +1,464 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos_ospfv3 class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
remove_empties,
search_obj_in_list,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
_in_target,
_is_w_same,
_bool_to_str,
)
class Ospfv3(ConfigBase):
"""
The vyos_ospfv3 class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"ospfv3",
]
def __init__(self, module):
super(Ospfv3, self).__init__(module)
def get_ospfv3_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
ospfv3_facts = facts["ansible_network_resources"].get("ospfv3", {})
return ospfv3_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_ospfv3_facts = self.get_ospfv3_facts()
else:
existing_ospfv3_facts = {}
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_ospfv3_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_ospfv3_facts = self.get_ospfv3_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_ospfv3_facts(data=running_config)
else:
changed_ospfv3_facts = {}
if self.state in self.ACTION_STATES:
result["before"] = existing_ospfv3_facts
if result["changed"]:
result["after"] = changed_ospfv3_facts
elif self.state == "gathered":
result["gathered"] = changed_ospfv3_facts
result["warnings"] = warnings
return result
def set_config(self, existing_ospfv3_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_ospfv3_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, w, h):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not w
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "deleted":
commands.extend(self._state_deleted(w, h))
elif self.state in ("merged", "rendered"):
commands.extend(self._state_merged(w, h))
elif self.state == "replaced":
commands.extend(self._state_replaced(w, h))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
commands.extend(self._render_ospf_param(have, want, opr=False))
commands.extend(self._render_ospf_param(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
commands.extend(self._render_ospf_param(want, have))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if have:
commands.append("delete protocols ospfv3")
return commands
def _render_ospf_param(self, want, have, opr=True):
"""
This function forms the set/delete commands for ospf leaf attributes
and triggers the process for other child attributes.
for firewall_global attributes.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
w = deepcopy(remove_empties(want))
if w:
for key, val in iteritems(w):
commands.extend(self._render_child_param(w, have, key, opr))
return commands
def _render_child_param(self, w, h, key, opr=True):
"""
This function invoke the function to extend commands
based on the key.
:param w: the desired configuration.
:param h: the current configuration.
:param key: attribute name.
:param opr: operation.
:return: list of commands.
"""
commands = []
if key == "areas":
commands.extend(self._render_areas(key, w, h, opr=opr))
elif key == "parameters":
commands.extend(self._render_dict_param(key, w, h, opr=opr))
elif key == "redistribute":
commands.extend(self._render_list_dict_param(key, w, h, opr=opr))
return commands
def _render_dict_param(self, attr, want, have, opr=True):
"""
This function generate the commands for dictionary elements.
:param attr: attribute name.
:param w: the desired configuration.
:param h: the target config.
:param opr: True/False.
:return: generated list of commands.
"""
commands = []
h = {}
if have:
h = have.get(attr) or {}
if not opr and not h:
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
elif want[attr]:
leaf_dict = {"parameters": "router_id"}
leaf = leaf_dict[attr]
for item, value in iteritems(want[attr]):
if (
opr
and item in leaf
and not _is_w_same(want[attr], h, item)
):
commands.append(
self._form_attr_cmd(
key=attr, attr=item, val=value, opr=opr
)
)
elif not opr and item in leaf and not _in_target(h, item):
commands.append(
self._form_attr_cmd(key=attr, attr=item, opr=opr)
)
return commands
def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for attributes with in desired list of dictionary.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param cmd: commands to be prepend.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h = []
name = {
"redistribute": "route_type",
"range": "address",
}
leaf_dict = {
"redistribute": ("route_map", "route_type"),
"range": ("address", "advertise", "not_advertise"),
}
leaf = leaf_dict[attr]
w = want.get(attr) or []
if have:
h = have.get(attr) or []
if not opr and not h:
commands.append(self._compute_command(attr=attr, opr=opr))
elif w:
for w_item in w:
for key, val in iteritems(w_item):
if not cmd:
cmd = self._compute_command(opr=opr)
h_item = search_obj_in_list(
w_item[name[attr]], h, name[attr]
)
if (
opr
and key in leaf
and not _is_w_same(w_item, h_item, key)
):
if key == "route_type" or (
key == "address"
and "advertise" not in w_item
and "not-advertise" not in w_item
):
if not val:
cmd = cmd.replace("set", "delete")
commands.append(cmd + attr + " " + str(val))
elif key in leaf_dict["range"] and key != "address":
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key.replace("_", "-")
)
elif key == "route_map":
commands.append(
cmd
+ attr
+ " "
+ w_item[name[attr]]
+ " "
+ key.replace("_", "-")
+ " "
+ str(val)
)
elif (
not opr and key in leaf and not _in_target(h_item, key)
):
if key in ("route_type", "address"):
commands.append(cmd + attr + " " + str(val))
else:
commands.append(
cmd
+ (attr + " " + w_item[name[attr]] + " " + key)
)
return commands
def _render_areas(self, attr, want, have, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for ospf area attributes.
:param attr: attribute name.
:param w: the desired config.
:param h: the target config.
:param opr: True/False.
:return: generated commands list.
"""
commands = []
h_lst = {}
w_lst = want.get(attr) or []
l_set = ("area_id", "export_list", "import_list")
if have:
h_lst = have.get(attr) or []
if not opr and not h_lst:
commands.append(self._form_attr_cmd(attr="area", opr=opr))
elif w_lst:
for w_area in w_lst:
cmd = (
self._compute_command(
key="area",
attr=_bool_to_str(w_area["area_id"]),
opr=opr,
)
+ " "
)
h_area = search_obj_in_list(
w_area["area_id"], h_lst, "area_id"
)
if not opr and not h_area:
commands.append(
self._form_attr_cmd(
key="area", attr=w_area["area_id"], opr=opr
)
)
else:
for key, val in iteritems(w_area):
if (
opr
and key in l_set
and not _is_w_same(w_area, h_area, key)
):
if key == "area_id":
commands.append(
self._form_attr_cmd(
attr="area",
val=_bool_to_str(val),
opr=opr,
)
)
else:
commands.append(
cmd
+ key.replace("_", "-")
+ " "
+ _bool_to_str(val).replace("_", "-")
)
elif not opr and key in l_set:
if key == "area_id" and not _in_target(
h_area, key
):
commands.append(cmd)
continue
elif key != "area_id" and not _in_target(
h_area, key
):
commands.append(cmd + val + " " + key)
elif key == "range":
commands.extend(
self._render_list_dict_param(
key, w_area, h_area, cmd, opr
)
)
return commands
def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
"""
This function forms the command for leaf attribute.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
:return: generated command.
"""
return self._compute_command(
key, attr=self._map_attrib(attr), val=val, opr=opr
)
def _compute_command(
self, key=None, attr=None, val=None, remove=False, opr=True
):
"""
This function construct the add/delete command based on passed attributes.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
:return: generated command.
"""
if remove or not opr:
cmd = "delete protocols ospfv3 "
else:
cmd = "set protocols ospfv3 "
if key:
cmd += key.replace("_", "-") + " "
if attr:
cmd += attr.replace("_", "-")
if val and opr:
cmd += " '" + str(val) + "'"
return cmd
def _map_attrib(self, attrib):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
return "disable" if attrib == "disabled" else attrib.replace("_", "-")
diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
index f0dd4f6..b359dbb 100644
--- a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
+++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
@@ -1,622 +1,621 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos_static_routes class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
ConfigBase,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
dict_diff,
remove_empties,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
get_route_type,
get_lst_diff_for_dicts,
get_lst_same_for_dicts,
dict_delete,
)
class Static_routes(ConfigBase):
"""
The vyos_static_routes class
"""
gather_subset = [
"!all",
"!min",
]
gather_network_resources = [
"static_routes",
]
def __init__(self, module):
super(Static_routes, self).__init__(module)
def get_static_routes_facts(self, data=None):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(
self.gather_subset, self.gather_network_resources, data=data
)
static_routes_facts = facts["ansible_network_resources"].get(
"static_routes"
)
if not static_routes_facts:
return []
return static_routes_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {"changed": False}
warnings = list()
commands = list()
if self.state in self.ACTION_STATES:
existing_static_routes_facts = self.get_static_routes_facts()
else:
existing_static_routes_facts = []
if self.state in self.ACTION_STATES or self.state == "rendered":
commands.extend(self.set_config(existing_static_routes_facts))
if commands and self.state in self.ACTION_STATES:
if not self._module.check_mode:
self._connection.edit_config(commands)
result["changed"] = True
if self.state in self.ACTION_STATES:
result["commands"] = commands
if self.state in self.ACTION_STATES or self.state == "gathered":
changed_static_routes_facts = self.get_static_routes_facts()
elif self.state == "rendered":
result["rendered"] = commands
elif self.state == "parsed":
running_config = self._module.params["running_config"]
if not running_config:
self._module.fail_json(
msg="value of running_config parameter must not be empty for state parsed"
)
result["parsed"] = self.get_static_routes_facts(
data=running_config
)
else:
changed_static_routes_facts = []
if self.state in self.ACTION_STATES:
result["before"] = existing_static_routes_facts
if result["changed"]:
result["after"] = changed_static_routes_facts
elif self.state == "gathered":
result["gathered"] = changed_static_routes_facts
result["warnings"] = warnings
return result
def set_config(self, existing_static_routes_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params["config"]
have = existing_static_routes_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if (
self.state in ("merged", "replaced", "overridden", "rendered")
and not want
):
self._module.fail_json(
msg="value of config parameter must not be empty for state {0}".format(
self.state
)
)
if self.state == "overridden":
commands.extend(self._state_overridden(want=want, have=have))
elif self.state == "deleted":
commands.extend(self._state_deleted(want=want, have=have))
elif want:
routes = self._get_routes(want)
for r in routes:
h_item = self.search_route_in_have(have, r["dest"])
if self.state in ("merged", "rendered"):
commands.extend(self._state_merged(want=r, have=h_item))
elif self.state == "replaced":
commands.extend(self._state_replaced(want=r, have=h_item))
return commands
def search_route_in_have(self, have, want_dest):
"""
This function returns the route if its found in
have config.
:param have:
:param dest:
:return: the matched route
"""
routes = self._get_routes(have)
for r in routes:
if r["dest"] == want_dest:
return r
return None
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
if have:
for key, value in iteritems(want):
if value:
if key == "next_hops":
commands.extend(self._update_next_hop(want, have))
elif key == "blackhole_config":
commands.extend(
self._update_blackhole(key, want, have)
)
commands.extend(self._state_merged(want, have))
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
routes = self._get_routes(have)
for r in routes:
route_in_want = self.search_route_in_have(want, r["dest"])
if not route_in_want:
commands.append(self._compute_command(r["dest"], remove=True))
routes = self._get_routes(want)
for r in routes:
route_in_have = self.search_route_in_have(have, r["dest"])
commands.extend(self._state_replaced(r, route_in_have))
return commands
def _state_merged(self, want, have, opr=True):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
if have:
commands.extend(self._render_updates(want, have))
else:
commands.extend(self._render_set_commands(want))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
routes = self._get_routes(want)
if not routes:
for w in want:
af = w["address_families"]
for item in af:
if self.afi_in_have(have, item):
commands.append(
self._compute_command(
afi=item["afi"], remove=True
)
)
else:
routes = self._get_routes(have)
if self._is_ip_route_exist(routes):
commands.append(self._compute_command(afi="ipv4", remove=True))
if self._is_ip_route_exist(routes, "route6"):
commands.append(self._compute_command(afi="ipv6", remove=True))
return commands
def _render_set_commands(self, want):
"""
This function returns the list of commands to add attributes which are
present in want
:param want:
:return: list of commands.
"""
commands = []
have = {}
for key, value in iteritems(want):
if value:
if key == "dest":
commands.append(self._compute_command(dest=want["dest"]))
elif key == "blackhole_config":
commands.extend(self._add_blackhole(key, want, have))
elif key == "next_hops":
commands.extend(self._add_next_hop(want, have))
return commands
def _add_blackhole(self, key, want, have):
"""
This function gets the diff for blackhole config specific attributes
and form the commands for attributes which are present in want but not in have.
:param key:
:param want:
:param have:
:return: list of commands
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(remove_empties(have))
want_blackhole = want_copy.get(key) or {}
have_blackhole = have_copy.get(key) or {}
updates = dict_delete(want_blackhole, have_blackhole)
if updates:
for attrib, value in iteritems(updates):
if value:
if attrib == "distance":
commands.append(
self._compute_command(
dest=want["dest"],
key="blackhole",
attrib=attrib,
remove=False,
value=str(value),
)
)
elif attrib == "type":
commands.append(
self._compute_command(
dest=want["dest"], key="blackhole"
)
)
return commands
def _add_next_hop(self, want, have, opr=True):
"""
This function gets the diff for next hop specific attributes
and form the commands to add attributes which are present in want but not in have.
:param want:
:param have:
:return: list of commands.
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(remove_empties(have))
if not opr:
diff_next_hops = get_lst_same_for_dicts(
want_copy, have_copy, "next_hops"
)
else:
diff_next_hops = get_lst_diff_for_dicts(
want_copy, have_copy, "next_hops"
)
if diff_next_hops:
for hop in diff_next_hops:
for element in hop:
if element == "forward_router_address":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
value=hop[element],
opr=opr,
)
)
elif element == "enabled" and not hop[element]:
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"],
value="disable",
opr=opr,
)
)
elif element == "admin_distance":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"]
+ " "
+ element,
value=str(hop[element]),
opr=opr,
)
)
elif element == "interface":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"]
+ " "
+ element,
value=hop[element],
opr=opr,
)
)
return commands
def _update_blackhole(self, key, want, have):
"""
This function gets the difference for blackhole dict and
form the commands to delete the attributes which are present in have but not in want.
:param want:
:param have:
:return: list of commands
:param key:
:param want:
:param have:
:return: list of commands
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(remove_empties(have))
want_blackhole = want_copy.get(key) or {}
have_blackhole = have_copy.get(key) or {}
updates = dict_delete(have_blackhole, want_blackhole)
if updates:
for attrib, value in iteritems(updates):
if value:
if attrib == "distance":
commands.append(
self._compute_command(
dest=want["dest"],
key="blackhole",
attrib=attrib,
remove=True,
value=str(value),
)
)
elif (
attrib == "type"
and "distance" not in want_blackhole.keys()
):
commands.append(
self._compute_command(
dest=want["dest"], key="blackhole", remove=True
)
)
return commands
def _update_next_hop(self, want, have, opr=True):
"""
This function gets the difference for next_hops list and
form the commands to delete the attributes which are present in have but not in want.
:param want:
:param have:
:return: list of commands
"""
commands = []
want_copy = deepcopy(remove_empties(want))
have_copy = deepcopy(remove_empties(have))
diff_next_hops = get_lst_diff_for_dicts(
have_copy, want_copy, "next_hops"
)
if diff_next_hops:
for hop in diff_next_hops:
for element in hop:
if element == "forward_router_address":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
value=hop[element],
remove=True,
)
)
elif element == "enabled":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"],
value="disable",
remove=True,
)
)
elif element == "admin_distance":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"]
+ " "
+ element,
value=str(hop[element]),
remove=True,
)
)
elif element == "interface":
commands.append(
self._compute_command(
dest=want["dest"],
key="next-hop",
attrib=hop["forward_router_address"]
+ " "
+ element,
value=hop[element],
remove=True,
)
)
return commands
def _render_updates(self, want, have, opr=True):
"""
This function takes the diff between want and have and
invokes the appropriate functions to create the commands
to update the attributes.
:param want:
:param have:
:return: list of commands
"""
commands = []
want_nh = want.get("next_hops") or []
# delete static route operation per destination
if not opr and not want_nh:
commands.append(
self._compute_command(dest=want["dest"], remove=True)
)
else:
temp_have_next_hops = have.pop("next_hops", None)
temp_want_next_hops = want.pop("next_hops", None)
updates = dict_diff(have, want)
if temp_have_next_hops:
have["next_hops"] = temp_have_next_hops
if temp_want_next_hops:
want["next_hops"] = temp_want_next_hops
commands.extend(self._add_next_hop(want, have, opr=opr))
if opr and updates:
for key, value in iteritems(updates):
if value:
if key == "blackhole_config":
commands.extend(
self._add_blackhole(key, want, have)
)
return commands
def _compute_command(
self,
dest=None,
key=None,
attrib=None,
value=None,
remove=False,
afi=None,
opr=True,
):
"""
This functions construct the required command based on the passed arguments.
:param dest:
:param key:
:param attrib:
:param value:
:param remove:
:return: constructed command
"""
if remove or not opr:
cmd = "delete protocols static " + self.get_route_type(dest, afi)
else:
cmd = "set protocols static " + self.get_route_type(dest, afi)
if dest:
cmd += " " + dest
if key:
cmd += " " + key
if attrib:
cmd += " " + attrib
if value:
cmd += " '" + value + "'"
return cmd
def afi_in_have(self, have, w_item):
"""
This functions checks for the afi
list in have
:param have:
:param w_item:
:return:
"""
if have:
for h in have:
af = h.get("address_families") or []
for item in af:
if w_item["afi"] == item["afi"]:
return True
return False
def get_route_type(self, dest=None, afi=None):
"""
This function returns the route type based on
destination ip address or afi
:param address:
:return:
"""
if dest:
return get_route_type(dest)
elif afi == "ipv4":
return "route"
elif afi == "ipv6":
return "route6"
def _is_ip_route_exist(self, routes, type="route"):
"""
This functions checks for the type of route.
:param routes:
:param type:
:return: True/False
"""
for r in routes:
if type == self.get_route_type(r["dest"]):
return True
return False
def _get_routes(self, lst):
"""
This function returns the list of routes
:param lst: list of address families
:return: list of routes
"""
r_list = []
for item in lst:
af = item["address_families"]
for element in af:
routes = element.get("routes") or []
for r in routes:
r_list.append(r)
return r_list
diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py
index 0d90df4..4c7b340 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -1,101 +1,100 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The facts class for vyos
this file validates each subset of facts and selectively
calls the appropriate facts gathering function
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import (
FactsBase,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import (
InterfacesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import (
L3_interfacesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import (
Lag_interfacesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import (
Lldp_globalFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import (
Lldp_interfacesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import (
Firewall_rulesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import (
Static_routesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import (
Firewall_globalFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import (
Firewall_interfacesFacts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import (
Ospfv3Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import (
Ospfv2Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (
Default,
Neighbors,
Config,
)
FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config)
FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts,
l3_interfaces=L3_interfacesFacts,
lag_interfaces=Lag_interfacesFacts,
lldp_global=Lldp_globalFacts,
lldp_interfaces=Lldp_interfacesFacts,
static_routes=Static_routesFacts,
firewall_rules=Firewall_rulesFacts,
firewall_global=Firewall_globalFacts,
firewall_interfaces=Firewall_interfacesFacts,
ospfv3=Ospfv3Facts,
ospfv2=Ospfv2Facts,
)
class Facts(FactsBase):
""" The fact class for vyos
"""
VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
def __init__(self, module):
super(Facts, self).__init__(module)
def get_facts(
self, legacy_facts_type=None, resource_facts_type=None, data=None
):
""" Collect the facts for vyos
:param legacy_facts_type: List of legacy facts types
:param resource_facts_type: List of resource fact types
:param data: previously collected conf
:rtype: dict
:return: the facts gathered
"""
if self.VALID_RESOURCE_SUBSETS:
self.get_network_resources_facts(
FACT_RESOURCE_SUBSETS, resource_facts_type, data
)
if self.VALID_LEGACY_GATHER_SUBSETS:
self.get_network_legacy_facts(
FACT_LEGACY_SUBSETS, legacy_facts_type
)
return self.ansible_facts, self._warnings
diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
index 37f90f3..0823259 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
@@ -1,389 +1,388 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos firewall_global fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from re import findall, search, M
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import (
Firewall_globalArgs,
)
class Firewall_globalFacts(object):
""" The vyos firewall_global fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Firewall_globalArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for firewall_global
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
data = self.get_device_data(connection)
objs = {}
firewalls = findall(r"^set firewall .*$", data, M)
if firewalls:
objs = self.render_config(firewalls)
facts = {}
params = utils.validate_config(self.argument_spec, {"config": objs})
facts["firewall_global"] = utils.remove_empties(params["config"])
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
conf = "\n".join(
filter(
lambda x: ("firewall ipv6-name" and "firewall name" not in x),
conf,
)
)
a_lst = [
"config_trap",
"validation",
"log_martians",
"syn_cookies",
"twa_hazards_protection",
]
firewall = self.parse_attr(conf, a_lst)
f_sub = {
"ping": self.parse_ping(conf),
"group": self.parse_group(conf),
"route_redirects": self.route_redirects(conf),
"state_policy": self.parse_state_policy(conf),
}
firewall.update(f_sub)
return firewall
def route_redirects(self, conf):
"""
This function forms the regex to fetch the afi and invoke
functions to fetch route redirects and source routes
:param conf: configuration data.
:return: generated rule list configuration.
"""
rr_lst = []
v6_attr = findall(
r"^set firewall (?:ipv6-src-route|ipv6-receive-redirects) (\S+)",
conf,
M,
)
if v6_attr:
obj = self.parse_rr_attrib(conf, "ipv6")
if obj:
rr_lst.append(obj)
v4_attr = findall(
r"^set firewall (?:ip-src-route|receive-redirects|send-redirects) (\S+)",
conf,
M,
)
if v4_attr:
obj = self.parse_rr_attrib(conf, "ipv4")
if obj:
rr_lst.append(obj)
return rr_lst
def parse_rr_attrib(self, conf, attrib=None):
"""
This function fetches the 'ip_src_route'
invoke function to parse icmp redirects.
:param conf: configuration to be parsed.
:param attrib: 'ipv4/ipv6'.
:return: generated config dictionary.
"""
cfg_dict = self.parse_attr(conf, ["ip_src_route"], type=attrib)
cfg_dict["icmp_redirects"] = self.parse_icmp_redirects(conf, attrib)
cfg_dict["afi"] = attrib
return cfg_dict
def parse_icmp_redirects(self, conf, attrib=None):
"""
This function triggers the parsing of 'icmp_redirects' attributes.
:param conf: configuration to be parsed.
:param attrib: 'ipv4/ipv6'.
:return: generated config dictionary.
"""
a_lst = ["send", "receive"]
cfg_dict = self.parse_attr(conf, a_lst, type=attrib)
return cfg_dict
def parse_ping(self, conf):
"""
This function triggers the parsing of 'ping' attributes.
:param conf: configuration to be parsed.
:return: generated config dictionary.
"""
a_lst = ["all", "broadcast"]
cfg_dict = self.parse_attr(conf, a_lst)
return cfg_dict
def parse_state_policy(self, conf):
"""
This function fetched the connecton type and invoke
function to parse other state-policy attributes.
:param conf: configuration data.
:return: generated rule list configuration.
"""
sp_lst = []
attrib = "state-policy"
policies = findall(r"^set firewall " + attrib + " (\\S+)", conf, M)
if policies:
rules_lst = []
for sp in set(policies):
sp_regex = r" %s .+$" % sp
cfg = "\n".join(findall(sp_regex, conf, M))
obj = self.parse_policies(cfg, sp)
obj["connection_type"] = sp
if obj:
rules_lst.append(obj)
sp_lst = sorted(rules_lst, key=lambda i: i["connection_type"])
return sp_lst
def parse_policies(self, conf, attrib=None):
"""
This function triggers the parsing of policy attributes
action and log.
:param conf: configuration
:param attrib: connection type.
:return: generated rule configuration dictionary.
"""
a_lst = ["action", "log"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_group(self, conf):
"""
This function triggers the parsing of 'group' attributes.
:param conf: configuration.
:return: generated config dictionary.
"""
cfg_dict = {}
cfg_dict["port_group"] = self.parse_group_lst(conf, "port-group")
cfg_dict["address_group"] = self.parse_group_lst(conf, "address-group")
cfg_dict["network_group"] = self.parse_group_lst(conf, "network-group")
return cfg_dict
def parse_group_lst(self, conf, type):
"""
This function fetches the name of group and invoke function to
parse group attributes'.
:param conf: configuration data.
:param type: type of group.
:return: generated group list configuration.
"""
g_lst = []
groups = findall(r"^set firewall group " + type + " (\\S+)", conf, M)
if groups:
rules_lst = []
for gr in set(groups):
gr_regex = r" %s .+$" % gr
cfg = "\n".join(findall(gr_regex, conf, M))
obj = self.parse_groups(cfg, type, gr)
obj["name"] = gr.strip("'")
if obj:
rules_lst.append(obj)
g_lst = sorted(rules_lst, key=lambda i: i["name"])
return g_lst
def parse_groups(self, conf, type, name):
"""
This function fetches the description and invoke
the parsing of group members.
:param conf: configuration.
:param type: type of group.
:param name: name of group.
:return: generated configuration dictionary.
"""
a_lst = ["name", "description"]
group = self.parse_attr(conf, a_lst)
key = self.get_key(type)
r_sub = {key[0]: self.parse_address_port_lst(conf, name, key[1])}
group.update(r_sub)
return group
def parse_address_port_lst(self, conf, name, key):
"""
This function forms the regex to fetch the
group members attributes.
:param conf: configuration data.
:param name: name of group.
:param key: key value.
:return: generated member list configuration.
"""
l_lst = []
attribs = findall(r"^.*" + name + " " + key + " (\\S+)", conf, M)
if attribs:
for attr in attribs:
if key == "port":
l_lst.append({"port": attr.strip("'")})
else:
l_lst.append({"address": attr.strip("'")})
return l_lst
def parse_attr(self, conf, attr_list, match=None, type=None):
"""
This function peforms the following:
- Form the regex to fetch the required attribute config.
- Type cast the output in desired format.
:param conf: configuration.
:param attr_list: list of attributes.
:param match: parent node/attribute name.
:return: generated config dictionary.
"""
config = {}
for attrib in attr_list:
regex = self.map_regex(attrib, type)
if match:
regex = match + " " + regex
if conf:
if self.is_bool(attrib):
attr = self.map_regex(attrib, type)
out = conf.find(attr.replace("_", "-"))
dis = conf.find(attr.replace("_", "-") + " 'disable'")
if out >= 1:
if dis >= 1:
config[attrib] = False
else:
config[attrib] = True
else:
out = search(r"^.*" + regex + " (.+)", conf, M)
if out:
val = out.group(1).strip("'")
if self.is_num(attrib):
val = int(val)
config[attrib] = val
return config
def get_key(self, type):
"""
This function map the group type to
member type
:param type:
:return:
"""
key = ()
if type == "port-group":
key = ("members", "port")
elif type == "address-group":
key = ("members", "address")
elif type == "network-group":
key = ("members", "network")
return key
def map_regex(self, attrib, type=None):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
regex = attrib.replace("_", "-")
if attrib == "all":
regex = "all-ping"
elif attrib == "disabled":
regex = "disable"
elif attrib == "broadcast":
regex = "broadcast-ping"
elif attrib == "send":
if type == "ipv6":
regex = "ipv6-send-redirects"
else:
regex = "send-redirects"
elif attrib == "ip_src_route":
if type == "ipv6":
regex = "ipv6-src-route"
elif attrib == "receive":
if type == "ipv6":
regex = "ipv6-receive-redirects"
else:
regex = "receive-redirects"
return regex
def is_num(self, attrib):
"""
This function looks for the attribute in predefined integer type set.
:param attrib: attribute.
:return: True/false.
"""
num_set = ("time", "code", "type", "count", "burst", "number")
return True if attrib in num_set else False
def get_src_route(self, attrib):
"""
This function looks for the attribute in predefined integer type set.
:param attrib: attribute.
:return: True/false.
"""
return "ipv6_src_route" if attrib == "ipv6" else "ip_src_route"
def is_bool(self, attrib):
"""
This function looks for the attribute in predefined bool type set.
:param attrib: attribute.
:return: True/False
"""
bool_set = (
"all",
"log",
"send",
"receive",
"broadcast",
"config_trap",
"log_martians",
"syn_cookies",
"ip_src_route",
"twa_hazards_protection",
)
return True if attrib in bool_set else False
diff --git a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py
index 0992d87..4640748 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py
@@ -1,197 +1,196 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos firewall_interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import (
Firewall_interfacesArgs,
)
class Firewall_interfacesFacts(object):
""" The vyos firewall_interfaces fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Firewall_interfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for firewall_interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
data = self.get_device_data(connection)
objs = []
interfaces = findall(
r"^set interfaces ethernet (?:\'*)(\S+)(?:\'*)", data, M
)
if interfaces:
objs = self.get_names(data, interfaces)
ansible_facts["ansible_network_resources"].pop(
"firewall_interfaces", None
)
facts = {}
if objs:
facts["firewall_interfaces"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["firewall_interfaces"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def get_names(self, data, interfaces):
"""
This function performs following:
- Form regex to fetch 'interface name' from interfaces firewall data.
- Form the name list.
:param data: configuration.
:param rules: list of interfaces.
:return: generated firewall interfaces configuration.
"""
names = []
for r in set(interfaces):
int_regex = r" %s .+$" % r.strip("'")
cfg = findall(int_regex, data, M)
fi = self.render_config(cfg)
fi["name"] = r.strip("'")
names.append(fi)
if names:
names = sorted(names, key=lambda i: i["name"])
return names
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
conf = "\n".join(filter(lambda x: "firewall" in x, conf))
config = {"access_rules": self.parse_access_rules(conf)}
return config
def parse_access_rules(self, conf):
"""
This function forms the regex to fetch the 'access-rules'
for specific interface.
:param conf: configuration data.
:return: generated access-rules list configuration.
"""
ar_lst = []
v4_ar = findall(r"^.*(in|out|local) name .*$", conf, M)
v6_ar = findall(r"^.*(in|out|local) ipv6-name .*$", conf, M)
if v4_ar:
v4_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " name", conf, M))
config = self.parse_int_rules(v4_conf, "ipv4")
if config:
ar_lst.append(config)
if v6_ar:
v6_conf = "\n".join(
findall(r"(^.*?%s.*?$)" % " ipv6-name", conf, M)
)
config = self.parse_int_rules(v6_conf, "ipv6")
if config:
ar_lst.append(config)
if ar_lst:
ar_lst = sorted(ar_lst, key=lambda i: i["afi"])
else:
empty_rules = findall(r"^.*(in|out|local).*", conf, M)
if empty_rules:
ar_lst.append({"afi": "ipv4", "rules": []})
ar_lst.append({"afi": "ipv6", "rules": []})
return ar_lst
def parse_int_rules(self, conf, afi):
"""
This function forms the regex to fetch the 'access-rules'
for specific interface based on ip-type.
:param conf: configuration data.
:param rules: rules configured per interface.
:param afi: ip address type.
:return: generated rule configuration dictionary.
"""
r_lst = []
config = {}
rules = ["in", "out", "local"]
for r in set(rules):
fr = {}
r_regex = r" %s .+$" % r
cfg = "\n".join(findall(r_regex, conf, M))
if cfg:
fr = self.parse_rules(cfg, afi)
else:
out = search(r"^.*firewall " + "'" + r + "'" + "(.*)", conf, M)
if out:
fr = {"direction": r}
if fr:
r_lst.append(fr)
if r_lst:
r_lst = sorted(r_lst, key=lambda i: i["direction"])
config = {"afi": afi, "rules": r_lst}
return config
def parse_rules(self, conf, afi):
"""
This function triggers the parsing of 'rule' attributes.
a_lst is a list having rule attributes which doesn't
have further sub attributes.
:param conf: configuration.
:param afi: ip address type.
:return: generated rule configuration dictionary.
"""
cfg = {}
out = findall(r"[^\s]+", conf, M)
if out:
cfg["direction"] = out[0].strip("'")
if afi == "ipv6":
out = findall(r"[^\s]+ ipv6-name (?:\'*)(\S+)(?:\'*)", conf, M)
if out:
cfg["name"] = str(out[0]).strip("'")
else:
out = findall(r"[^\s]+ name (?:\'*)(\S+)(?:\'*)", conf, M)
if out:
cfg["name"] = out[-1].strip("'")
return cfg
diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
index 3b66192..971ea6f 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
@@ -1,381 +1,380 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos firewall_rules fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import (
Firewall_rulesArgs,
)
class Firewall_rulesFacts(object):
""" The vyos firewall_rules fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Firewall_rulesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for firewall_rules
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
data = self.get_device_data(connection)
# split the config into instances of the resource
objs = []
v6_rules = findall(
r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M
)
v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M)
if v6_rules:
config = self.get_rules(data, v6_rules, type="ipv6")
if config:
config = utils.remove_empties(config)
objs.append(config)
if v4_rules:
config = self.get_rules(data, v4_rules, type="ipv4")
if config:
config = utils.remove_empties(config)
objs.append(config)
ansible_facts["ansible_network_resources"].pop("firewall_rules", None)
facts = {}
if objs:
facts["firewall_rules"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["firewall_rules"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def get_rules(self, data, rules, type):
"""
This function performs following:
- Form regex to fetch 'rule-sets' specific config from data.
- Form the rule-set list based on ip address.
:param data: configuration.
:param rules: list of rule-sets.
:param type: ip address type.
:return: generated rule-sets configuration.
"""
r_v4 = []
r_v6 = []
for r in set(rules):
rule_regex = r" %s .+$" % r.strip("'")
cfg = findall(rule_regex, data, M)
fr = self.render_config(cfg, r.strip("'"))
fr["name"] = r.strip("'")
if type == "ipv6":
r_v6.append(fr)
else:
r_v4.append(fr)
if r_v4:
config = {"afi": "ipv4", "rule_sets": r_v4}
if r_v6:
config = {"afi": "ipv6", "rule_sets": r_v6}
return config
def render_config(self, conf, match):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
conf = "\n".join(filter(lambda x: x, conf))
a_lst = ["description", "default_action", "enable_default_log"]
config = self.parse_attr(conf, a_lst, match)
if not config:
config = {}
config["rules"] = self.parse_rules_lst(conf)
return config
def parse_rules_lst(self, conf):
"""
This function forms the regex to fetch the 'rules' with in
'rule-sets'
:param conf: configuration data.
:return: generated rule list configuration.
"""
r_lst = []
rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M)
if rules:
rules_lst = []
for r in set(rules):
r_regex = r" %s .+$" % r
cfg = "\n".join(findall(r_regex, conf, M))
obj = self.parse_rules(cfg)
obj["number"] = int(r)
if obj:
rules_lst.append(obj)
r_lst = sorted(rules_lst, key=lambda i: i["number"])
return r_lst
def parse_rules(self, conf):
"""
This function triggers the parsing of 'rule' attributes.
a_lst is a list having rule attributes which doesn't
have further sub attributes.
:param conf: configuration
:return: generated rule configuration dictionary.
"""
a_lst = [
"ipsec",
"action",
"protocol",
"fragment",
"disabled",
"description",
]
rule = self.parse_attr(conf, a_lst)
r_sub = {
"p2p": self.parse_p2p(conf),
"tcp": self.parse_tcp(conf, "tcp"),
"icmp": self.parse_icmp(conf, "icmp"),
"time": self.parse_time(conf, "time"),
"limit": self.parse_limit(conf, "limit"),
"state": self.parse_state(conf, "state"),
"recent": self.parse_recent(conf, "recent"),
"source": self.parse_src_or_dest(conf, "source"),
"destination": self.parse_src_or_dest(conf, "destination"),
}
rule.update(r_sub)
return rule
def parse_p2p(self, conf):
"""
This function forms the regex to fetch the 'p2p' with in
'rules'
:param conf: configuration data.
:return: generated rule list configuration.
"""
a_lst = []
applications = findall(r"p2p (?:\'*)(\d+)(?:\'*)", conf, M)
if applications:
app_lst = []
for r in set(applications):
obj = {"application": r.strip("'")}
app_lst.append(obj)
a_lst = sorted(app_lst, key=lambda i: i["application"])
return a_lst
def parse_src_or_dest(self, conf, attrib=None):
"""
This function triggers the parsing of 'source or
destination' attributes.
:param conf: configuration.
:param attrib:'source/destination'.
:return:generated source/destination configuration dictionary.
"""
a_lst = ["port", "address", "mac_address"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
cfg_dict["group"] = self.parse_group(conf, attrib + " group")
return cfg_dict
def parse_recent(self, conf, attrib=None):
"""
This function triggers the parsing of 'recent' attributes
:param conf: configuration.
:param attrib: 'recent'.
:return: generated config dictionary.
"""
a_lst = ["time", "count"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_tcp(self, conf, attrib=None):
"""
This function triggers the parsing of 'tcp' attributes.
:param conf: configuration.
:param attrib: 'tcp'.
:return: generated config dictionary.
"""
cfg_dict = self.parse_attr(conf, ["flags"], match=attrib)
return cfg_dict
def parse_time(self, conf, attrib=None):
"""
This function triggers the parsing of 'time' attributes.
:param conf: configuration.
:param attrib: 'time'.
:return: generated config dictionary.
"""
a_lst = [
"stopdate",
"stoptime",
"weekdays",
"monthdays",
"startdate",
"starttime",
]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_state(self, conf, attrib=None):
"""
This function triggers the parsing of 'state' attributes.
:param conf: configuration
:param attrib: 'state'.
:return: generated config dictionary.
"""
a_lst = ["new", "invalid", "related", "established"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_group(self, conf, attrib=None):
"""
This function triggers the parsing of 'group' attributes.
:param conf: configuration.
:param attrib: 'group'.
:return: generated config dictionary.
"""
a_lst = ["port_group", "address_group", "network_group"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_icmp(self, conf, attrib=None):
"""
This function triggers the parsing of 'icmp' attributes.
:param conf: configuration to be parsed.
:param attrib: 'icmp'.
:return: generated config dictionary.
"""
a_lst = ["code", "type", "type_name"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_limit(self, conf, attrib=None):
"""
This function triggers the parsing of 'limit' attributes.
:param conf: configuration to be parsed.
:param attrib: 'limit'
:return: generated config dictionary.
"""
cfg_dict = self.parse_attr(conf, ["burst"], match=attrib)
cfg_dict["rate"] = self.parse_rate(conf, "rate")
return cfg_dict
def parse_rate(self, conf, attrib=None):
"""
This function triggers the parsing of 'rate' attributes.
:param conf: configuration.
:param attrib: 'rate'
:return: generated config dictionary.
"""
a_lst = ["unit", "number"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
def parse_attr(self, conf, attr_list, match=None):
"""
This function peforms the following:
- Form the regex to fetch the required attribute config.
- Type cast the output in desired format.
:param conf: configuration.
:param attr_list: list of attributes.
:param match: parent node/attribute name.
:return: generated config dictionary.
"""
config = {}
for attrib in attr_list:
regex = self.map_regex(attrib)
if match:
regex = match + " " + regex
if conf:
if self.is_bool(attrib):
out = conf.find(attrib.replace("_", "-"))
dis = conf.find(attrib.replace("_", "-") + " 'disable'")
if out >= 1:
if dis >= 1:
config[attrib] = False
else:
config[attrib] = True
else:
out = search(r"^.*" + regex + " (.+)", conf, M)
if out:
val = out.group(1).strip("'")
if self.is_num(attrib):
val = int(val)
config[attrib] = val
return config
def map_regex(self, attrib):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
regex = attrib.replace("_", "-")
if attrib == "disabled":
regex = "disable"
return regex
def is_bool(self, attrib):
"""
This function looks for the attribute in predefined bool type set.
:param attrib: attribute.
:return: True/False
"""
bool_set = (
"new",
"invalid",
"related",
"disabled",
"established",
"enable_default_log",
)
return True if attrib in bool_set else False
def is_num(self, attrib):
"""
This function looks for the attribute in predefined integer type set.
:param attrib: attribute.
:return: True/false.
"""
num_set = ("time", "code", "type", "count", "burst", "number")
return True if attrib in num_set else False
diff --git a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
index fe8e18c..4b24803 100644
--- a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
@@ -1,135 +1,134 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import (
InterfacesArgs,
)
class InterfacesFacts(object):
""" The vyos interfaces fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = InterfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = connection.get_config(flags=["| grep interfaces"])
objs = []
interface_names = findall(
r"^set interfaces (?:ethernet|bonding|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)",
data,
M,
)
if interface_names:
for interface in set(interface_names):
intf_regex = r" %s .+$" % interface.strip("'")
cfg = findall(intf_regex, data, M)
obj = self.render_config(cfg)
obj["name"] = interface.strip("'")
if obj:
objs.append(obj)
facts = {}
if objs:
facts["interfaces"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["interfaces"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf))
eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf))
config = self.parse_attribs(
["description", "speed", "mtu", "duplex"], eth_conf
)
config["vifs"] = self.parse_vifs(vif_conf)
return utils.remove_empties(config)
def parse_vifs(self, conf):
vif_names = findall(r"vif (?:\'*)(\d+)(?:\'*)", conf, M)
vifs_list = None
if vif_names:
vifs_list = []
for vif in set(vif_names):
vif_regex = r" %s .+$" % vif
cfg = "\n".join(findall(vif_regex, conf, M))
obj = self.parse_attribs(["description", "mtu"], cfg)
obj["vlan_id"] = int(vif)
if obj:
vifs_list.append(obj)
vifs_list = sorted(vifs_list, key=lambda i: i["vlan_id"])
return vifs_list
def parse_attribs(self, attribs, conf):
config = {}
for item in attribs:
value = utils.parse_conf_arg(conf, item)
if value and item == "mtu":
config[item] = int(value.strip("'"))
elif value:
config[item] = value.strip("'")
else:
config[item] = None
if "disable" in conf:
config["enabled"] = False
else:
config["enabled"] = True
return utils.remove_empties(config)
diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
index a21c667..eac8467 100644
--- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
@@ -1,144 +1,143 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos l3_interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import re
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible.module_utils.six import iteritems
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
get_ip_address_version,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import (
L3_interfacesArgs,
)
class L3_interfacesFacts(object):
""" The vyos l3_interfaces fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = L3_interfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for l3_interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = connection.get_config()
# operate on a collection of resource x
objs = []
interface_names = re.findall(
r"set interfaces (?:ethernet|bonding|vti|vxlan) (?:\'*)(\S+)(?:\'*)",
data,
re.M,
)
if interface_names:
for interface in set(interface_names):
intf_regex = r" %s .+$" % interface
cfg = re.findall(intf_regex, data, re.M)
obj = self.render_config(cfg)
obj["name"] = interface.strip("'")
if obj:
objs.append(obj)
ansible_facts["ansible_network_resources"].pop("l3_interfaces", None)
facts = {}
if objs:
facts["l3_interfaces"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["l3_interfaces"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf))
eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf))
config = self.parse_attribs(eth_conf)
config["vifs"] = self.parse_vifs(vif_conf)
return utils.remove_empties(config)
def parse_vifs(self, conf):
vif_names = re.findall(r"vif (\d+)", conf, re.M)
vifs_list = None
if vif_names:
vifs_list = []
for vif in set(vif_names):
vif_regex = r" %s .+$" % vif
cfg = "\n".join(re.findall(vif_regex, conf, re.M))
obj = self.parse_attribs(cfg)
obj["vlan_id"] = vif
if obj:
vifs_list.append(obj)
return vifs_list
def parse_attribs(self, conf):
config = {}
ipaddrs = re.findall(r"address (\S+)", conf, re.M)
config["ipv4"] = []
config["ipv6"] = []
for item in ipaddrs:
item = item.strip("'")
if item == "dhcp":
config["ipv4"].append({"address": item})
elif item == "dhcpv6":
config["ipv6"].append({"address": item})
else:
ip_version = get_ip_address_version(item.split("/")[0])
if ip_version == 4:
config["ipv4"].append({"address": item})
else:
config["ipv6"].append({"address": item})
for key, value in iteritems(config):
if value == []:
config[key] = None
return utils.remove_empties(config)
diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
index b91c3b9..9056294 100644
--- a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py
@@ -1,145 +1,144 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos lag_interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import (
Lag_interfacesArgs,
)
class Lag_interfacesFacts(object):
""" The vyos lag_interfaces fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Lag_interfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for lag_interfaces
:param module: the module instance
:param connection: the device connection
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = connection.get_config()
objs = []
lag_names = findall(r"^set interfaces bonding (\S+)", data, M)
if lag_names:
for lag in set(lag_names):
lag_regex = r" %s .+$" % lag
cfg = findall(lag_regex, data, M)
obj = self.render_config(cfg)
members = []
member = {}
group_regex = r".*eth.* '%s'" % lag
g_cfg = findall(group_regex, data, M)
for item in g_cfg:
output = search("^set interfaces ethernet (\\S+)", item, M)
if output:
member["member"] = output.group(1).strip("'")
members.append(member)
obj["name"] = lag.strip("'")
if members:
obj["members"] = members
if obj:
objs.append(obj)
facts = {}
if objs:
facts["lag_interfaces"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["lag_interfaces"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
arp_monitor_conf = "\n".join(
filter(lambda x: ("arp-monitor" in x), conf)
)
hash_policy_conf = "\n".join(
filter(lambda x: ("hash-policy" in x), conf)
)
lag_conf = "\n".join(filter(lambda x: ("bond" in x), conf))
config = self.parse_attribs(["mode", "primary"], lag_conf)
config["arp_monitor"] = self.parse_arp_monitor(arp_monitor_conf)
config["hash_policy"] = self.parse_hash_policy(hash_policy_conf)
return utils.remove_empties(config)
def parse_attribs(self, attribs, conf):
config = {}
for item in attribs:
value = utils.parse_conf_arg(conf, item)
if value:
config[item] = value.strip("'")
else:
config[item] = None
return utils.remove_empties(config)
def parse_arp_monitor(self, conf):
arp_monitor = None
if conf:
arp_monitor = {}
target_list = []
interval = search(r"^.*arp-monitor interval (.+)", conf, M)
targets = findall(r"^.*arp-monitor target '(.+)'", conf, M)
if targets:
for target in targets:
target_list.append(target)
arp_monitor["target"] = target_list
if interval:
value = interval.group(1).strip("'")
arp_monitor["interval"] = int(value)
return arp_monitor
def parse_hash_policy(self, conf):
hash_policy = None
if conf:
hash_policy = search(r"^.*hash-policy (.+)", conf, M)
hash_policy = hash_policy.group(1).strip("'")
return hash_policy
diff --git a/plugins/module_utils/network/vyos/facts/legacy/base.py b/plugins/module_utils/network/vyos/facts/legacy/base.py
index 764d5dc..f6b343e 100644
--- a/plugins/module_utils/network/vyos/facts/legacy/base.py
+++ b/plugins/module_utils/network/vyos/facts/legacy/base.py
@@ -1,163 +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)
-# pylint: skip-file
"""
The VyOS interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import platform
import re
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
run_commands,
get_capabilities,
)
class LegacyFactsBase(object):
COMMANDS = frozenset()
def __init__(self, module):
self.module = module
self.facts = dict()
self.warnings = list()
self.responses = None
def populate(self):
self.responses = run_commands(self.module, list(self.COMMANDS))
class Default(LegacyFactsBase):
COMMANDS = [
"show version",
]
def populate(self):
super(Default, self).populate()
data = self.responses[0]
self.facts["serialnum"] = self.parse_serialnum(data)
self.facts.update(self.platform_facts())
def parse_serialnum(self, data):
match = re.search(r"HW S/N:\s+(\S+)", data)
if match:
return match.group(1)
def platform_facts(self):
platform_facts = {}
resp = get_capabilities(self.module)
device_info = resp["device_info"]
platform_facts["system"] = device_info["network_os"]
for item in ("model", "image", "version", "platform", "hostname"):
val = device_info.get("network_os_%s" % item)
if val:
platform_facts[item] = val
platform_facts["api"] = resp["network_api"]
platform_facts["python_version"] = platform.python_version()
return platform_facts
class Config(LegacyFactsBase):
COMMANDS = [
"show configuration commands",
"show system commit",
]
def populate(self):
super(Config, self).populate()
self.facts["config"] = self.responses
commits = self.responses[1]
entries = list()
entry = None
for line in commits.split("\n"):
match = re.match(r"(\d+)\s+(.+)by(.+)via(.+)", line)
if match:
if entry:
entries.append(entry)
entry = dict(
revision=match.group(1),
datetime=match.group(2),
by=str(match.group(3)).strip(),
via=str(match.group(4)).strip(),
comment=None,
)
else:
entry["comment"] = line.strip()
self.facts["commits"] = entries
class Neighbors(LegacyFactsBase):
COMMANDS = [
"show lldp neighbors",
"show lldp neighbors detail",
]
def populate(self):
super(Neighbors, self).populate()
all_neighbors = self.responses[0]
if "LLDP not configured" not in all_neighbors:
neighbors = self.parse(self.responses[1])
self.facts["neighbors"] = self.parse_neighbors(neighbors)
def parse(self, data):
parsed = list()
values = None
for line in data.split("\n"):
if not line:
continue
elif line[0] == " ":
values += "\n%s" % line
elif line.startswith("Interface"):
if values:
parsed.append(values)
values = line
if values:
parsed.append(values)
return parsed
def parse_neighbors(self, data):
facts = dict()
for item in data:
interface = self.parse_interface(item)
host = self.parse_host(item)
port = self.parse_port(item)
if interface not in facts:
facts[interface] = list()
facts[interface].append(dict(host=host, port=port))
return facts
def parse_interface(self, data):
match = re.search(r"^Interface:\s+(\S+),", data)
return match.group(1)
def parse_host(self, data):
match = re.search(r"SysName:\s+(.+)$", data, re.M)
if match:
return match.group(1)
def parse_port(self, data):
match = re.search(r"PortDescr:\s+(.+)$", data, re.M)
if match:
return match.group(1)
diff --git a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py
index fa21b68..3c7e2f9 100644
--- a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py
+++ b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py
@@ -1,117 +1,116 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos lldp_global fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import (
Lldp_globalArgs,
)
class Lldp_globalFacts(object):
""" The vyos lldp_global fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Lldp_globalArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for lldp_global
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = connection.get_config()
objs = {}
lldp_output = findall(r"^set service lldp (\S+)", data, M)
if lldp_output:
for item in set(lldp_output):
lldp_regex = r" %s .+$" % item
cfg = findall(lldp_regex, data, M)
obj = self.render_config(cfg)
if obj:
objs.update(obj)
lldp_service = findall(r"^set service (lldp)?('lldp')", data, M)
if lldp_service or lldp_output:
lldp_obj = {}
lldp_obj["enable"] = True
objs.update(lldp_obj)
facts = {}
params = utils.validate_config(self.argument_spec, {"config": objs})
facts["lldp_global"] = utils.remove_empties(params["config"])
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
protocol_conf = "\n".join(
filter(lambda x: ("legacy-protocols" in x), conf)
)
att_conf = "\n".join(
filter(lambda x: ("legacy-protocols" not in x), conf)
)
config = self.parse_attribs(["snmp", "address"], att_conf)
config["legacy_protocols"] = self.parse_protocols(protocol_conf)
return utils.remove_empties(config)
def parse_protocols(self, conf):
protocol_support = None
if conf:
protocols = findall(r"^.*legacy-protocols (.+)", conf, M)
if protocols:
protocol_support = []
for protocol in protocols:
protocol_support.append(protocol.strip("'"))
return protocol_support
def parse_attribs(self, attribs, conf):
config = {}
for item in attribs:
value = utils.parse_conf_arg(conf, item)
if value:
config[item] = value.strip("'")
else:
config[item] = None
return utils.remove_empties(config)
diff --git a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py
index 8cf9ef8..dcfbc6e 100644
--- a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py
@@ -1,156 +1,155 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos lldp_interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import (
Lldp_interfacesArgs,
)
class Lldp_interfacesFacts(object):
""" The vyos lldp_interfaces fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Lldp_interfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for lldp_interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = connection.get_config()
objs = []
lldp_names = findall(r"^set service lldp interface (\S+)", data, M)
if lldp_names:
for lldp in set(lldp_names):
lldp_regex = r" %s .+$" % lldp
cfg = findall(lldp_regex, data, M)
obj = self.render_config(cfg)
obj["name"] = lldp.strip("'")
if obj:
objs.append(obj)
facts = {}
if objs:
facts["lldp_interfaces"] = objs
ansible_facts["ansible_network_resources"].update(facts)
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = {}
location = {}
civic_conf = "\n".join(filter(lambda x: ("civic-based" in x), conf))
elin_conf = "\n".join(filter(lambda x: ("elin" in x), conf))
coordinate_conf = "\n".join(
filter(lambda x: ("coordinate-based" in x), conf)
)
disable = "\n".join(filter(lambda x: ("disable" in x), conf))
coordinate_based_conf = self.parse_attribs(
["altitude", "datum", "longitude", "latitude"], coordinate_conf
)
elin_based_conf = self.parse_lldp_elin_based(elin_conf)
civic_based_conf = self.parse_lldp_civic_based(civic_conf)
if disable:
config["enable"] = False
if coordinate_conf:
location["coordinate_based"] = coordinate_based_conf
config["location"] = location
elif civic_based_conf:
location["civic_based"] = civic_based_conf
config["location"] = location
elif elin_conf:
location["elin"] = elin_based_conf
config["location"] = location
return utils.remove_empties(config)
def parse_attribs(self, attribs, conf):
config = {}
for item in attribs:
value = utils.parse_conf_arg(conf, item)
if value:
value = value.strip("'")
if item == "altitude":
value = int(value)
config[item] = value
else:
config[item] = None
return utils.remove_empties(config)
def parse_lldp_civic_based(self, conf):
civic_based = None
if conf:
civic_info_list = []
civic_add_list = findall(r"^.*civic-based ca-type (.+)", conf, M)
if civic_add_list:
for civic_add in civic_add_list:
ca = civic_add.split(" ")
c_add = {}
c_add["ca_type"] = int(ca[0].strip("'"))
c_add["ca_value"] = ca[2].strip("'")
civic_info_list.append(c_add)
country_code = search(
r"^.*civic-based country-code (.+)", conf, M
)
civic_based = {}
civic_based["ca_info"] = civic_info_list
civic_based["country_code"] = country_code.group(1).strip("'")
return civic_based
def parse_lldp_elin_based(self, conf):
elin_based = None
if conf:
e_num = search(r"^.* elin (.+)", conf, M)
elin_based = e_num.group(1).strip("'")
return elin_based
diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
index b58689d..d62fa9a 100644
--- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
+++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py
@@ -1,500 +1,499 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos ospfv2 fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import (
Ospfv2Args,
)
class Ospfv2Facts(object):
""" The vyos ospfv2 fact class
"""
def __init__(
self, module, subspec="config", options="options",
):
self._module = module
self.argument_spec = Ospfv2Args.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for ospfv2
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = self.get_device_data(connection)
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
objs = {}
ospfv2 = findall(r"^set protocols ospf (.+)", data, M)
if ospfv2:
objs = self.render_config(ospfv2)
facts = {}
params = utils.validate_config(self.argument_spec, {"config": objs})
facts["ospfv2"] = utils.remove_empties(params["config"])
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure
:param conf: The configuration
:returns: The generated config
"""
conf = "\n".join(filter(lambda x: x, conf))
a_lst = ["default_metric", "log_adjacency_changes"]
config = self.parse_attr(conf, a_lst)
if not config:
config = {}
config["timers"] = self.parse_timers(conf)
config["auto_cost"] = self.parse_auto_cost(conf)
config["distance"] = self.parse_distance(conf)
config["max_metric"] = self.parse_max_metric(conf)
config["default_information"] = self.parse_def_info(conf)
config["route_map"] = self.parse_leaf_list(conf, "route-map")
config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te")
config["areas"] = self.parse_attrib_list(conf, "area", "area_id")
config["parameters"] = self.parse_attrib(
conf, "parameters", "parameters"
)
config["neighbor"] = self.parse_attrib_list(
conf, "neighbor", "neighbor_id"
)
config["passive_interface"] = self.parse_leaf_list(
conf, "passive-interface"
)
config["redistribute"] = self.parse_attrib_list(
conf, "redistribute", "route_type"
)
config["passive_interface_exclude"] = self.parse_leaf_list(
conf, "passive-interface-exclude"
)
return config
def parse_timers(self, conf):
"""
This function triggers the parsing of 'timers' attributes
:param conf: configuration
:return: generated config dictionary
"""
cfg_dict = {}
cfg_dict["refresh"] = self.parse_refresh(conf, "refresh")
cfg_dict["throttle"] = self.parse_throttle(conf, "spf")
return cfg_dict
def parse_throttle(self, conf, attrib=None):
"""
This function triggers the parsing of 'throttle' attributes
:param conf: configuration
:param attrib: 'spf'
:return: generated config dictionary
"""
cfg_dict = {}
cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib)
return cfg_dict
def parse_refresh(self, conf, attrib=None):
"""
This function triggers the parsing of 'refresh' attributes
:param conf: configuration
:param attrib: 'refresh'
:return: generated config dictionary
"""
cfg_dict = self.parse_attr(conf, ["timers"], match=attrib)
return cfg_dict
def parse_leaf_list(self, conf, attrib):
"""
This function forms the regex to fetch the listed attributes
from the configuration data
:param conf: configuration data
:param attrib: attribute name
:return: generated rule list configuration
"""
lst = []
items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
if items:
for i in set(items):
lst.append(i.strip("'"))
lst.sort()
return lst
def parse_distance(self, conf, attrib=None):
"""
This function triggers the parsing of 'distance' attributes
:param conf: configuration
:param attrib: attribute name
:return: generated config dictionary
"""
cfg_dict = self.parse_attr(conf, ["global"], match=attrib)
cfg_dict["ospf"] = self.parse_ospf(conf, "ospf")
return cfg_dict
def parse_ospf(self, conf, attrib=None):
"""
This function triggers the parsing of 'distance ospf' attributes
:param conf: configuration
:param attrib: 'ospf'
:return: generated config dictionary
"""
cfg_dict = self.parse_attrib(conf, "ospf", match=attrib)
return cfg_dict
def parse_max_metric(self, conf):
"""
This function triggers the parsing of 'max_metric' attributes
:param conf: configuration
:return: generated config dictionary
"""
cfg_dict = {}
cfg_dict["router_lsa"] = self.parse_attrib(
conf, "router_lsa", match="router-lsa"
)
return cfg_dict
def parse_auto_cost(self, conf, attrib=None):
"""
This function triggers the parsing of 'auto_cost' attributes
:param conf: configuration
:param attrib: attribute name
:return: generated config dictionary
"""
cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib)
return cfg_dict
def parse_def_info(self, conf):
"""
This function triggers the parsing of 'default_information' attributes
:param conf: configuration
:return: generated config dictionary
"""
cfg_dict = {}
cfg_dict["originate"] = self.parse_attrib(
conf, "originate", "originate"
)
return cfg_dict
def parse_area(self, conf, area_id):
"""
This function triggers the parsing of 'area' attributes.
:param conf: configuration data
:param area_id: area identity
:return: generated rule configuration dictionary.
"""
rule = self.parse_attrib(conf, "area_id", match=area_id)
r_sub = {
"area_type": self.parse_area_type(conf, "area-type"),
"network": self.parse_network(conf),
"range": self.parse_attrib_list(conf, "range", "address"),
"virtual_link": self.parse_attrib_list(
conf, "virtual-link", "address"
),
}
rule.update(r_sub)
return rule
def parse_key(self, conf, key_id):
"""
This function triggers the parsing of 'area' attributes.
:param conf: configuration data
:param area_id: area identity
:return: generated rule configuration dictionary.
"""
rule = self.parse_attrib(conf, "key_id", match=key_id)
return rule
def parse_area_type(self, conf, attrib=None):
"""
This function triggers the parsing of 'area_type' attributes
:param conf: configuration
:param attrib: 'area-type'
:return: generated config dictionary
"""
cfg_dict = self.parse_attr(conf, ["normal"], match=attrib)
cfg_dict["nssa"] = self.parse_attrib(conf, "nssa", match="nssa")
cfg_dict["stub"] = self.parse_attrib(conf, "stub", match="stub")
return cfg_dict
def parse_network(self, conf):
"""
This function forms the regex to fetch the 'network'
:param conf: configuration data
:return: generated rule list configuration
"""
a_lst = []
applications = findall(r"network (.+)", conf, M)
if applications:
app_lst = []
for r in set(applications):
obj = {"address": r.strip("'")}
app_lst.append(obj)
a_lst = sorted(app_lst, key=lambda i: i["address"])
return a_lst
def parse_vlink(self, conf):
"""
This function triggers the parsing of 'virtual_link' attributes
:param conf: configuration data
:return: generated rule configuration dictionary
"""
rule = self.parse_attrib(conf, "vlink")
r_sub = {
"authentication": self.parse_authentication(conf, "authentication")
}
rule.update(r_sub)
return rule
def parse_authentication(self, conf, attrib=None):
"""
This function triggers the parsing of 'authentication' attributes.
:param conf: configuration
:param attrib: 'authentication'
:return: generated config dictionary
"""
cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib)
cfg_dict["md5"] = self.parse_attrib_list(conf, "key-id", "key_id")
return cfg_dict
def parse_attrib_list(self, conf, attrib, param):
"""
This function forms the regex to fetch the listed attributes
from config
:param conf: configuration data
:param attrib: attribute name
:param param: parameter data
:return: generated rule list configuration
"""
r_lst = []
if attrib == "area":
items = findall(
r"^" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)",
conf,
M,
)
elif attrib == "key-id":
items = findall(
r"^.*" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)",
conf,
M,
)
else:
items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
if items:
a_lst = []
for item in set(items):
i_regex = r" %s .+$" % item
cfg = "\n".join(findall(i_regex, conf, M))
if attrib == "area":
obj = self.parse_area(cfg, item)
elif attrib == "virtual-link":
obj = self.parse_vlink(cfg)
elif attrib == "key-id":
obj = self.parse_key(cfg, item)
else:
obj = self.parse_attrib(cfg, attrib)
obj[param] = item.strip("'")
if obj:
a_lst.append(obj)
r_lst = sorted(a_lst, key=lambda i: i[param])
return r_lst
def parse_attrib(self, conf, param, match=None):
"""
This function triggers the parsing of 'ospf' attributes
:param conf: configuration data
:return: generated configuration dictionary
"""
param_lst = {
"key_id": ["md5_key"],
"mpls_te": ["enabled", "router_address"],
"area_id": ["shortcut", "authentication"],
"neighbor": ["priority", "poll_interval"],
"stub": ["set", "default_cost", "no_summary"],
"range": ["cost", "substitute", "not_advertise"],
"ospf": ["external", "inter_area", "intra_area"],
"spf": ["delay", "max_holdtime", "initial_holdtime"],
"redistribute": ["metric", "metric_type", "route_map"],
"nssa": ["set", "translate", "default_cost", "no_summary"],
"config_routes": ["default_metric", "log_adjacency_changes"],
"originate": ["always", "metric", "metric_type", "route_map"],
"router_lsa": ["administrative", "on_shutdown", "on_startup"],
"parameters": [
"abr_type",
"opaque_lsa",
"router_id",
"rfc1583_compatibility",
],
"vlink": [
"dead_interval",
"hello_interval",
"transmit_delay",
"retransmit_interval",
],
}
cfg_dict = self.parse_attr(conf, param_lst[param], match)
return cfg_dict
def parse_attr(self, conf, attr_list, match=None):
"""
This function peforms the following:
- Form the regex to fetch the required attribute config.
- Type cast the output in desired format.
:param conf: configuration.
:param attr_list: list of attributes.
:param match: parent node/attribute name.
:return: generated config dictionary.
"""
config = {}
for attrib in attr_list:
regex = self.map_regex(attrib)
if match:
regex = match.replace("_", "-") + " " + regex
if conf:
if self.is_bool(attrib):
out = conf.find(attrib.replace("_", "-"))
dis = conf.find(attrib.replace("_", "-") + " 'disable'")
if match:
if attrib == "set" and conf.find(match) >= 1:
config[attrib] = True
en = conf.find(match + " 'enable'")
if out >= 1:
if dis >= 1:
config[attrib] = False
else:
config[attrib] = True
elif match and en >= 1:
config[attrib] = True
else:
out = search(r"^.*" + regex + " (.+)", conf, M)
if out:
val = out.group(1).strip("'")
if self.is_num(attrib):
val = int(val)
config[attrib] = val
return config
def map_regex(self, attrib):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
return (
"disable"
if attrib == "disabled"
else (
"enable"
if attrib == "enabled"
else (
"area" if attrib == "area_id" else attrib.replace("_", "-")
)
)
)
def is_bool(self, attrib):
"""
This function looks for the attribute in predefined bool type set.
:param attrib: attribute.
:return: True/False
"""
bool_set = (
"set",
"always",
"normal",
"enabled",
"opaque_lsa",
"not_advertise",
"administrative",
"rfc1583_compatibility",
)
return True if attrib in bool_set else False
def is_num(self, attrib):
"""
This function looks for the attribute in predefined integer type set.
:param attrib: attribute.
:return: True/false.
"""
num_set = (
"ospf",
"delay",
"metric",
"inter_area",
"intra_area",
"on_startup",
"metric_type",
"on_shutdown",
"max_holdtime",
"poll_interval",
"default_metric",
"initial_holdtime",
"key_id",
)
return True if attrib in num_set else False
diff --git a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py
index 8c2681e..457a963 100644
--- a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py
+++ b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py
@@ -1,214 +1,213 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
"""
The vyos ospfv3 fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import (
Ospfv3Args,
)
class Ospfv3Facts(object):
""" The vyos ospfv3 fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Ospfv3Args.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for ospfv3
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = self.get_device_data(connection)
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
objs = {}
ospfv3 = findall(r"^set protocols ospfv3 (.+)", data, M)
if ospfv3:
objs = self.render_config(ospfv3)
facts = {}
params = utils.validate_config(self.argument_spec, {"config": objs})
facts["ospfv3"] = utils.remove_empties(params["config"])
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure
:param conf: The configuration
:returns: The generated config
"""
conf = "\n".join(filter(lambda x: x, conf))
config = {}
config["parameters"] = self.parse_attrib(
conf, "parameters", "parameters"
)
config["areas"] = self.parse_attrib_list(conf, "area", "area_id")
config["redistribute"] = self.parse_attrib_list(
conf, "redistribute", "route_type"
)
return config
def parse_attrib_list(self, conf, attrib, param):
"""
This function forms the regex to fetch the listed attributes
from config
:param conf: configuration data
:param attrib: attribute name
:param param: parameter data
:return: generated rule list configuration
"""
r_lst = []
if attrib == "area":
items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
else:
items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M)
if items:
a_lst = []
for item in set(items):
i_regex = r" %s .+$" % item
cfg = "\n".join(findall(i_regex, conf, M))
if attrib == "area":
obj = self.parse_area(cfg, item)
else:
obj = self.parse_attrib(cfg, attrib)
obj[param] = item.strip("'")
if obj:
a_lst.append(obj)
r_lst = sorted(a_lst, key=lambda i: i[param])
return r_lst
def parse_area(self, conf, area_id):
"""
This function triggers the parsing of 'area' attributes.
:param conf: configuration data
:param area_id: area identity
:return: generated rule configuration dictionary.
"""
rule = self.parse_attrib(conf, "area_id", match=area_id)
r_sub = {"range": self.parse_attrib_list(conf, "range", "address")}
rule.update(r_sub)
return rule
def parse_attrib(self, conf, param, match=None):
"""
This function triggers the parsing of 'ospf' attributes
:param conf: configuration data
:return: generated configuration dictionary
"""
param_lst = {
"area_id": ["export_list", "import_list"],
"redistribute": ["route_map"],
"range": ["advertise", "not_advertise"],
"parameters": ["router_id"],
}
cfg_dict = self.parse_attr(conf, param_lst[param], match)
return cfg_dict
def parse_attr(self, conf, attr_list, match=None):
"""
This function peforms the following:
- Form the regex to fetch the required attribute config.
- Type cast the output in desired format.
:param conf: configuration.
:param attr_list: list of attributes.
:param match: parent node/attribute name.
:return: generated config dictionary.
"""
config = {}
for attrib in attr_list:
regex = self.map_regex(attrib)
if match:
regex = match.replace("_", "-") + " " + regex
if conf:
if self.is_bool(attrib):
out = conf.find(attrib.replace("_", "-"))
dis = conf.find(attrib.replace("_", "-") + " 'disable'")
if match:
en = conf.find(match + " 'enable'")
if out >= 1:
if dis >= 1:
config[attrib] = False
else:
config[attrib] = True
elif match and en >= 1:
config[attrib] = True
else:
out = search(r"^.*" + regex + " (.+)", conf, M)
if out:
val = out.group(1).strip("'")
if self.is_num(attrib):
val = int(val)
config[attrib] = val
return config
def map_regex(self, attrib):
"""
- This function construct the regex string.
- replace the underscore with hyphen.
:param attrib: attribute
:return: regex string
"""
return (
"disable"
if attrib == "disabled"
else "enable"
if attrib == "enabled"
else attrib.replace("_", "-")
)
def is_bool(self, attrib):
"""
This function looks for the attribute in predefined bool type set.
:param attrib: attribute.
:return: True/False
"""
bool_set = ("enabled", "advertise", "not_advertise")
return True if attrib in bool_set else False
def is_num(self, attrib):
"""
This function looks for the attribute in predefined integer type set.
:param attrib: attribute.
:return: True/false.
"""
num_set = "ospf"
return True if attrib in num_set else False
diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py
index 2c92aa8..0004947 100644
--- a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py
+++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py
@@ -1,182 +1,181 @@
#
# -*- 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)
-# pylint: skip-file
"""
The vyos static_routes fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from re import findall, search, M
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
utils,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import (
Static_routesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import (
get_route_type,
)
class Static_routesFacts(object):
""" The vyos static_routes fact class
"""
def __init__(self, module, subspec="config", options="options"):
self._module = module
self.argument_spec = Static_routesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_device_data(self, connection):
return connection.get_config()
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for static_routes
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = self.get_device_data(connection)
# typically data is populated from the current device configuration
# data = connection.get('show running-config | section ^interface')
# using mock data instead
objs = []
r_v4 = []
r_v6 = []
af = []
static_routes = findall(
r"set protocols static route(6)? (\S+)", data, M
)
if static_routes:
for route in set(static_routes):
route_regex = r" %s .+$" % route[1]
cfg = findall(route_regex, data, M)
sr = self.render_config(cfg)
sr["dest"] = route[1].strip("'")
afi = self.get_afi(sr["dest"])
if afi == "ipv4":
r_v4.append(sr)
else:
r_v6.append(sr)
if r_v4:
afi_v4 = {"afi": "ipv4", "routes": r_v4}
af.append(afi_v4)
if r_v6:
afi_v6 = {"afi": "ipv6", "routes": r_v6}
af.append(afi_v6)
config = {"address_families": af}
if config:
objs.append(config)
ansible_facts["ansible_network_resources"].pop("static_routes", None)
facts = {}
if objs:
facts["static_routes"] = []
params = utils.validate_config(
self.argument_spec, {"config": objs}
)
for cfg in params["config"]:
facts["static_routes"].append(utils.remove_empties(cfg))
ansible_facts["ansible_network_resources"].update(facts)
return ansible_facts
def render_config(self, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x), conf))
blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf))
routes_dict = {
"blackhole_config": self.parse_blackhole(blackhole_conf),
"next_hops": self.parse_next_hop(next_hops_conf),
}
return routes_dict
def parse_blackhole(self, conf):
blackhole = None
if conf:
distance = search(r"^.*blackhole distance (.\S+)", conf, M)
bh = conf.find("blackhole")
if distance is not None:
blackhole = {}
value = distance.group(1).strip("'")
blackhole["distance"] = int(value)
elif bh:
blackhole = {}
blackhole["type"] = "blackhole"
return blackhole
def get_afi(self, address):
route_type = get_route_type(address)
if route_type == "route":
return "ipv4"
elif route_type == "route6":
return "ipv6"
def parse_next_hop(self, conf):
nh_list = None
if conf:
nh_list = []
hop_list = findall(r"^.*next-hop (.+)", conf, M)
if hop_list:
for hop in hop_list:
distance = search(r"^.*distance (.\S+)", hop, M)
interface = search(r"^.*interface (.\S+)", hop, M)
dis = hop.find("disable")
hop_info = hop.split(" ")
nh_info = {
"forward_router_address": hop_info[0].strip("'")
}
if interface:
nh_info["interface"] = interface.group(1).strip("'")
if distance:
value = distance.group(1).strip("'")
nh_info["admin_distance"] = int(value)
elif dis >= 1:
nh_info["enabled"] = False
for element in nh_list:
if (
element["forward_router_address"]
== nh_info["forward_router_address"]
):
if "interface" in nh_info.keys():
element["interface"] = nh_info["interface"]
if "admin_distance" in nh_info.keys():
element["admin_distance"] = nh_info[
"admin_distance"
]
if "enabled" in nh_info.keys():
element["enabled"] = nh_info["enabled"]
nh_info = None
if nh_info is not None:
nh_list.append(nh_info)
return nh_list
diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py
index c00448a..7e0f3cc 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -1,270 +1,269 @@
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
# utils
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.compat import (
ipaddress,
)
def search_obj_in_list(name, lst, key="name"):
if lst:
for item in lst:
if item[key] == name:
return item
return None
def get_interface_type(interface):
"""Gets the type of interface
"""
if interface.startswith("eth"):
return "ethernet"
elif interface.startswith("bond"):
return "bonding"
elif interface.startswith("vti"):
return "vti"
elif interface.startswith("lo"):
return "loopback"
def dict_delete(base, comparable):
"""
This function generates a dict containing key, value pairs for keys
that are present in the `base` dict but not present in the `comparable`
dict.
:param base: dict object to base the diff on
:param comparable: dict object to compare against base
:returns: new dict object with key, value pairs that needs to be deleted.
"""
to_delete = dict()
for key in base:
if isinstance(base[key], dict):
sub_diff = dict_delete(base[key], comparable.get(key, {}))
if sub_diff:
to_delete[key] = sub_diff
else:
if key not in comparable:
to_delete[key] = base[key]
return to_delete
def diff_list_of_dicts(want, have):
diff = []
set_w = set(tuple(d.items()) for d in want)
set_h = set(tuple(d.items()) for d in have)
difference = set_w.difference(set_h)
for element in difference:
diff.append(dict((x, y) for x, y in element))
return diff
def get_lst_diff_for_dicts(want, have, lst):
"""
This function generates a list containing values
that are only in want and not in list in have dict
:param want: dict object to want
:param have: dict object to have
:param lst: list the diff on
:return: new list object with values which are only in want.
"""
if not have:
diff = want.get(lst) or []
else:
want_elements = want.get(lst) or {}
have_elements = have.get(lst) or {}
diff = list_diff_want_only(want_elements, have_elements)
return diff
def get_lst_same_for_dicts(want, have, lst):
"""
This function generates a list containing values
that are common for list in want and list in have dict
:param want: dict object to want
:param have: dict object to have
:param lst: list the comparison on
:return: new list object with values which are common in want and have.
"""
diff = None
if want and have:
want_list = want.get(lst) or {}
have_list = have.get(lst) or {}
diff = [
i
for i in want_list and have_list
if i in have_list and i in want_list
]
return diff
def list_diff_have_only(want_list, have_list):
"""
This function generated the list containing values
that are only in have list.
:param want_list:
:param have_list:
:return: new list with values which are only in have list
"""
if have_list and not want_list:
diff = have_list
elif not have_list:
diff = None
else:
diff = [
i
for i in have_list + want_list
if i in have_list and i not in want_list
]
return diff
def list_diff_want_only(want_list, have_list):
"""
This function generated the list containing values
that are only in want list.
:param want_list:
:param have_list:
:return: new list with values which are only in want list
"""
if have_list and not want_list:
diff = None
elif not have_list:
diff = want_list
else:
diff = [
i
for i in have_list + want_list
if i in want_list and i not in have_list
]
return diff
def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2):
"""
This function return the dict object if it exist in list.
:param d_val1:
:param d_val2:
:param lst:
:param key1:
:param key2:
:return:
"""
obj = next(
(
item
for item in lst
if item[key1] == d_val1 and item[key2] == d_val2
),
None,
)
if obj:
return obj
else:
return None
def key_value_in_dict(have_key, have_value, want_dict):
"""
This function checks whether the key and values exist in dict
:param have_key:
:param have_value:
:param want_dict:
:return:
"""
for key, value in iteritems(want_dict):
if key == have_key and value == have_value:
return True
return False
def is_dict_element_present(dict, key):
"""
This function checks whether the key is present in dict.
:param dict:
:param key:
:return:
"""
for item in dict:
if item == key:
return True
return False
def get_ip_address_version(address):
"""
This function returns the version of IP address
:param address: IP address
:return:
"""
try:
address = unicode(address)
except NameError:
address = str(address)
version = ipaddress.ip_address(address.split("/")[0]).version
return version
def get_route_type(address):
"""
This function returns the route type based on IP address
:param address:
:return:
"""
version = get_ip_address_version(address)
if version == 6:
return "route6"
elif version == 4:
return "route"
def _bool_to_str(val):
"""
This function converts the bool value into string.
:param val: bool value.
:return: enable/disable.
"""
return (
"enable"
if str(val) == "True"
else "disable"
if str(val) == "False"
else val
)
def _is_w_same(w, h, key):
"""
This function checks whether the key value is same in desired and
target config dictionary.
:param w: base config.
:param h: target config.
:param key:attribute name.
:return: True/False.
"""
return True if h and key in h and h[key] == w[key] else False
def _in_target(h, key):
"""
This function checks whether the target exist and key present in target config.
:param h: target config.
:param key: attribute name.
:return: True/False.
"""
return True if h and key in h else False
diff --git a/plugins/module_utils/network/vyos/vyos.py b/plugins/module_utils/network/vyos/vyos.py
index c4c77e3..908395a 100644
--- a/plugins/module_utils/network/vyos/vyos.py
+++ b/plugins/module_utils/network/vyos/vyos.py
@@ -1,125 +1,124 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# (c) 2016 Red Hat Inc.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
-# pylint: skip-file
import json
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.connection import Connection, ConnectionError
_DEVICE_CONFIGS = {}
vyos_provider_spec = {
"host": dict(),
"port": dict(type="int"),
"username": dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])),
"password": dict(
fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True
),
"ssh_keyfile": dict(
fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"]), type="path"
),
"timeout": dict(type="int"),
}
vyos_argument_spec = {
"provider": dict(
type="dict", options=vyos_provider_spec, removed_in_version=2.14
),
}
def get_provider_argspec():
return vyos_provider_spec
def get_connection(module):
if hasattr(module, "_vyos_connection"):
return module._vyos_connection
capabilities = get_capabilities(module)
network_api = capabilities.get("network_api")
if network_api == "cliconf":
module._vyos_connection = Connection(module._socket_path)
else:
module.fail_json(msg="Invalid connection type %s" % network_api)
return module._vyos_connection
def get_capabilities(module):
if hasattr(module, "_vyos_capabilities"):
return module._vyos_capabilities
try:
capabilities = Connection(module._socket_path).get_capabilities()
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
module._vyos_capabilities = json.loads(capabilities)
return module._vyos_capabilities
def get_config(module, flags=None, format=None):
flags = [] if flags is None else flags
global _DEVICE_CONFIGS
if _DEVICE_CONFIGS != {}:
return _DEVICE_CONFIGS
else:
connection = get_connection(module)
try:
out = connection.get_config(flags=flags, format=format)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
cfg = to_text(out, errors="surrogate_then_replace").strip()
_DEVICE_CONFIGS = cfg
return cfg
def run_commands(module, commands, check_rc=True):
connection = get_connection(module)
try:
response = connection.run_commands(
commands=commands, check_rc=check_rc
)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
return response
def load_config(module, commands, commit=False, comment=None):
connection = get_connection(module)
try:
response = connection.edit_config(
candidate=commands, commit=commit, comment=comment
)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
return response.get("diff")
diff --git a/plugins/modules/vyos_banner.py b/plugins/modules/vyos_banner.py
index e11173e..aa0bd5c 100644
--- a/plugins/modules/vyos_banner.py
+++ b/plugins/modules/vyos_banner.py
@@ -1,194 +1,193 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
DOCUMENTATION = """
module: vyos_banner
author: Trishna Guha (@trishnaguha)
short_description: Manage multiline banners on VyOS devices
description:
- This will configure both pre-login and post-login banners on remote devices running
VyOS. It allows playbooks to add or remote banner text from the active running configuration.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
banner:
description:
- Specifies which banner that should be configured on the remote device.
required: true
choices:
- pre-login
- post-login
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).
state:
description:
- Specifies whether or not the configuration is present in the current devices
active running configuration.
default: present
choices:
- present
- absent
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: configure the pre-login banner
vyos.vyos.vyos_banner:
banner: pre-login
text: |
this is my pre-login banner
that contains a multiline
string
state: present
- name: remove the post-login banner
vyos.vyos.vyos_banner:
banner: post-login
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- banner pre-login
- this is my pre-login banner
- that contains a multiline
- string
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def spec_to_commands(updates, module):
commands = list()
want, have = updates
state = module.params["state"]
if state == "absent":
if have.get("state") != "absent" or (
have.get("state") != "absent"
and "text" in have.keys()
and have["text"]
):
commands.append(
"delete system login banner %s" % module.params["banner"]
)
elif state == "present":
if want["text"] and want["text"].encode().decode(
"unicode_escape"
) != have.get("text"):
banner_cmd = (
"set system login banner %s " % module.params["banner"]
)
banner_cmd += want["text"].strip()
commands.append(banner_cmd)
return commands
def config_to_dict(module):
data = get_config(module)
output = None
obj = {"banner": module.params["banner"], "state": "absent"}
for line in data.split("\n"):
if line.startswith("set system login banner %s" % obj["banner"]):
match = re.findall(r"%s (.*)" % obj["banner"], line, re.M)
output = match
if output:
obj["text"] = output[0].encode().decode("unicode_escape")
obj["state"] = "present"
return obj
def map_params_to_obj(module):
text = module.params["text"]
if text:
text = "%r" % (str(text).strip())
return {
"banner": module.params["banner"],
"text": text,
"state": module.params["state"],
}
def main():
""" main entry point for module execution
"""
argument_spec = dict(
banner=dict(required=True, choices=["pre-login", "post-login"]),
text=dict(),
state=dict(default="present", choices=["present", "absent"]),
)
argument_spec.update(vyos_argument_spec)
required_if = [("state", "present", ("text",))]
module = AnsibleModule(
argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = config_to_dict(module)
commands = spec_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_command.py b/plugins/modules/vyos_command.py
index 2bf5899..eec4344 100644
--- a/plugins/modules/vyos_command.py
+++ b/plugins/modules/vyos_command.py
@@ -1,220 +1,219 @@
#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
DOCUMENTATION = """
module: vyos_command
author: Nathaniel Case (@Qalthos)
short_description: Run one or more commands on VyOS devices
description:
- The command module allows running one or more commands on remote devices running
VyOS. This module can also be introspected to validate key parameters before returning
successfully. If the conditional statements are not met in the wait period, the
task fails.
- Certain C(show) commands in VyOS produce many lines of output and use a custom pager
that can cause this module to hang. If the value of the environment variable C(ANSIBLE_VYOS_TERMINAL_LENGTH)
is not set, the default number of 10000 is used.
version_added: 1.0.0
extends_documentation_fragment:
- vyos.vyos.vyos
options:
commands:
description:
- The ordered set of commands to execute on the remote device running VyOS. The
output from the command execution is returned to the playbook. If the I(wait_for)
argument is provided, the module is not returned until the condition is satisfied
or the number of retries has been exceeded.
required: true
wait_for:
description:
- Specifies what to evaluate from the output of the command and what conditionals
to apply. This argument will cause the task to wait for a particular conditional
to be true before moving forward. If the conditional is not true by the configured
I(retries), the task fails. See examples.
aliases:
- waitfor
match:
description:
- The I(match) argument is used in conjunction with the I(wait_for) argument to
specify the match policy. Valid values are C(all) or C(any). If the value is
set to C(all) then all conditionals in the wait_for must be satisfied. If the
value is set to C(any) then only one of the values must be satisfied.
default: all
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.
default: 10
interval:
description:
- Configures the interval in seconds to wait between I(retries) of the command.
If the command does not pass the specified conditions, the interval indicates
how long to wait before trying the command again.
default: 1
notes:
- Tested against VyOS 1.1.8 (helium).
- Running C(show system boot-messages all) will cause the module to hang since VyOS
is using a custom pager setting to display the output of that command.
- If a command sent to the device requires answering a prompt, it is possible to pass
a dict containing I(command), I(answer) and I(prompt). See examples.
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
"""
EXAMPLES = """
- name: show configuration on ethernet devices eth0 and eth1
vyos.vyos.vyos_command:
commands:
- show interfaces ethernet {{ item }}
with_items:
- eth0
- eth1
- name: run multiple commands and check if version output contains specific version
string
vyos.vyos.vyos_command:
commands:
- show version
- show hardware cpu
wait_for:
- result[0] contains 'VyOS 1.1.7'
- name: run command that requires answering a prompt
vyos.vyos.vyos_command:
commands:
- command: rollback 1
prompt: Proceed with reboot? [confirm][y]
answer: y
"""
RETURN = """
stdout:
description: The set of responses from the commands
returned: always apart from low level errors (such as action plugin)
type: list
sample: ['...', '...']
stdout_lines:
description: The value of stdout split into a list
returned: always
type: list
sample: [['...', '...'], ['...'], ['...']]
failed_conditions:
description: The list of conditionals that have failed
returned: failed
type: list
sample: ['...', '...']
warnings:
description: The list of warnings (if any) generated by module based on arguments
returned: always
type: list
sample: ['...', '...']
"""
import time
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import (
Conditional,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
transform_commands,
to_lines,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def parse_commands(module, warnings):
commands = transform_commands(module)
if module.check_mode:
for item in list(commands):
if not item["command"].startswith("show"):
warnings.append(
"Only show commands are supported when using check mode, not "
"executing %s" % item["command"]
)
commands.remove(item)
return commands
def main():
spec = dict(
commands=dict(type="list", required=True),
wait_for=dict(type="list", aliases=["waitfor"]),
match=dict(default="all", choices=["all", "any"]),
retries=dict(default=10, type="int"),
interval=dict(default=1, type="int"),
)
spec.update(vyos_argument_spec)
module = AnsibleModule(argument_spec=spec, supports_check_mode=True)
warnings = list()
result = {"changed": False, "warnings": warnings}
commands = parse_commands(module, warnings)
wait_for = module.params["wait_for"] or list()
try:
conditionals = [Conditional(c) for c in wait_for]
except AttributeError as exc:
module.fail_json(msg=to_text(exc))
retries = module.params["retries"]
interval = module.params["interval"]
match = module.params["match"]
- for _ in range(retries):
+ for item in range(retries):
responses = run_commands(module, commands)
for item in list(conditionals):
if item(responses):
if match == "any":
conditionals = list()
break
conditionals.remove(item)
if not conditionals:
break
time.sleep(interval)
if conditionals:
failed_conditions = [item.raw for item in conditionals]
msg = "One or more conditional statements have not been satisfied"
module.fail_json(msg=msg, failed_conditions=failed_conditions)
result.update(
{"stdout": responses, "stdout_lines": list(to_lines(responses))}
)
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py
index 0453e33..baa63f3 100644
--- a/plugins/modules/vyos_config.py
+++ b/plugins/modules/vyos_config.py
@@ -1,351 +1,350 @@
#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
DOCUMENTATION = """
module: vyos_config
author: Nathaniel Case (@Qalthos)
short_description: Manage VyOS configuration on remote device
description:
- This module provides configuration file management of VyOS devices. It provides
arguments for managing both the configuration file and state of the active configuration.
All configuration statements are based on `set` and `delete` commands in the device
configuration.
version_added: 1.0.0
extends_documentation_fragment:
- vyos.vyos.vyos
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
lines:
description:
- The ordered set of configuration lines to be managed and compared with the existing
configuration on the remote device.
src:
description:
- The C(src) argument specifies the path to the source config file to load. The
source config file can either be in bracket format or set format. The source
file can include Jinja2 template variables.
match:
description:
- The C(match) argument controls the method used to match against the current
active configuration. By default, the desired config is matched against the
active config and the deltas are loaded. If the C(match) argument is set to
C(none) the active configuration is ignored and the configuration is always
loaded.
default: line
choices:
- line
- none
backup:
description:
- The C(backup) argument will backup the current devices active configuration
to the Ansible control host prior to making any changes. If the C(backup_options)
value is not given, the backup file will be located in the backup folder in
the playbook root directory or role root directory, if playbook is part of an
ansible role. If the directory does not exist, it is created.
type: bool
default: no
comment:
description:
- Allows a commit description to be specified to be included when the configuration
is committed. If the configuration is not changed or committed, this argument
is ignored.
default: configured by vyos_config
config:
description:
- The C(config) argument specifies the base configuration to use to compare against
the desired configuration. If this value is not specified, the module will
automatically retrieve the current active configuration from the remote device.
save:
description:
- The C(save) argument controls whether or not changes made to the active configuration
are saved to disk. This is independent of committing the config. When set
to True, the active configuration is saved.
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 _config.@
dir_path:
description:
- This option provides the path ending with directory name in which the backup
configuration file will be stored. If the directory does not exist it will
be first created and the filename is either the value of C(filename) or
default filename as described in C(filename) options description. If the
path value is not given in that case a I(backup) directory will be created
in the current working directory and backup configuration will be copied
in C(filename) within I(backup) directory.
type: path
type: dict
"""
EXAMPLES = """
- name: configure the remote device
vyos.vyos.vyos_config:
lines:
- set system host-name {{ inventory_hostname }}
- set service lldp
- delete service dhcp-server
- name: backup and load from file
vyos.vyos.vyos_config:
src: vyos.cfg
backup: yes
- name: render a Jinja2 template onto the VyOS router
vyos.vyos.vyos_config:
src: vyos_template.j2
- name: for idempotency, use full-form commands
vyos.vyos.vyos_config:
lines:
# - set int eth eth2 description 'OUTSIDE'
- set interface ethernet eth2 description 'OUTSIDE'
- name: configurable backup path
vyos.vyos.vyos_config:
backup: yes
backup_options:
filename: backup.cfg
dir_path: /home/user
"""
RETURN = """
commands:
description: The list of configuration commands sent to the device
returned: always
type: list
sample: ['...', '...']
filtered:
description: The list of configuration commands removed to avoid a load failure
returned: always
type: list
sample: ['...', '...']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: str
sample: /playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34
filename:
description: The name of the backup file
returned: when backup is yes and filename is not specified in backup options
type: str
sample: vyos_config.2016-07-16@22:28:34
shortname:
description: The full path to the backup file excluding the timestamp
returned: when backup is yes and filename is not specified in backup options
type: str
sample: /playbooks/ansible/backup/vyos_config
date:
description: The date extracted from the backup file name
returned: when backup is yes
type: str
sample: "2016-07-16"
time:
description: The time extracted from the backup file name
returned: when backup is yes
type: str
sample: "22:28:34"
"""
import re
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import ConnectionError
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
get_config,
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
get_connection,
)
DEFAULT_COMMENT = "configured by vyos_config"
CONFIG_FILTERS = [
re.compile(r"set system login user \S+ authentication encrypted-password")
]
def get_candidate(module):
contents = module.params["src"] or module.params["lines"]
if module.params["src"]:
contents = format_commands(contents.splitlines())
contents = "\n".join(contents)
return contents
def format_commands(commands):
"""
This function format the input commands and removes the prepend white spaces
for command lines having 'set' or 'delete' and it skips empty lines.
:param commands:
:return: list of commands
"""
return [
line.strip() if line.split()[0] in ("set", "delete") else line
for line in commands
if len(line.strip()) > 0
]
def diff_config(commands, config):
config = [str(c).replace("'", "") for c in config.splitlines()]
updates = list()
visited = set()
for line in commands:
item = str(line).replace("'", "")
if not item.startswith("set") and not item.startswith("delete"):
raise ValueError("line must start with either `set` or `delete`")
elif item.startswith("set") and item not in config:
updates.append(line)
elif item.startswith("delete"):
if not config:
updates.append(line)
else:
item = re.sub(r"delete", "set", item)
for entry in config:
if entry.startswith(item) and line not in visited:
updates.append(line)
visited.add(line)
return list(updates)
def sanitize_config(config, result):
result["filtered"] = list()
index_to_filter = list()
for regex in CONFIG_FILTERS:
for index, line in enumerate(list(config)):
if regex.search(line):
result["filtered"].append(line)
index_to_filter.append(index)
# Delete all filtered configs
for filter_index in sorted(index_to_filter, reverse=True):
del config[filter_index]
def run(module, result):
# get the current active config from the node or passed in via
# the config param
config = module.params["config"] or get_config(module)
# create the candidate config object from the arguments
candidate = get_candidate(module)
# create loadable config that includes only the configuration updates
connection = get_connection(module)
try:
response = connection.get_diff(
candidate=candidate,
running=config,
diff_match=module.params["match"],
)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
commands = response.get("config_diff")
sanitize_config(commands, result)
result["commands"] = commands
commit = not module.check_mode
comment = module.params["comment"]
diff = None
if commands:
diff = load_config(module, commands, commit=commit, comment=comment)
if result.get("filtered"):
result["warnings"].append(
"Some configuration commands were "
"removed, please see the filtered key"
)
result["changed"] = True
if module._diff:
result["diff"] = {"prepared": diff}
def main():
backup_spec = dict(filename=dict(), dir_path=dict(type="path"))
argument_spec = dict(
src=dict(type="path"),
lines=dict(type="list"),
match=dict(default="line", choices=["line", "none"]),
comment=dict(default=DEFAULT_COMMENT),
config=dict(),
backup=dict(type="bool", default=False),
backup_options=dict(type="dict", options=backup_spec),
save=dict(type="bool", default=False),
)
argument_spec.update(vyos_argument_spec)
mutually_exclusive = [("lines", "src")]
module = AnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = dict(changed=False, warnings=warnings)
if module.params["backup"]:
result["__backup__"] = get_config(module=module)
if any((module.params["src"], module.params["lines"])):
run(module, result)
if module.params["save"]:
diff = run_commands(module, commands=["configure", "compare saved"])[1]
if diff != "[edit]":
run_commands(module, commands=["save"])
result["changed"] = True
run_commands(module, commands=["exit"])
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py
index 42c327e..7521a3b 100644
--- a/plugins/modules/vyos_facts.py
+++ b/plugins/modules/vyos_facts.py
@@ -1,171 +1,170 @@
#!/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)
-# pylint: skip-file
"""
The module file for vyos_facts
"""
DOCUMENTATION = """
module: vyos_facts
short_description: Get facts about vyos devices.
description:
- Collects facts from network devices running the vyos operating system. This module
places the facts gathered in the fact tree keyed by the respective resource name. The
facts module will always collect a base set of facts from the device and can enable
or disable collection of additional facts.
version_added: 1.0.0
author:
- Nathaniel Case (@qalthos)
- Nilashish Chakraborty (@Nilashishc)
- Rohit Thakur (@rohitthakur2590)
extends_documentation_fragment:
- vyos.vyos.vyos
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected to a given subset. Possible
values for this argument include all, default, config, and neighbors. Can specify
a list of values to include a larger subset. Values can also be used with an
initial C(M(!)) to specify that a specific subset should not be collected.
required: false
default: '!config'
gather_network_resources:
description:
- When supplied, this argument will restrict the facts collected to a given subset.
Possible values for this argument include all and the resources like interfaces.
Can specify a list of values to include a larger subset. Values can also be
used with an initial C(M(!)) to specify that a specific subset should not be
collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces',
'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global',
'firewall_interfaces', 'ospfv3', 'ospfv2'.
required: false
"""
EXAMPLES = """
# Gather all facts
- vyos.vyos.vyos_facts:
gather_subset: all
gather_network_resources: all
# collect only the config and default facts
- vyos.vyos.vyos_facts:
gather_subset: config
# collect everything exception the config
- vyos.vyos.vyos_facts:
gather_subset: '!config'
# Collect only the interfaces facts
- vyos.vyos.vyos_facts:
gather_subset:
- '!all'
- '!min'
gather_network_resources:
- interfaces
# Do not collect interfaces facts
- vyos.vyos.vyos_facts:
gather_network_resources:
- '!interfaces'
# Collect interfaces and minimal default facts
- vyos.vyos.vyos_facts:
gather_subset: min
gather_network_resources: interfaces
"""
RETURN = """
ansible_net_config:
description: The running-config from the device
returned: when config is configured
type: str
ansible_net_commits:
description: The set of available configuration revisions
returned: when present
type: list
ansible_net_hostname:
description: The configured system hostname
returned: always
type: str
ansible_net_model:
description: The device model string
returned: always
type: str
ansible_net_serialnum:
description: The serial number of the device
returned: always
type: str
ansible_net_version:
description: The version of the software running
returned: always
type: str
ansible_net_neighbors:
description: The set of LLDP neighbors
returned: when interface is configured
type: list
ansible_net_gather_subset:
description: The list of subsets gathered by the module
returned: always
type: list
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
ansible_net_gather_network_resources:
description: The list of fact resource subsets collected from the device
returned: always
type: list
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import (
FactsArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
Facts,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def main():
"""
Main entry point for module execution
:returns: ansible_facts
"""
argument_spec = FactsArgs.argument_spec
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(
argument_spec=argument_spec, supports_check_mode=True
)
warnings = []
if module.params["gather_subset"] == "!config":
warnings.append(
"default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards"
)
result = Facts(module).get_facts()
ansible_facts, additional_warnings = result
warnings.extend(additional_warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_firewall_global.py b/plugins/modules/vyos_firewall_global.py
index 0fa8395..0574aa8 100644
--- a/plugins/modules/vyos_firewall_global.py
+++ b/plugins/modules/vyos_firewall_global.py
@@ -1,1203 +1,1202 @@
#!/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.
#
#############################################
-# pylint: skip-file
"""
The module file for vyos_firewall_global
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_firewall_global
short_description: FIREWALL global resource module
description: This module manage global policies or configurations for firewall on
VyOS devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description:
- A dictionary of Firewall global configuration options.
type: dict
suboptions:
route_redirects:
description: -A dictionary of Firewall icmp redirect and source route global
configuration options.
type: list
elements: dict
suboptions:
afi:
description:
- Specifies IP address type
type: str
choices:
- ipv4
- ipv6
required: true
icmp_redirects:
description:
- Specifies whether to allow sending/receiving of IPv4/v6 ICMP redirect
messages.
type: dict
suboptions:
send:
description:
- Permits or denies transmitting packets ICMP redirect messages.
type: bool
receive:
description:
- Permits or denies receiving packets ICMP redirect messages.
type: bool
ip_src_route:
description:
- Specifies whether or not to process source route IP options.
type: bool
ping:
description:
- Policy for handling of all IPv4 ICMP echo requests.
type: dict
suboptions:
all:
description:
- Enables or disables response to all IPv4 ICMP Echo Request (ping) messages.
- The system responds to IPv4 ICMP Echo Request messages.
type: bool
broadcast:
description:
- Enables or disables response to broadcast IPv4 ICMP Echo Request and
Timestamp Request messages.
- IPv4 ICMP Echo and Timestamp Request messages are not processed.
type: bool
config_trap:
description:
- SNMP trap generation on firewall configuration changes.
type: bool
validation:
description:
- Specifies a policy for source validation by reversed path, as defined in
RFC 3704.
- (disable) No source validation is performed.
- (loose) Enable Loose Reverse Path Forwarding as defined in RFC3704.
- (strict) Enable Strict Reverse Path Forwarding as defined in RFC3704.
type: str
choices:
- stricu
- loose
- disable
group:
description:
- Defines a group of objects for referencing in firewall rules.
type: dict
suboptions:
address_group:
description:
- Defines a group of IP addresses for referencing in firewall rules.
type: list
elements: dict
suboptions:
name:
description:
- Name of the firewall address group.
type: str
required: true
description:
description:
- Allows you to specify a brief description for the address group.
type: str
members:
description:
- Address-group members.
- IPv4 address to match.
- IPv4 range to match.
type: list
elements: dict
suboptions:
address:
description: IP address.
type: str
network_group:
description:
- Defines a group of networks for referencing in firewall rules.
type: list
elements: dict
suboptions:
name:
description:
- Name of the firewall network group.
type: str
required: true
description:
description:
- Allows you to specify a brief description for the network group.
type: str
members:
description:
- Adds an IPv4 network to the specified network group.
- The format is ip-address/prefix.
type: list
elements: dict
suboptions:
address:
description: IP address.
type: str
port_group:
description:
- Defines a group of ports for referencing in firewall rules.
type: list
elements: dict
suboptions:
name:
description:
- Name of the firewall port group.
type: str
required: true
description:
description:
- Allows you to specify a brief description for the port group.
type: str
members:
description:
- Port-group member.
type: list
elements: dict
suboptions:
port:
description: Defines the number.
type: str
log_martians:
description:
- Specifies whether or not to record packets with invalid addresses in the
log.
- (True) Logs packets with invalid addresses.
- (False) Does not log packets with invalid addresses.
type: bool
syn_cookies:
description:
- Specifies policy for using TCP SYN cookies with IPv4.
- (True) Enables TCP SYN cookies with IPv4.
- (False) Disables TCP SYN cookies with IPv4.
type: bool
twa_hazards_protection:
description:
- RFC1337 TCP TIME-WAIT assasination hazards protection.
type: bool
state_policy:
description:
- Specifies global firewall state-policy.
type: list
elements: dict
suboptions:
connection_type:
description: Specifies connection type.
type: str
choices:
- established
- invalid
- related
action:
description:
- Action for packets part of an established connection.
type: str
choices:
- accept
- drop
- reject
log:
description:
- Enable logging of packets part of an established connection.
type: bool
running_config:
description:
- The module, by default, will connect to the remote device and retrieve the current
running-config to use as a base for comparing against the contents of source.
There are times when it is not desirable to have the task get the current running-config
for every task in a playbook. The I(running_config) argument allows the implementer
to pass in the configuration to use as the base config for comparison. This
value of this option should be the output received from device by executing
command C(show configuration commands | grep 'firewall')
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos# run show configuration commands | grep firewall
#
#
- name: Merge the provided configuration with the exisiting running configuration
vyos.vyos.vyos_firewall_global:
config:
validation: strict
config_trap: true
log_martians: true
syn_cookies: true
twa_hazards_protection: true
ping:
all: true
broadcast: true
state_policy:
- connection_type: established
action: accept
log: true
- connection_type: invalid
action: reject
route_redirects:
- afi: ipv4
ip_src_route: true
icmp_redirects:
send: true
receive: false
group:
address_group:
- name: MGMT-HOSTS
description: This group has the Management hosts address list
members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
network_group:
- name: MGMT
description: This group has the Management network addresses
members:
- address: 192.0.1.0/24
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": []
#
# "commands": [
# "set firewall group address-group MGMT-HOSTS address 192.0.1.1",
# "set firewall group address-group MGMT-HOSTS address 192.0.1.3",
# "set firewall group address-group MGMT-HOSTS address 192.0.1.5",
# "set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'",
# "set firewall group address-group MGMT-HOSTS",
# "set firewall group network-group MGMT network 192.0.1.0/24",
# "set firewall group network-group MGMT description 'This group has the Management network addresses'",
# "set firewall group network-group MGMT",
# "set firewall ip-src-route 'enable'",
# "set firewall receive-redirects 'disable'",
# "set firewall send-redirects 'enable'",
# "set firewall config-trap 'enable'",
# "set firewall state-policy established action 'accept'",
# "set firewall state-policy established log 'enable'",
# "set firewall state-policy invalid action 'reject'",
# "set firewall broadcast-ping 'enable'",
# "set firewall all-ping 'enable'",
# "set firewall log-martians 'enable'",
# "set firewall twa-hazards-protection 'enable'",
# "set firewall syn-cookies 'enable'",
# "set firewall source-validation 'strict'"
# ]
#
# "after": {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "This group has the Management hosts address list",
# "members": [
# {
# "address": "192.0.1.1"
# },
# {
# "address": "192.0.1.3"
# },
# {
# "address": "192.0.1.5"
# }
# ],
# "name": "MGMT-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group MGMT-HOSTS address '192.0.1.1'
# set firewall group address-group MGMT-HOSTS address '192.0.1.3'
# set firewall group address-group MGMT-HOSTS address '192.0.1.5'
# set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
#
#
# Using parsed
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_firewall_global:
running_config:
"set firewall all-ping 'enable'
set firewall broadcast-ping 'enable'
set firewall config-trap 'enable'
set firewall group address-group ENG-HOSTS address '192.0.3.1'
set firewall group address-group ENG-HOSTS address '192.0.3.2'
set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'
set firewall group address-group SALES-HOSTS address '192.0.2.1'
set firewall group address-group SALES-HOSTS address '192.0.2.2'
set firewall group address-group SALES-HOSTS address '192.0.2.3'
set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'
set firewall group network-group MGMT description 'This group has the Management network addresses'
set firewall group network-group MGMT network '192.0.1.0/24'
set firewall ip-src-route 'enable'
set firewall log-martians 'enable'
set firewall receive-redirects 'disable'
set firewall send-redirects 'enable'
set firewall source-validation 'strict'
set firewall state-policy established action 'accept'
set firewall state-policy established log 'enable'
set firewall state-policy invalid action 'reject'
set firewall syn-cookies 'enable'
set firewall twa-hazards-protection 'enable'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.3.1"
# },
# {
# "address": "192.0.3.2"
# }
# ],
# "name": "ENG-HOSTS"
# },
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.2.1"
# },
# {
# "address": "192.0.2.2"
# },
# {
# "address": "192.0.2.3"
# }
# ],
# "name": "SALES-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
# }
#
#
# Using deleted
#
# Before state
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group MGMT-HOSTS address '192.0.1.1'
# set firewall group address-group MGMT-HOSTS address '192.0.1.3'
# set firewall group address-group MGMT-HOSTS address '192.0.1.5'
# set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
- name: Delete attributes of firewall.
vyos.vyos.vyos_firewall_global:
config:
state_policy:
config_trap:
log_martians:
syn_cookies:
twa_hazards_protection:
route_redirects:
ping:
group:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "This group has the Management hosts address list",
# "members": [
# {
# "address": "192.0.1.1"
# },
# {
# "address": "192.0.1.3"
# },
# {
# "address": "192.0.1.5"
# }
# ],
# "name": "MGMT-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
# "commands": [
# "delete firewall source-validation",
# "delete firewall group",
# "delete firewall log-martians",
# "delete firewall ip-src-route",
# "delete firewall receive-redirects",
# "delete firewall send-redirects",
# "delete firewall config-trap",
# "delete firewall state-policy",
# "delete firewall syn-cookies",
# "delete firewall broadcast-ping",
# "delete firewall all-ping",
# "delete firewall twa-hazards-protection"
# ]
#
# "after": []
# After state
# ------------
# vyos@192# run show configuration commands | grep firewall
# set 'firewall'
#
#
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group MGMT-HOSTS address '192.0.1.1'
# set firewall group address-group MGMT-HOSTS address '192.0.1.3'
# set firewall group address-group MGMT-HOSTS address '192.0.1.5'
# set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
#
- name: Replace firewall global attributes configuration.
vyos.vyos.vyos_firewall_global:
config:
validation: strict
config_trap: true
log_martians: true
syn_cookies: true
twa_hazards_protection: true
ping:
all: true
broadcast: true
state_policy:
- connection_type: established
action: accept
log: true
- connection_type: invalid
action: reject
route_redirects:
- afi: ipv4
ip_src_route: true
icmp_redirects:
send: true
receive: false
group:
address_group:
- name: SALES-HOSTS
description: Sales office hosts address list
members:
- address: 192.0.2.1
- address: 192.0.2.2
- address: 192.0.2.3
- name: ENG-HOSTS
description: Sales office hosts address list
members:
- address: 192.0.3.1
- address: 192.0.3.2
network_group:
- name: MGMT
description: This group has the Management network addresses
members:
- address: 192.0.1.0/24
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "This group has the Management hosts address list",
# "members": [
# {
# "address": "192.0.1.1"
# },
# {
# "address": "192.0.1.3"
# },
# {
# "address": "192.0.1.5"
# }
# ],
# "name": "MGMT-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
#
# "commands": [
# "delete firewall group address-group MGMT-HOSTS",
# "set firewall group address-group SALES-HOSTS address 192.0.2.1",
# "set firewall group address-group SALES-HOSTS address 192.0.2.2",
# "set firewall group address-group SALES-HOSTS address 192.0.2.3",
# "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'",
# "set firewall group address-group SALES-HOSTS",
# "set firewall group address-group ENG-HOSTS address 192.0.3.1",
# "set firewall group address-group ENG-HOSTS address 192.0.3.2",
# "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'",
# "set firewall group address-group ENG-HOSTS"
# ]
#
# "after": {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.3.1"
# },
# {
# "address": "192.0.3.2"
# }
# ],
# "name": "ENG-HOSTS"
# },
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.2.1"
# },
# {
# "address": "192.0.2.2"
# },
# {
# "address": "192.0.2.3"
# }
# ],
# "name": "SALES-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group ENG-HOSTS address '192.0.3.1'
# set firewall group address-group ENG-HOSTS address '192.0.3.2'
# set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'
# set firewall group address-group SALES-HOSTS address '192.0.2.1'
# set firewall group address-group SALES-HOSTS address '192.0.2.2'
# set firewall group address-group SALES-HOSTS address '192.0.2.3'
# set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
#
#
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group ENG-HOSTS address '192.0.3.1'
# set firewall group address-group ENG-HOSTS address '192.0.3.2'
# set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'
# set firewall group address-group SALES-HOSTS address '192.0.2.1'
# set firewall group address-group SALES-HOSTS address '192.0.2.2'
# set firewall group address-group SALES-HOSTS address '192.0.2.3'
# set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
#
- name: Gather firewall global config with provided configurations
vyos.vyos.vyos_firewall_global:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.3.1"
# },
# {
# "address": "192.0.3.2"
# }
# ],
# "name": "ENG-HOSTS"
# },
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.2.1"
# },
# {
# "address": "192.0.2.2"
# },
# {
# "address": "192.0.2.3"
# }
# ],
# "name": "SALES-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall all-ping 'enable'
# set firewall broadcast-ping 'enable'
# set firewall config-trap 'enable'
# set firewall group address-group ENG-HOSTS address '192.0.3.1'
# set firewall group address-group ENG-HOSTS address '192.0.3.2'
# set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'
# set firewall group address-group SALES-HOSTS address '192.0.2.1'
# set firewall group address-group SALES-HOSTS address '192.0.2.2'
# set firewall group address-group SALES-HOSTS address '192.0.2.3'
# set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'
# set firewall group network-group MGMT description 'This group has the Management network addresses'
# set firewall group network-group MGMT network '192.0.1.0/24'
# set firewall ip-src-route 'enable'
# set firewall log-martians 'enable'
# set firewall receive-redirects 'disable'
# set firewall send-redirects 'enable'
# set firewall source-validation 'strict'
# set firewall state-policy established action 'accept'
# set firewall state-policy established log 'enable'
# set firewall state-policy invalid action 'reject'
# set firewall syn-cookies 'enable'
# set firewall twa-hazards-protection 'enable'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_firewall_global:
config:
validation: strict
config_trap: true
log_martians: true
syn_cookies: true
twa_hazards_protection: true
ping:
all: true
broadcast: true
state_policy:
- connection_type: established
action: accept
log: true
- connection_type: invalid
action: reject
route_redirects:
- afi: ipv4
ip_src_route: true
icmp_redirects:
send: true
receive: false
group:
address_group:
- name: SALES-HOSTS
description: Sales office hosts address list
members:
- address: 192.0.2.1
- address: 192.0.2.2
- address: 192.0.2.3
- name: ENG-HOSTS
description: Sales office hosts address list
members:
- address: 192.0.3.1
- address: 192.0.3.2
network_group:
- name: MGMT
description: This group has the Management network addresses
members:
- address: 192.0.1.0/24
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set firewall group address-group SALES-HOSTS address 192.0.2.1",
# "set firewall group address-group SALES-HOSTS address 192.0.2.2",
# "set firewall group address-group SALES-HOSTS address 192.0.2.3",
# "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'",
# "set firewall group address-group SALES-HOSTS",
# "set firewall group address-group ENG-HOSTS address 192.0.3.1",
# "set firewall group address-group ENG-HOSTS address 192.0.3.2",
# "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'",
# "set firewall group address-group ENG-HOSTS",
# "set firewall group network-group MGMT network 192.0.1.0/24",
# "set firewall group network-group MGMT description 'This group has the Management network addresses'",
# "set firewall group network-group MGMT",
# "set firewall ip-src-route 'enable'",
# "set firewall receive-redirects 'disable'",
# "set firewall send-redirects 'enable'",
# "set firewall config-trap 'enable'",
# "set firewall state-policy established action 'accept'",
# "set firewall state-policy established log 'enable'",
# "set firewall state-policy invalid action 'reject'",
# "set firewall broadcast-ping 'enable'",
# "set firewall all-ping 'enable'",
# "set firewall log-martians 'enable'",
# "set firewall twa-hazards-protection 'enable'",
# "set firewall syn-cookies 'enable'",
# "set firewall source-validation 'strict'"
# ]
#
#
"""
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: ['set firewall group address-group ENG-HOSTS',
'set firewall group address-group ENG-HOSTS address 192.0.3.1']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import (
Firewall_globalArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_global.firewall_global import (
Firewall_global,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Firewall_globalArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Firewall_global(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_firewall_interfaces.py b/plugins/modules/vyos_firewall_interfaces.py
index b702cea..7ca936d 100644
--- a/plugins/modules/vyos_firewall_interfaces.py
+++ b/plugins/modules/vyos_firewall_interfaces.py
@@ -1,1289 +1,1288 @@
#!/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.
#
#############################################
-# pylint: skip-file
"""
The module file for vyos_firewall_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_firewall_interfaces
short_description: FIREWALL interfaces resource module
description: Manage firewall rules of interfaces on VyOS network devices.
version_added: 1.0.0
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: A list of firewall rules options for interfaces.
type: list
elements: dict
suboptions:
name:
description:
- Name/Identifier for the interface.
type: str
required: true
access_rules:
description:
- Specifies firewall rules attached to the interfaces.
type: list
elements: dict
suboptions:
afi:
description:
- Specifies the AFI for the Firewall rules to be configured on this interface.
type: str
choices:
- ipv4
- ipv6
required: true
rules:
description:
- Specifies the firewall rules for the provided AFI.
type: list
elements: dict
suboptions:
name:
description:
- Specifies the name of the IPv4/IPv6 Firewall rule for the interface.
type: str
direction:
description:
- Specifies the direction of packets that the firewall rule will be
applied on.
type: str
choices:
- in
- local
- out
required: true
running_config:
description:
- The module, by default, will connect to the remote device and retrieve the current
running-config to use as a base for comparing against the contents of source.
There are times when it is not desirable to have the task get the current running-config
for every task in a playbook. The I(running_config) argument allows the implementer
to pass in the configuration to use as the base config for comparison. This
value of this option should be the output received from device by executing
command C(show configuration commands | grep 'firewall'
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- parsed
- rendered
- gathered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
#
- name: Merge the provided configuration with the existing running configuration
vyos.vyos.vyos_firewall_interfaces:
config:
- access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: in
- name: OUTBOUND
direction: out
- name: LOCAL
direction: local
- afi: ipv6
rules:
- name: V6-LOCAL
direction: local
name: eth1
- access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: in
- name: OUTBOUND
direction: out
- name: LOCAL
direction: local
- afi: ipv6
rules:
- name: V6-LOCAL
direction: local
name: eth3
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": [
# {
# "name": "eth0"
# },
# {
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "name": "eth3"
# }
# ]
#
# "commands": [
# "set interfaces ethernet eth1 firewall in name 'INBOUND'",
# "set interfaces ethernet eth1 firewall out name 'OUTBOUND'",
# "set interfaces ethernet eth1 firewall local name 'LOCAL'",
# "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'",
# "set interfaces ethernet eth3 firewall in name 'INBOUND'",
# "set interfaces ethernet eth3 firewall out name 'OUTBOUND'",
# "set interfaces ethernet eth3 firewall local name 'LOCAL'",
# "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'"
# ]
#
# "after": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
#
- name: Merge the provided configuration with the existing running configuration
vyos.vyos.vyos_firewall_interfaces:
config:
- access_rules:
- afi: ipv4
rules:
- name: OUTBOUND
direction: in
- name: INBOUND
direction: out
name: eth1
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# "commands": [
# "set interfaces ethernet eth1 firewall in name 'OUTBOUND'",
# "set interfaces ethernet eth1 firewall out name 'INBOUND'"
# ]
#
# "after": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "OUTBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "INBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'OUTBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'INBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
#
- name: Replace device configurations of listed firewall interfaces with provided
configurations
vyos.vyos.vyos_firewall_interfaces:
config:
- name: eth1
access_rules:
- afi: ipv4
rules:
- name: OUTBOUND
direction: out
- afi: ipv6
rules:
- name: V6-LOCAL
direction: local
- name: eth3
access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: in
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# "commands": [
# "delete interfaces ethernet eth1 firewall in name",
# "delete interfaces ethernet eth1 firewall local name",
# "delete interfaces ethernet eth3 firewall local name",
# "delete interfaces ethernet eth3 firewall out name",
# "delete interfaces ethernet eth3 firewall local ipv6-name"
# ]
#
# "after": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall 'in'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall 'local'
# set interfaces ethernet eth3 firewall 'out'
# Using overridden
#
# Before state
# --------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall 'in'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall 'local'
# set interfaces ethernet eth3 firewall 'out'
#
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_firewall_interfaces:
config:
- name: eth3
access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: out
state: overridden
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before":[
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
# "commands": [
# "delete interfaces ethernet eth1 firewall",
# "delete interfaces ethernet eth3 firewall in name",
# "set interfaces ethernet eth3 firewall out name 'INBOUND'"
#
#
# "after": [
# {
# "name": "eth0"
# },
# {
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "out",
# "name": "INBOUND"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
#
# After state
# ------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth3 firewall 'in'
# set interfaces ethernet eth3 firewall 'local'
# set interfaces ethernet eth3 firewall out name 'INBOUND'
# Using deleted per interface name
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
#
- name: Delete firewall interfaces based on interface name.
vyos.vyos.vyos_firewall_interfaces:
config:
- name: eth1
- name: eth3
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
# "commands": [
# "delete interfaces ethernet eth1 firewall",
# "delete interfaces ethernet eth3 firewall"
# ]
#
# "after": [
# {
# "name": "eth0"
# },
# {
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "name": "eth3"
# }
# ]
# After state
# ------------
# vyos@vyos# run show configuration commands | grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# Using deleted per afi
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
#
- name: Delete firewall interfaces config per afi.
vyos.vyos.vyos_firewall_interfaces:
config:
- name: eth1
access_rules:
- afi: ipv4
- afi: ipv6
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "commands": [
# "delete interfaces ethernet eth1 firewall in name",
# "delete interfaces ethernet eth1 firewall out name",
# "delete interfaces ethernet eth1 firewall local name",
# "delete interfaces ethernet eth1 firewall local ipv6-name"
# ]
#
# After state
# ------------
# vyos@vyos# run show configuration commands | grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# Using deleted without config
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall in name 'INBOUND'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall local name 'LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth3 firewall local name 'LOCAL'
# set interfaces ethernet eth3 firewall out name 'OUTBOUND'
#
- name: Delete firewall interfaces config when empty config provided.
vyos.vyos.vyos_firewall_interfaces:
config:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "commands": [
# "delete interfaces ethernet eth1 firewall",
# "delete interfaces ethernet eth1 firewall"
# ]
#
# After state
# ------------
# vyos@vyos# run show configuration commands | grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# Using parsed
#
#
- name: Parse the provided configuration
vyos.vyos.vyos_firewall_interfaces:
running_config:
"set interfaces ethernet eth1 firewall in name 'INBOUND'
set interfaces ethernet eth1 firewall out name 'OUTBOUND'
set interfaces ethernet eth1 firewall local name 'LOCAL'
set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth2 firewall in name 'INBOUND'
set interfaces ethernet eth2 firewall out name 'OUTBOUND'
set interfaces ethernet eth2 firewall local name 'LOCAL'
set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# },
# {
# "direction": "local",
# "name": "LOCAL"
# },
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth2"
# },
# {
# "name": "eth3"
# }
# ]
# Using gathered
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall 'in'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall 'local'
# set interfaces ethernet eth3 firewall 'out'
#
- name: Gather listed firewall interfaces.
vyos.vyos.vyos_firewall_interfaces:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "name": "eth0"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "out",
# "name": "OUTBOUND"
# }
# ]
# },
# {
# "afi": "ipv6",
# "rules": [
# {
# "direction": "local",
# "name": "V6-LOCAL"
# }
# ]
# }
# ],
# "name": "eth1"
# },
# {
# "name": "eth2"
# },
# {
# "access_rules": [
# {
# "afi": "ipv4",
# "rules": [
# {
# "direction": "in",
# "name": "INBOUND"
# }
# ]
# }
# ],
# "name": "eth3"
# }
# ]
#
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name 'V6-LOCAL'
# set firewall name 'INBOUND'
# set firewall name 'LOCAL'
# set firewall name 'OUTBOUND'
# set interfaces ethernet eth1 firewall 'in'
# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
# set interfaces ethernet eth1 firewall out name 'OUTBOUND'
# set interfaces ethernet eth3 firewall in name 'INBOUND'
# set interfaces ethernet eth3 firewall 'local'
# set interfaces ethernet eth3 firewall 'out'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_firewall_interfaces:
config:
- name: eth2
access_rules:
- afi: ipv4
rules:
- direction: in
name: INGRESS
- direction: out
name: OUTGRESS
- direction: local
name: DROP
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set interfaces ethernet eth2 firewall in name 'INGRESS'",
# "set interfaces ethernet eth2 firewall out name 'OUTGRESS'",
# "set interfaces ethernet eth2 firewall local name 'DROP'",
# "set interfaces ethernet eth2 firewall local ipv6-name 'LOCAL'"
# ]
"""
RETURN = """
before:
description: The configuration prior to the 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:
- "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'"
- "set interfaces ethernet eth3 firewall in name 'INBOUND'"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import (
Firewall_interfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_interfaces.firewall_interfaces import (
Firewall_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Firewall_interfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Firewall_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py
index 9420dca..3bd6451 100644
--- a/plugins/modules/vyos_firewall_rules.py
+++ b/plugins/modules/vyos_firewall_rules.py
@@ -1,1515 +1,1514 @@
#!/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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_firewall_rules
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_firewall_rules
short_description: FIREWALL rules resource module
description: This module manages firewall rule-set attributes on VyOS devices
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: A dictionary of Firewall rule-set options.
type: list
elements: dict
suboptions:
afi:
description:
- Specifies the type of rule-set.
type: str
choices:
- ipv4
- ipv6
required: true
rule_sets:
description:
- The Firewall rule-set list.
type: list
elements: dict
suboptions:
name:
description:
- Firewall rule set name.
type: str
default_action:
description:
- Default action for rule-set.
- drop (Drop if no prior rules are hit (default))
- reject (Drop and notify source if no prior rules are hit)
- accept (Accept if no prior rules are hit)
type: str
choices:
- drop
- reject
- accept
description:
description:
- Rule set description.
type: str
enable_default_log:
description:
- Option to log packets hitting default-action.
type: bool
rules:
description:
- A ditionary that specifies the rule-set configurations.
type: list
elements: dict
suboptions:
number:
description:
- Rule number.
type: int
required: true
description:
description:
- Description of this rule.
type: str
action:
description:
- Specifying the action.
type: str
choices:
- drop
- reject
- accept
- inspect
destination:
description:
- Specifying the destination parameters.
type: dict
suboptions:
address:
description:
- Destination ip address subnet or range.
- IPv4/6 address, subnet or range to match.
- Match everything except the specified address, subnet or range.
- Destination ip address subnet or range.
type: str
group:
description:
- Destination group.
type: dict
suboptions:
address_group:
description:
- Group of addresses.
type: str
network_group:
description:
- Group of networks.
type: str
port_group:
description:
- Group of ports.
type: str
port:
description:
- Multiple destination ports can be specified as a comma-separated
list.
- The whole list can also be "negated" using '!'.
- For example:'!22,telnet,http,123,1001-1005'.
type: str
disabled:
description:
- Option to disable firewall rule.
type: bool
fragment:
description:
- IP fragment match.
type: str
choices:
- match-frag
- match-non-frag
icmp:
description:
- ICMP type and code information.
type: dict
suboptions:
type_name:
description:
- ICMP type-name.
type: str
choices:
- any
- echo-reply
- destination-unreachable
- network-unreachable
- host-unreachable
- protocol-unreachable
- port-unreachable
- fragmentation-needed
- source-route-failed
- network-unknown
- host-unknown
- network-prohibited
- host-prohibited
- TOS-network-unreachable
- TOS-host-unreachable
- communication-prohibited
- host-precedence-violation
- precedence-cutoff
- source-quench
- redirect
- network-redirect
- host-redirect
- TOS-network-redirect
- TOS-host-redirect
- echo-request
- router-advertisement
- router-solicitation
- time-exceeded
- ttl-zero-during-transit
- ttl-zero-during-reassembly
- parameter-problem
- ip-header-bad
- required-option-missing
- timestamp-request
- timestamp-reply
- address-mask-request
- address-mask-reply
- ping
- pong
- ttl-exceeded
code:
description:
- ICMP code.
type: int
type:
description:
- ICMP type.
type: int
ipsec:
description:
- Inboud ip sec packets.
type: str
choices:
- match-ipsec
- match-none
limit:
description:
- Rate limit using a token bucket filter.
type: dict
suboptions:
burst:
description:
- Maximum number of packets to allow in excess of rate.
type: int
rate:
description:
- format for rate (integer/time unit).
- any one of second, minute, hour or day may be used to specify
time unit.
- eg. 1/second implies rule to be matched at an average of once
per second.
type: dict
suboptions:
number:
description:
- This is the integer value.
type: int
unit:
description:
- This is the time unit.
type: str
p2p:
description:
- P2P application packets.
type: list
elements: dict
suboptions:
application:
description:
- Name of the application.
type: str
choices:
- all
- applejuice
- bittorrent
- directconnect
- edonkey
- gnutella
- kazaa
protocol:
description:
- Protocol to match (protocol name in /etc/protocols or protocol number
or all).
- IP protocol name from /etc/protocols (e.g. "tcp" or "udp").
- <0-255> IP protocol number.
- tcp_udp Both TCP and UDP.
- all All IP protocols.
- (!)All IP protocols except for the specified name or number.
type: str
recent:
description:
- Parameters for matching recently seen sources.
type: dict
suboptions:
count:
description:
- Source addresses seen more than N times.
type: int
time:
description:
- Source addresses seen in the last N seconds.
type: int
source:
description:
- Source parameters.
type: dict
suboptions:
address:
description:
- Source ip address subnet or range.
- IPv4/6 address, subnet or range to match.
- Match everything except the specified address, subnet or range.
- Source ip address subnet or range.
type: str
group:
description:
- Source group.
type: dict
suboptions:
address_group:
description:
- Group of addresses.
type: str
network_group:
description:
- Group of networks.
type: str
port_group:
description:
- Group of ports.
type: str
port:
description:
- Multiple source ports can be specified as a comma-separated
list.
- The whole list can also be "negated" using '!'.
- For example:'!22,telnet,http,123,1001-1005'.
type: str
mac_address:
description:
- MAC address to match.
- Match everything except the specified MAC address.
type: str
state:
description:
- Session state.
type: dict
suboptions:
established:
description:
- Established state.
type: bool
invalid:
description:
- Invalid state.
type: bool
new:
description:
- New state.
type: bool
related:
description:
- Related state.
type: bool
tcp:
description:
- TCP flags to match.
type: dict
suboptions:
flags:
description:
- TCP flags to be matched.
type: str
time:
description:
- Time to match rule.
type: dict
suboptions:
utc:
description:
- Interpret times for startdate, stopdate, starttime and stoptime
to be UTC.
type: bool
monthdays:
description:
- Monthdays to match rule on.
type: str
startdate:
description:
- Date to start matching rule.
type: str
starttime:
description:
- Time of day to start matching rule.
type: str
stopdate:
description:
- Date to stop matching rule.
type: str
stoptime:
description:
- Time of day to stop matching rule.
type: str
weekdays:
description:
- Weekdays to match rule on.
type: str
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep firewall).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state the configuration should be left in
type: str
choices:
- merged
- replaced
- overridden
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
# Using deleted to delete firewall rules based on rule-set name
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall name Downlink default-action 'accept'
# set firewall name Downlink description 'IPv4 INBOUND rule set'
# set firewall name Downlink rule 501 action 'accept'
# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'
# set firewall name Downlink rule 501 ipsec 'match-ipsec'
# set firewall name Downlink rule 502 action 'reject'
# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'
# set firewall name Downlink rule 502 ipsec 'match-ipsec'
#
- name: Delete attributes of given firewall rules.
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv4
rule_sets:
- name: Downlink
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "Downlink",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 501 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 501
# },
# {
# "action": "reject",
# "description": "Rule 502 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 502
# }
# ]
# }
# ]
# }
# ]
# "commands": [
# "delete firewall name Downlink"
# ]
#
# "after": []
# After state
# ------------
# vyos@vyos# run show configuration commands | grep firewall
# set firewall group address-group 'inbound'
# Using deleted to delete firewall rules based on afi
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# set firewall group address-group 'inbound'
# set firewall name Downlink default-action 'accept'
# set firewall name Downlink description 'IPv4 INBOUND rule set'
# set firewall name Downlink rule 501 action 'accept'
# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'
# set firewall name Downlink rule 501 ipsec 'match-ipsec'
# set firewall name Downlink rule 502 action 'reject'
# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'
# set firewall name Downlink rule 502 ipsec 'match-ipsec'
#
- name: Delete attributes of given firewall rules.
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv4
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK",
# "rules": [
# {
# "action": "accept",
# "description": "Fwipv6-Rule 1 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 1
# },
# {
# "action": "accept",
# "description": "Fwipv6-Rule 2 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 2
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "Downlink",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 501 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 501
# },
# {
# "action": "reject",
# "description": "Rule 502 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 502
# }
# ]
# }
# ]
# }
# ]
# "commands": [
# "delete firewall name"
# ]
#
# "after": []
# After state
# ------------
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# Using deleted to delete all the the firewall rules when provided config is empty
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall name Downlink default-action 'accept'
# set firewall name Downlink description 'IPv4 INBOUND rule set'
# set firewall name Downlink rule 501 action 'accept'
# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'
# set firewall name Downlink rule 501 ipsec 'match-ipsec'
# set firewall name Downlink rule 502 action 'reject'
# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'
# set firewall name Downlink rule 502 ipsec 'match-ipsec'
#
- name: Delete attributes of given firewall rules.
vyos.vyos.vyos_firewall_rules:
config:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "Downlink",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 501 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 501
# },
# {
# "action": "reject",
# "description": "Rule 502 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 502
# }
# ]
# }
# ]
# }
# ]
# "commands": [
# "delete firewall name"
# ]
#
# "after": []
# After state
# ------------
# vyos@vyos# run show configuration commands | grep firewall
# set firewall group address-group 'inbound'
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos# run show configuration commands | grep firewall
# set firewall group address-group 'inbound'
#
- name: Merge the provided configuration with the exisiting running configuration
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv6
rule_sets:
- name: UPLINK
description: This is ipv6 specific rule-set
default_action: accept
rules:
- number: 1
action: accept
description: Fwipv6-Rule 1 is configured by Ansible
ipsec: match-ipsec
- number: 2
action: accept
description: Fwipv6-Rule 2 is configured by Ansible
ipsec: match-ipsec
- afi: ipv4
rule_sets:
- name: INBOUND
description: IPv4 INBOUND rule set
default_action: accept
rules:
- number: 101
action: accept
description: Rule 101 is configured by Ansible
ipsec: match-ipsec
- number: 102
action: reject
description: Rule 102 is configured by Ansible
ipsec: match-ipsec
- number: 103
action: accept
description: Rule 103 is configured by Ansible
destination:
group:
address_group: inbound
source:
address: 192.0.2.0
state:
established: true
new: false
invalid: false
related: true
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": []
#
# "commands": [
# "set firewall ipv6-name UPLINK default-action 'accept'",
# "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'",
# "set firewall ipv6-name UPLINK rule 1 action 'accept'",
# "set firewall ipv6-name UPLINK rule 1",
# "set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'",
# "set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'",
# "set firewall ipv6-name UPLINK rule 2 action 'accept'",
# "set firewall ipv6-name UPLINK rule 2",
# "set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'",
# "set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'",
# "set firewall name INBOUND default-action 'accept'",
# "set firewall name INBOUND description 'IPv4 INBOUND rule set'",
# "set firewall name INBOUND rule 101 action 'accept'",
# "set firewall name INBOUND rule 101",
# "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'",
# "set firewall name INBOUND rule 101 ipsec 'match-ipsec'",
# "set firewall name INBOUND rule 102 action 'reject'",
# "set firewall name INBOUND rule 102",
# "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'",
# "set firewall name INBOUND rule 102 ipsec 'match-ipsec'",
# "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'",
# "set firewall name INBOUND rule 103 destination group address-group inbound",
# "set firewall name INBOUND rule 103",
# "set firewall name INBOUND rule 103 source address 192.0.2.0",
# "set firewall name INBOUND rule 103 state established enable",
# "set firewall name INBOUND rule 103 state related enable",
# "set firewall name INBOUND rule 103 state invalid disable",
# "set firewall name INBOUND rule 103 state new disable",
# "set firewall name INBOUND rule 103 action 'accept'"
# ]
#
# "after": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK",
# "rules": [
# {
# "action": "accept",
# "description": "Fwipv6-Rule 1 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 1
# },
# {
# "action": "accept",
# "description": "Fwipv6-Rule 2 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 2
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 102 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 102
# },
# {
# "action": "accept",
# "description": "Rule 103 is configured by Ansible",
# "destination": {
# "group": {
# "address_group": "inbound"
# }
# },
# "number": 103,
# "source": {
# "address": "192.0.2.0"
# },
# "state": {
# "established": true,
# "invalid": false,
# "new": false,
# "related": true
# }
# }
# ]
# }
# ]
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 102 action 'reject'
# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'
# set firewall name INBOUND rule 102 ipsec 'match-ipsec'
# set firewall name INBOUND rule 103 action 'accept'
# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'
# set firewall name INBOUND rule 103 destination group address-group 'inbound'
# set firewall name INBOUND rule 103 source address '192.0.2.0'
# set firewall name INBOUND rule 103 state established 'enable'
# set firewall name INBOUND rule 103 state invalid 'disable'
# set firewall name INBOUND rule 103 state new 'disable'
# set firewall name INBOUND rule 103 state related 'enable'
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 102 action 'reject'
# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'
# set firewall name INBOUND rule 102 ipsec 'match-ipsec'
# set firewall name INBOUND rule 103 action 'accept'
# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'
# set firewall name INBOUND rule 103 destination group address-group 'inbound'
# set firewall name INBOUND rule 103 source address '192.0.2.0'
# set firewall name INBOUND rule 103 state established 'enable'
# set firewall name INBOUND rule 103 state invalid 'disable'
# set firewall name INBOUND rule 103 state new 'disable'
# set firewall name INBOUND rule 103 state related 'enable'
#
- name: Replace device configurations of listed firewall rules with provided configurations
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv6
rule_sets:
- name: UPLINK
description: This is ipv6 specific rule-set
default_action: accept
- afi: ipv4
rule_sets:
- name: INBOUND
description: IPv4 INBOUND rule set
default_action: accept
rules:
- number: 101
action: accept
description: Rule 101 is configured by Ansible
ipsec: match-ipsec
- number: 104
action: reject
description: Rule 104 is configured by Ansible
ipsec: match-none
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK",
# "rules": [
# {
# "action": "accept",
# "description": "Fwipv6-Rule 1 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 1
# },
# {
# "action": "accept",
# "description": "Fwipv6-Rule 2 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 2
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 102 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 102
# },
# {
# "action": "accept",
# "description": "Rule 103 is configured by Ansible",
# "destination": {
# "group": {
# "address_group": "inbound"
# }
# },
# "number": 103,
# "source": {
# "address": "192.0.2.0"
# },
# "state": {
# "established": true,
# "invalid": false,
# "new": false,
# "related": true
# }
# }
# ]
# }
# ]
# }
# ]
#
# "commands": [
# "delete firewall ipv6-name UPLINK rule 1",
# "delete firewall ipv6-name UPLINK rule 2",
# "delete firewall name INBOUND rule 102",
# "delete firewall name INBOUND rule 103",
# "set firewall name INBOUND rule 104 action 'reject'",
# "set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible'",
# "set firewall name INBOUND rule 104",
# "set firewall name INBOUND rule 104 ipsec 'match-none'"
# ]
#
# "after": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK"
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 104 is configured by Ansible",
# "ipsec": "match-none",
# "number": 104
# }
# ]
# }
# ]
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 104 action 'reject'
# set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible'
# set firewall name INBOUND rule 104 ipsec 'match-none'
# Using overridden
#
# Before state
# --------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 104 action 'reject'
# set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible'
# set firewall name INBOUND rule 104 ipsec 'match-none'
#
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv4
rule_sets:
- name: Downlink
description: IPv4 INBOUND rule set
default_action: accept
rules:
- number: 501
action: accept
description: Rule 501 is configured by Ansible
ipsec: match-ipsec
- number: 502
action: reject
description: Rule 502 is configured by Ansible
ipsec: match-ipsec
state: overridden
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK"
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 104 is configured by Ansible",
# "ipsec": "match-none",
# "number": 104
# }
# ]
# }
# ]
# }
# ]
#
# "commands": [
# "delete firewall ipv6-name UPLINK",
# "delete firewall name INBOUND",
# "set firewall name Downlink default-action 'accept'",
# "set firewall name Downlink description 'IPv4 INBOUND rule set'",
# "set firewall name Downlink rule 501 action 'accept'",
# "set firewall name Downlink rule 501",
# "set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'",
# "set firewall name Downlink rule 501 ipsec 'match-ipsec'",
# "set firewall name Downlink rule 502 action 'reject'",
# "set firewall name Downlink rule 502",
# "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'",
# "set firewall name Downlink rule 502 ipsec 'match-ipsec'"
#
#
# "after": [
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "Downlink",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 501 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 501
# },
# {
# "action": "reject",
# "description": "Rule 502 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 502
# }
# ]
# }
# ]
# }
# ]
#
#
# After state
# ------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall name Downlink default-action 'accept'
# set firewall name Downlink description 'IPv4 INBOUND rule set'
# set firewall name Downlink rule 501 action 'accept'
# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'
# set firewall name Downlink rule 501 ipsec 'match-ipsec'
# set firewall name Downlink rule 502 action 'reject'
# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'
# set firewall name Downlink rule 502 ipsec 'match-ipsec'
# Using gathered
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 102 action 'reject'
# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'
# set firewall name INBOUND rule 102 ipsec 'match-ipsec'
# set firewall name INBOUND rule 103 action 'accept'
# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'
# set firewall name INBOUND rule 103 destination group address-group 'inbound'
# set firewall name INBOUND rule 103 source address '192.0.2.0'
# set firewall name INBOUND rule 103 state established 'enable'
# set firewall name INBOUND rule 103 state invalid 'disable'
# set firewall name INBOUND rule 103 state new 'disable'
# set firewall name INBOUND rule 103 state related 'enable'
#
- name: Gather listed firewall rules with provided configurations
vyos.vyos.vyos_firewall_rules:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK",
# "rules": [
# {
# "action": "accept",
# "description": "Fwipv6-Rule 1 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 1
# },
# {
# "action": "accept",
# "description": "Fwipv6-Rule 2 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 2
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 102 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 102
# },
# {
# "action": "accept",
# "description": "Rule 103 is configured by Ansible",
# "destination": {
# "group": {
# "address_group": "inbound"
# }
# },
# "number": 103,
# "source": {
# "address": "192.0.2.0"
# },
# "state": {
# "established": true,
# "invalid": false,
# "new": false,
# "related": true
# }
# }
# ]
# }
# ]
# }
# ]
#
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands| grep firewall
# set firewall group address-group 'inbound'
# set firewall ipv6-name UPLINK default-action 'accept'
# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'
# set firewall ipv6-name UPLINK rule 1 action 'accept'
# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'
# set firewall ipv6-name UPLINK rule 2 action 'accept'
# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'
# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'
# set firewall name INBOUND default-action 'accept'
# set firewall name INBOUND description 'IPv4 INBOUND rule set'
# set firewall name INBOUND rule 101 action 'accept'
# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'
# set firewall name INBOUND rule 101 ipsec 'match-ipsec'
# set firewall name INBOUND rule 102 action 'reject'
# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'
# set firewall name INBOUND rule 102 ipsec 'match-ipsec'
# set firewall name INBOUND rule 103 action 'accept'
# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'
# set firewall name INBOUND rule 103 destination group address-group 'inbound'
# set firewall name INBOUND rule 103 source address '192.0.2.0'
# set firewall name INBOUND rule 103 state established 'enable'
# set firewall name INBOUND rule 103 state invalid 'disable'
# set firewall name INBOUND rule 103 state new 'disable'
# set firewall name INBOUND rule 103 state related 'enable'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_firewall_rules:
config:
- afi: ipv6
rule_sets:
- name: UPLINK
description: This is ipv6 specific rule-set
default_action: accept
- afi: ipv4
rule_sets:
- name: INBOUND
description: IPv4 INBOUND rule set
default_action: accept
rules:
- number: 101
action: accept
description: Rule 101 is configured by Ansible
ipsec: match-ipsec
- number: 102
action: reject
description: Rule 102 is configured by Ansible
ipsec: match-ipsec
- number: 103
action: accept
description: Rule 103 is configured by Ansible
destination:
group:
address_group: inbound
source:
address: 192.0.2.0
state:
established: true
new: false
invalid: false
related: true
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set firewall ipv6-name UPLINK default-action 'accept'",
# "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'",
# "set firewall name INBOUND default-action 'accept'",
# "set firewall name INBOUND description 'IPv4 INBOUND rule set'",
# "set firewall name INBOUND rule 101 action 'accept'",
# "set firewall name INBOUND rule 101",
# "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'",
# "set firewall name INBOUND rule 101 ipsec 'match-ipsec'",
# "set firewall name INBOUND rule 102 action 'reject'",
# "set firewall name INBOUND rule 102",
# "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'",
# "set firewall name INBOUND rule 102 ipsec 'match-ipsec'",
# "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'",
# "set firewall name INBOUND rule 103 destination group address-group inbound",
# "set firewall name INBOUND rule 103",
# "set firewall name INBOUND rule 103 source address 192.0.2.0",
# "set firewall name INBOUND rule 103 state established enable",
# "set firewall name INBOUND rule 103 state related enable",
# "set firewall name INBOUND rule 103 state invalid disable",
# "set firewall name INBOUND rule 103 state new disable",
# "set firewall name INBOUND rule 103 action 'accept'"
# ]
# Using parsed
#
#
- name: Parsed the provided input commands.
vyos.vyos.vyos_firewall_rules:
running_config:
"set firewall group address-group 'inbound'
set firewall name Downlink default-action 'accept'
set firewall name Downlink description 'IPv4 INBOUND rule set'
set firewall name Downlink rule 501 action 'accept'
set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'
set firewall name Downlink rule 501 ipsec 'match-ipsec'
set firewall name Downlink rule 502 action 'reject'
set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'
set firewall name Downlink rule 502 ipsec 'match-ipsec'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "Downlink",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 501 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 501
# },
# {
# "action": "reject",
# "description": "Rule 502 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 502
# }
# ]
# }
# ]
# }
# ]
"""
RETURN = """
before:
description: The configuration prior to the 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:
- "set firewall name Downlink default-action 'accept'"
- "set firewall name Downlink description 'IPv4 INBOUND rule set'"
- "set firewall name Downlink rule 501 action 'accept'"
- "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'"
- "set firewall name Downlink rule 502 ipsec 'match-ipsec'"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import (
Firewall_rulesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import (
Firewall_rules,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Firewall_rulesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Firewall_rules(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_interface.py b/plugins/modules/vyos_interface.py
index 04a29f5..be844c5 100644
--- a/plugins/modules/vyos_interface.py
+++ b/plugins/modules/vyos_interface.py
@@ -1,474 +1,473 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_interface
author: Ganesh Nalawade (@ganeshrn)
short_description: (deprecated, removed after 2022-06-01) Manage Interface on VyOS
network devices
description:
- This module provides declarative management of Interfaces on VyOS network devices.
version_added: 1.0.0
deprecated:
alternative: vyos_interfaces
why: Updated modules released with more functionality.
removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the Interface.
required: true
description:
description:
- Description of Interface.
enabled:
description:
- Interface link status.
type: bool
speed:
description:
- Interface link speed.
mtu:
description:
- Maximum size of transmit packet.
duplex:
description:
- Interface link status.
default: auto
choices:
- full
- half
- auto
delay:
description:
- Time in seconds to wait before checking for the operational state on remote
device. This wait is applicable for operational state argument which are I(state)
with values C(up)/C(down) and I(neighbors).
default: 10
neighbors:
description:
- Check the operational state of given interface C(name) for LLDP neighbor.
- The following suboptions are available.
suboptions:
host:
description:
- LLDP neighbor host for given interface C(name).
port:
description:
- LLDP neighbor port to which given interface C(name) is connected.
aggregate:
description: List of Interfaces definitions.
state:
description:
- State of the Interface configuration, C(up) means present and operationally
up and C(down) means present and operationally C(down)
default: present
choices:
- present
- absent
- up
- down
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: configure interface
vyos.vyos.vyos_interface:
name: eth0
description: test-interface
- name: remove interface
vyos.vyos.vyos_interface:
name: eth0
state: absent
- name: make interface down
vyos.vyos.vyos_interface:
name: eth0
enabled: false
- name: make interface up
vyos.vyos.vyos_interface:
name: eth0
enabled: true
- name: Configure interface speed, mtu, duplex
vyos.vyos.vyos_interface:
name: eth5
state: present
speed: 100
mtu: 256
duplex: full
- name: Set interface using aggregate
vyos.vyos.vyos_interface:
aggregate:
- {name: eth1, description: test-interface-1, speed: 100, duplex: half, mtu: 512}
- {name: eth2, description: test-interface-2, speed: 1000, duplex: full, mtu: 256}
- name: Disable interface on aggregate
net_interface:
aggregate:
- name: eth1
- name: eth2
enabled: false
- name: Delete interface using aggregate
net_interface:
aggregate:
- name: eth1
- name: eth2
state: absent
- name: Check lldp neighbors intent arguments
vyos.vyos.vyos_interface:
name: eth0
neighbors:
- port: eth0
host: netdev
- name: Config + intent
vyos.vyos.vyos_interface:
name: eth1
enabled: false
state: down
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces ethernet eth0 description "test-interface"
- set interfaces ethernet eth0 speed 100
- set interfaces ethernet eth0 mtu 256
- set interfaces ethernet eth0 duplex full
"""
import re
from copy import deepcopy
from time import sleep
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import exec_command
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
conditional,
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
get_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates):
commands = list()
want, have = updates
params = ("speed", "description", "duplex", "mtu")
for w in want:
name = w["name"]
disable = w["disable"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
set_interface = "set interfaces ethernet " + name
delete_interface = "delete interfaces ethernet " + name
if state == "absent" and obj_in_have:
commands.append(delete_interface)
elif state in ("present", "up", "down"):
if obj_in_have:
for item in params:
value = w.get(item)
if value and value != obj_in_have.get(item):
if item == "description":
value = "'" + str(value) + "'"
commands.append(
set_interface + " " + item + " " + str(value)
)
if disable and not obj_in_have.get("disable", False):
commands.append(set_interface + " disable")
elif not disable and obj_in_have.get("disable", False):
commands.append(delete_interface + " disable")
else:
commands.append(set_interface)
for item in params:
value = w.get(item)
if value:
if item == "description":
value = "'" + str(value) + "'"
commands.append(
set_interface + " " + item + " " + str(value)
)
if disable:
commands.append(set_interface + " disable")
return commands
def map_config_to_obj(module):
data = get_config(module, flags=["| grep interface"])
obj = []
for line in data.split("\n"):
if line.startswith("set interfaces ethernet"):
match = re.search(r"set interfaces ethernet (\S+)", line, re.M)
name = match.group(1)
if name:
interface = {}
for item in obj:
if item["name"] == name:
interface = item
break
if not interface:
interface = {"name": name}
obj.append(interface)
match = re.search(r"%s (\S+)" % name, line, re.M)
if match:
param = match.group(1)
if param == "description":
match = re.search(r"description (.+)", line, re.M)
description = match.group(1).strip("'")
interface["description"] = description
elif param == "speed":
match = re.search(r"speed (\S+)", line, re.M)
speed = match.group(1).strip("'")
interface["speed"] = speed
elif param == "mtu":
match = re.search(r"mtu (\S+)", line, re.M)
mtu = match.group(1).strip("'")
interface["mtu"] = int(mtu)
elif param == "duplex":
match = re.search(r"duplex (\S+)", line, re.M)
duplex = match.group(1).strip("'")
interface["duplex"] = duplex
elif param.strip("'") == "disable":
interface["disable"] = True
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
d = item.copy()
if d["enabled"]:
d["disable"] = False
else:
d["disable"] = True
obj.append(d)
else:
params = {
"name": module.params["name"],
"description": module.params["description"],
"speed": module.params["speed"],
"mtu": module.params["mtu"],
"duplex": module.params["duplex"],
"delay": module.params["delay"],
"state": module.params["state"],
"neighbors": module.params["neighbors"],
}
if module.params["enabled"]:
params.update({"disable": False})
else:
params.update({"disable": True})
obj.append(params)
return obj
def check_declarative_intent_params(module, want, result):
failed_conditions = []
have_neighbors = None
for w in want:
want_state = w.get("state")
want_neighbors = w.get("neighbors")
if want_state not in ("up", "down") and not want_neighbors:
continue
if result["changed"]:
sleep(w["delay"])
command = "show interfaces ethernet %s" % w["name"]
rc, out, err = exec_command(module, command)
if rc != 0:
module.fail_json(
msg=to_text(err, errors="surrogate_then_replace"),
command=command,
rc=rc,
)
if want_state in ("up", "down"):
match = re.search(r"%s (\w+)" % "state", out, re.M)
have_state = None
if match:
have_state = match.group(1)
if have_state is None or not conditional(
want_state, have_state.strip().lower()
):
failed_conditions.append("state " + "eq(%s)" % want_state)
if want_neighbors:
have_host = []
have_port = []
if have_neighbors is None:
rc, have_neighbors, err = exec_command(
module, "show lldp neighbors detail"
)
if rc != 0:
module.fail_json(
msg=to_text(err, errors="surrogate_then_replace"),
command=command,
rc=rc,
)
if have_neighbors:
lines = have_neighbors.strip().split("Interface: ")
for line in lines:
field = line.split("\n")
if field[0].split(",")[0].strip() == w["name"]:
for item in field:
if item.strip().startswith("SysName:"):
have_host.append(item.split(":")[1].strip())
if item.strip().startswith("PortDescr:"):
have_port.append(item.split(":")[1].strip())
for item in want_neighbors:
host = item.get("host")
port = item.get("port")
if host and host not in have_host:
failed_conditions.append("host " + host)
if port and port not in have_port:
failed_conditions.append("port " + port)
return failed_conditions
def main():
""" main entry point for module execution
"""
neighbors_spec = dict(host=dict(), port=dict())
element_spec = dict(
name=dict(),
description=dict(),
speed=dict(),
mtu=dict(type="int"),
duplex=dict(choices=["full", "half", "auto"]),
enabled=dict(default=True, type="bool"),
neighbors=dict(type="list", elements="dict", options=neighbors_spec),
delay=dict(default=10, type="int"),
state=dict(
default="present", choices=["present", "absent", "up", "down"]
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
required_together = [["speed", "duplex"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
required_together=required_together,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have))
result["commands"] = commands
if commands:
commit = not module.check_mode
diff = load_config(module, commands, commit=commit)
if diff:
if module._diff:
result["diff"] = {"prepared": diff}
result["changed"] = True
failed_conditions = check_declarative_intent_params(module, want, result)
if failed_conditions:
msg = "One or more conditional statements have not been satisfied"
module.fail_json(msg=msg, failed_conditions=failed_conditions)
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_interfaces.py b/plugins/modules/vyos_interfaces.py
index 53f2c5b..47f9777 100644
--- a/plugins/modules/vyos_interfaces.py
+++ b/plugins/modules/vyos_interfaces.py
@@ -1,1133 +1,1132 @@
#!/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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_interfaces
short_description: Interfaces resource module
description:
- This module manages the interface attributes on VyOS network devices.
- This module supports managing base attributes of Ethernet, Bonding, VXLAN, Loopback
and Virtual Tunnel Interfaces.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Nilashish Chakraborty (@nilashishc)
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: The provided interfaces configuration.
type: list
suboptions:
name:
description:
- Full name of the interface, e.g. eth0, eth1, bond0, vti1, vxlan2.
type: str
required: true
description:
description:
- Interface description.
type: str
duplex:
description:
- Interface duplex mode.
- Applicable for Ethernet interfaces only.
choices:
- full
- half
- auto
type: str
enabled:
default: true
description:
- Administrative state of the interface.
- Set the value to C(true) to administratively enable the interface or C(false)
to disable it.
type: bool
mtu:
description:
- MTU for a specific interface. Refer to vendor documentation for valid values.
- Applicable for Ethernet, Bonding, VXLAN and Virtual Tunnel interfaces.
type: int
speed:
description:
- Interface link speed.
- Applicable for Ethernet interfaces only.
type: str
choices:
- auto
- '10'
- '100'
- '1000'
- '2500'
- '10000'
vifs:
description:
- Virtual sub-interfaces related configuration.
- 802.1Q VLAN interfaces are represented as virtual sub-interfaces in VyOS.
type: list
suboptions:
vlan_id:
description:
- Identifier for the virtual sub-interface.
type: int
description:
description:
- Virtual sub-interface description.
type: str
enabled:
description:
- Administrative state of the virtual sub-interface.
- Set the value to C(true) to administratively enable the interface or
C(false) to disable it.
type: bool
default: true
mtu:
description:
- MTU for the virtual sub-interface.
- Refer to vendor documentation for valid values.
type: int
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep interfaces).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- rendered
- gathered
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
#
# -------------
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces loopback lo
- name: Merge provided configuration with device configuration
vyos.vyos.vyos_interfaces:
config:
- name: eth2
description: Configured by Ansible
enabled: true
vifs:
- vlan_id: 200
description: VIF 200 - ETH2
- name: eth3
description: Configured by Ansible
mtu: 1500
- name: bond1
description: Bond - 1
mtu: 1200
- name: vti2
description: VTI - 2
enabled: false
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "enabled": true,
# "name": "eth3"
# },
# {
# "enabled": true,
# "name": "eth2"
# },
# {
# "enabled": true,
# "name": "eth1"
# },
# {
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
# "commands": [
# "set interfaces ethernet eth2 description 'Configured by Ansible'",
# "set interfaces ethernet eth2 vif 200",
# "set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'",
# "set interfaces ethernet eth3 description 'Configured by Ansible'",
# "set interfaces ethernet eth3 mtu '1500'",
# "set interfaces bonding bond1",
# "set interfaces bonding bond1 description 'Bond - 1'",
# "set interfaces bonding bond1 mtu '1200'",
# "set interfaces vti vti2",
# "set interfaces vti vti2 description 'VTI - 2'",
# "set interfaces vti vti2 disable"
# ]
#
# "after": [
# {
# "description": "Bond - 1",
# "enabled": true,
# "mtu": 1200,
# "name": "bond1"
# },
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "description": "VTI - 2",
# "enabled": false,
# "name": "vti2"
# },
# {
# "description": "Configured by Ansible",
# "enabled": true,
# "mtu": 1500,
# "name": "eth3"
# },
# {
# "description": "Configured by Ansible",
# "enabled": true,
# "name": "eth2",
# "vifs": [
# {
# "description": "VIF 200 - ETH2",
# "enabled": true,
# "vlan_id": "200"
# }
# ]
# },
# {
# "enabled": true,
# "name": "eth1"
# },
# {
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
#
# -------------
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces bonding bond1 description 'Bond - 1'
# set interfaces bonding bond1 mtu '1200'
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth2 description 'Configured by Ansible'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'
# set interfaces ethernet eth3 description 'Configured by Ansible'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 mtu '1500'
# set interfaces loopback lo
# set interfaces vti vti2 description 'VTI - 2'
# set interfaces vti vti2 disable
#
# Using replaced
#
# -------------
# Before state:
# -------------
#
# vyos:~$ show configuration commands | grep eth
# set interfaces bonding bond1 description 'Bond - 1'
# set interfaces bonding bond1 mtu '1400'
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
# set interfaces ethernet eth1 duplex 'full'
# set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65'
# set interfaces ethernet eth1 smp_affinity 'auto'
# set interfaces ethernet eth1 speed '100'
# set interfaces ethernet eth2 description 'Configured by Ansible'
# set interfaces ethernet eth2 duplex 'full'
# set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79'
# set interfaces ethernet eth2 mtu '500'
# set interfaces ethernet eth2 smp_affinity 'auto'
# set interfaces ethernet eth2 speed '100'
# set interfaces ethernet eth2 vif 200 description 'Configured by Ansible'
# set interfaces ethernet eth3 description 'Configured by Ansible'
# set interfaces ethernet eth3 duplex 'full'
# set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85'
# set interfaces ethernet eth3 mtu '1500'
# set interfaces ethernet eth3 smp_affinity 'auto'
# set interfaces ethernet eth3 speed '100'
# set interfaces loopback lo
#
#
- name: Replace device configurations of listed interfaces with provided configurations
vyos.vyos.vyos_interfaces:
config:
- name: eth2
description: Replaced by Ansible
- name: eth3
description: Replaced by Ansible
- name: eth1
description: Replaced by Ansible
state: replaced
#
#
# -----------------------
# Module Execution Result
# -----------------------
#
# "before": [
# {
# "description": "Bond - 1",
# "enabled": true,
# "mtu": 1400,
# "name": "bond1"
# },
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "description": "Configured by Ansible",
# "duplex": "full",
# "enabled": true,
# "mtu": 1500,
# "name": "eth3",
# "speed": "100"
# },
# {
# "description": "Configured by Ansible",
# "duplex": "full",
# "enabled": true,
# "mtu": 500,
# "name": "eth2",
# "speed": "100",
# "vifs": [
# {
# "description": "VIF 200 - ETH2",
# "enabled": true,
# "vlan_id": "200"
# }
# ]
# },
# {
# "description": "Configured by Ansible Eng Team",
# "duplex": "full",
# "enabled": true,
# "name": "eth1",
# "speed": "100"
# },
# {
# "description": "Management Interface for the Appliance",
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
# "commands": [
# "delete interfaces ethernet eth2 speed",
# "delete interfaces ethernet eth2 duplex",
# "delete interfaces ethernet eth2 mtu",
# "delete interfaces ethernet eth2 vif 200 description",
# "set interfaces ethernet eth2 description 'Replaced by Ansible'",
# "delete interfaces ethernet eth3 speed",
# "delete interfaces ethernet eth3 duplex",
# "delete interfaces ethernet eth3 mtu",
# "set interfaces ethernet eth3 description 'Replaced by Ansible'",
# "delete interfaces ethernet eth1 speed",
# "delete interfaces ethernet eth1 duplex",
# "set interfaces ethernet eth1 description 'Replaced by Ansible'"
# ]
#
# "after": [
# {
# "description": "Bond - 1",
# "enabled": true,
# "mtu": 1400,
# "name": "bond1"
# },
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "description": "Replaced by Ansible",
# "enabled": true,
# "name": "eth3"
# },
# {
# "description": "Replaced by Ansible",
# "enabled": true,
# "name": "eth2",
# "vifs": [
# {
# "enabled": true,
# "vlan_id": "200"
# }
# ]
# },
# {
# "description": "Replaced by Ansible",
# "enabled": true,
# "name": "eth1"
# },
# {
# "description": "Management Interface for the Appliance",
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
#
# -------------
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces bonding bond1 description 'Bond - 1'
# set interfaces bonding bond1 mtu '1400'
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Replaced by Ansible'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth2 description 'Replaced by Ansible'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth2 vif 200
# set interfaces ethernet eth3 description 'Replaced by Ansible'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces loopback lo
#
#
# Using overridden
#
#
# --------------
# Before state
# --------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 description 'Ethernet Interface - 0'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 mtu '1200'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 mtu '100'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth1 vif 100 description 'VIF 100 - ETH1'
# set interfaces ethernet eth1 vif 100 disable
# set interfaces ethernet eth2 description 'Configured by Ansible Team (Admin Down)'
# set interfaces ethernet eth2 disable
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 mtu '600'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth3 description 'Configured by Ansible Network'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces loopback lo
# set interfaces vti vti1 description 'Virtual Tunnel Interface - 1'
# set interfaces vti vti1 mtu '68'
#
#
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_interfaces:
config:
- name: eth0
description: Outbound Interface For The Appliance
speed: auto
duplex: auto
- name: eth2
speed: auto
duplex: auto
- name: eth3
mtu: 1200
state: overridden
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
# "before": [
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "description": "Virtual Tunnel Interface - 1",
# "enabled": true,
# "mtu": 68,
# "name": "vti1"
# },
# {
# "description": "Configured by Ansible Network",
# "enabled": true,
# "name": "eth3"
# },
# {
# "description": "Configured by Ansible Team (Admin Down)",
# "enabled": false,
# "mtu": 600,
# "name": "eth2"
# },
# {
# "description": "Configured by Ansible Eng Team",
# "enabled": true,
# "mtu": 100,
# "name": "eth1",
# "vifs": [
# {
# "description": "VIF 100 - ETH1",
# "enabled": false,
# "vlan_id": "100"
# }
# ]
# },
# {
# "description": "Ethernet Interface - 0",
# "duplex": "auto",
# "enabled": true,
# "mtu": 1200,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
# "commands": [
# "delete interfaces vti vti1 description",
# "delete interfaces vti vti1 mtu",
# "delete interfaces ethernet eth1 description",
# "delete interfaces ethernet eth1 mtu",
# "delete interfaces ethernet eth1 vif 100 description",
# "delete interfaces ethernet eth1 vif 100 disable",
# "delete interfaces ethernet eth0 mtu",
# "set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'",
# "delete interfaces ethernet eth2 description",
# "delete interfaces ethernet eth2 mtu",
# "set interfaces ethernet eth2 duplex 'auto'",
# "delete interfaces ethernet eth2 disable",
# "set interfaces ethernet eth2 speed 'auto'",
# "delete interfaces ethernet eth3 description",
# "set interfaces ethernet eth3 mtu '1200'"
# ],
#
# "after": [
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "enabled": true,
# "name": "vti1"
# },
# {
# "enabled": true,
# "mtu": 1200,
# "name": "eth3"
# },
# {
# "duplex": "auto",
# "enabled": true,
# "name": "eth2",
# "speed": "auto"
# },
# {
# "enabled": true,
# "name": "eth1",
# "vifs": [
# {
# "enabled": true,
# "vlan_id": "100"
# }
# ]
# },
# {
# "description": "Outbound Interface For The Appliance",
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
#
# ------------
# After state
# ------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth1 vif 100
# set interfaces ethernet eth2 duplex 'auto'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth2 speed 'auto'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 mtu '1200'
# set interfaces loopback lo
# set interfaces vti vti1
#
#
# Using deleted
#
#
# -------------
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces bonding bond0 mtu '1300'
# set interfaces bonding bond1 description 'LAG - 1'
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Configured by Ansible Network'
# set interfaces ethernet eth1 duplex 'full'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth1 speed '100'
# set interfaces ethernet eth2 description 'Configured by Ansible'
# set interfaces ethernet eth2 disable
# set interfaces ethernet eth2 duplex 'full'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 mtu '600'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth2 speed '100'
# set interfaces ethernet eth3 description 'Configured by Ansible Network'
# set interfaces ethernet eth3 duplex 'full'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 speed '100'
# set interfaces loopback lo
#
#
- name: Delete attributes of given interfaces (Note - This won't delete the interfaces
themselves)
vyos.vyos.vyos_interfaces:
config:
- name: bond1
- name: eth1
- name: eth2
- name: eth3
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "enabled": true,
# "mtu": 1300,
# "name": "bond0"
# },
# {
# "description": "LAG - 1",
# "enabled": true,
# "name": "bond1"
# },
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "description": "Configured by Ansible Network",
# "duplex": "full",
# "enabled": true,
# "name": "eth3",
# "speed": "100"
# },
# {
# "description": "Configured by Ansible",
# "duplex": "full",
# "enabled": false,
# "mtu": 600,
# "name": "eth2",
# "speed": "100"
# },
# {
# "description": "Configured by Ansible Network",
# "duplex": "full",
# "enabled": true,
# "name": "eth1",
# "speed": "100"
# },
# {
# "description": "Outbound Interface for this appliance",
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
# "commands": [
# "delete interfaces bonding bond1 description",
# "delete interfaces ethernet eth1 speed",
# "delete interfaces ethernet eth1 duplex",
# "delete interfaces ethernet eth1 description",
# "delete interfaces ethernet eth2 speed",
# "delete interfaces ethernet eth2 disable",
# "delete interfaces ethernet eth2 duplex",
# "delete interfaces ethernet eth2 disable",
# "delete interfaces ethernet eth2 description",
# "delete interfaces ethernet eth2 disable",
# "delete interfaces ethernet eth2 mtu",
# "delete interfaces ethernet eth2 disable",
# "delete interfaces ethernet eth3 speed",
# "delete interfaces ethernet eth3 duplex",
# "delete interfaces ethernet eth3 description"
# ]
#
# "after": [
# {
# "enabled": true,
# "mtu": 1300,
# "name": "bond0"
# },
# {
# "enabled": true,
# "name": "bond1"
# },
# {
# "enabled": true,
# "name": "lo"
# },
# {
# "enabled": true,
# "name": "eth3"
# },
# {
# "enabled": true,
# "name": "eth2"
# },
# {
# "enabled": true,
# "name": "eth1"
# },
# {
# "description": "Outbound Interface for this appliance",
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
#
# ------------
# After state
# ------------
#
# vyos@vyos:~$ show configuration commands | grep interfaces
# set interfaces bonding bond0 mtu '1300'
# set interfaces bonding bond1
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
# set interfaces ethernet eth1 smp-affinity 'auto'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth2 smp-affinity 'auto'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces loopback lo
#
#
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep interfaces
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Configured by Ansible'
# set interfaces ethernet eth1 duplex 'auto'
# set interfaces ethernet eth1 mtu '1500'
# set interfaces ethernet eth1 speed 'auto'
# set interfaces ethernet eth1 vif 200 description 'VIF - 200'
# set interfaces ethernet eth2 description 'Configured by Ansible'
# set interfaces ethernet eth2 duplex 'auto'
# set interfaces ethernet eth2 mtu '1500'
# set interfaces ethernet eth2 speed 'auto'
# set interfaces ethernet eth2 vif 200 description 'VIF - 200'
#
- name: Gather listed interfaces with provided configurations
vyos.vyos.vyos_interfaces:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "description": "Configured by Ansible",
# "duplex": "auto",
# "enabled": true,
# "mtu": 1500,
# "name": "eth2",
# "speed": "auto",
# "vifs": [
# {
# "description": "VIF - 200",
# "enabled": true,
# "vlan_id": 200
# }
# ]
# },
# {
# "description": "Configured by Ansible",
# "duplex": "auto",
# "enabled": true,
# "mtu": 1500,
# "name": "eth1",
# "speed": "auto",
# "vifs": [
# {
# "description": "VIF - 200",
# "enabled": true,
# "vlan_id": 200
# }
# ]
# },
# {
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
#
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep interfaces
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 description 'Configured by Ansible'
# set interfaces ethernet eth1 duplex 'auto'
# set interfaces ethernet eth1 mtu '1500'
# set interfaces ethernet eth1 speed 'auto'
# set interfaces ethernet eth1 vif 200 description 'VIF - 200'
# set interfaces ethernet eth2 description 'Configured by Ansible'
# set interfaces ethernet eth2 duplex 'auto'
# set interfaces ethernet eth2 mtu '1500'
# set interfaces ethernet eth2 speed 'auto'
# set interfaces ethernet eth2 vif 200 description 'VIF - 200'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_interfaces:
config:
- name: eth0
enabled: true
duplex: auto
speed: auto
- name: eth1
description: Configured by Ansible - Interface 1
mtu: 1500
speed: auto
duplex: auto
enabled: true
vifs:
- vlan_id: 100
description: Eth1 - VIF 100
mtu: 400
enabled: true
- vlan_id: 101
description: Eth1 - VIF 101
enabled: true
- name: eth2
description: Configured by Ansible - Interface 2 (ADMIN DOWN)
mtu: 600
enabled: false
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set interfaces ethernet eth0 duplex 'auto'",
# "set interfaces ethernet eth0 speed 'auto'",
# "delete interfaces ethernet eth0 disable",
# "set interfaces ethernet eth1 duplex 'auto'",
# "delete interfaces ethernet eth1 disable",
# "set interfaces ethernet eth1 speed 'auto'",
# "set interfaces ethernet eth1 description 'Configured by Ansible - Interface 1'",
# "set interfaces ethernet eth1 mtu '1500'",
# "set interfaces ethernet eth1 vif 100 description 'Eth1 - VIF 100'",
# "set interfaces ethernet eth1 vif 100 mtu '400'",
# "set interfaces ethernet eth1 vif 101 description 'Eth1 - VIF 101'",
# "set interfaces ethernet eth2 disable",
# "set interfaces ethernet eth2 description 'Configured by Ansible - Interface 2 (ADMIN DOWN)'",
# "set interfaces ethernet eth2 mtu '600'"
# ]
# Using parsed
#
#
- name: Parse the configuration.
vyos.vyos.vyos_interfaces:
running_config:
"set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth0 duplex 'auto'
set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19'
set interfaces ethernet eth0 smp_affinity 'auto'
set interfaces ethernet eth0 speed 'auto'
set interfaces ethernet eth1 description 'Configured by Ansible'
set interfaces ethernet eth1 duplex 'auto'
set interfaces ethernet eth1 mtu '1500'
set interfaces ethernet eth1 speed 'auto'
set interfaces ethernet eth1 vif 200 description 'VIF - 200'
set interfaces ethernet eth2 description 'Configured by Ansible'
set interfaces ethernet eth2 duplex 'auto'
set interfaces ethernet eth2 mtu '1500'
set interfaces ethernet eth2 speed 'auto'
set interfaces ethernet eth2 vif 200 description 'VIF - 200'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "description": "Configured by Ansible",
# "duplex": "auto",
# "enabled": true,
# "mtu": 1500,
# "name": "eth2",
# "speed": "auto",
# "vifs": [
# {
# "description": "VIF - 200",
# "enabled": true,
# "vlan_id": 200
# }
# ]
# },
# {
# "description": "Configured by Ansible",
# "duplex": "auto",
# "enabled": true,
# "mtu": 1500,
# "name": "eth1",
# "speed": "auto",
# "vifs": [
# {
# "description": "VIF - 200",
# "enabled": true,
# "vlan_id": 200
# }
# ]
# },
# {
# "duplex": "auto",
# "enabled": true,
# "name": "eth0",
# "speed": "auto"
# }
# ]
"""
RETURN = """
before:
description: The configuration 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:
- 'set interfaces ethernet eth1 mtu 1200'
- 'set interfaces ethernet eth2 vif 100 description VIF 100'
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import (
InterfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.interfaces.interfaces import (
Interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=InterfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_l3_interface.py b/plugins/modules/vyos_l3_interface.py
index 23028c4..676d6ec 100644
--- a/plugins/modules/vyos_l3_interface.py
+++ b/plugins/modules/vyos_l3_interface.py
@@ -1,332 +1,331 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_l3_interface
author: Ricardo Carrillo Cruz (@rcarrillocruz)
short_description: (deprecated, removed after 2022-06-01) Manage L3 interfaces on
VyOS network devices
description:
- This module provides declarative management of L3 interfaces on VyOS network devices.
version_added: 1.0.0
deprecated:
alternative: vyos_l3_interfaces
why: Updated modules released with more functionality.
removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the L3 interface.
ipv4:
description:
- IPv4 of the L3 interface.
ipv6:
description:
- IPv6 of the L3 interface.
aggregate:
description: List of L3 interfaces definitions
state:
description:
- State of the L3 interface configuration.
default: present
choices:
- present
- absent
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: Set eth0 IPv4 address
vyos.vyos.vyos_l3_interface:
name: eth0
ipv4: 192.168.0.1/24
- name: Remove eth0 IPv4 address
vyos.vyos.vyos_l3_interface:
name: eth0
state: absent
- name: Set IP addresses on aggregate
vyos.vyos.vyos_l3_interface:
aggregate:
- {name: eth1, ipv4: 192.168.2.10/24}
- {name: eth2, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
- name: Remove IP addresses on aggregate
vyos.vyos.vyos_l3_interface:
aggregate:
- {name: eth1, ipv4: 192.168.2.10/24}
- {name: eth2, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces ethernet eth0 address '192.168.0.1/24'
"""
import socket
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
is_masklen,
validate_ip_address,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def is_ipv4(value):
if value:
address = value.split("/")
if is_masklen(address[1]) and validate_ip_address(address[0]):
return True
return False
def is_ipv6(value):
if value:
address = value.split("/")
if 0 <= int(address[1]) <= 128:
try:
socket.inet_pton(socket.AF_INET6, address[0])
except socket.error:
return False
return True
return False
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
ipv4 = w["ipv4"]
ipv6 = w["ipv6"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent" and obj_in_have:
if (
not ipv4
and not ipv6
and (obj_in_have["ipv4"] or obj_in_have["ipv6"])
):
if name == "lo":
commands.append("delete interfaces loopback lo address")
else:
commands.append(
"delete interfaces ethernet " + name + " address"
)
else:
if ipv4 and ipv4 in obj_in_have["ipv4"]:
if name == "lo":
commands.append(
"delete interfaces loopback lo address " + ipv4
)
else:
commands.append(
"delete interfaces ethernet "
+ name
+ " address "
+ ipv4
)
if ipv6 and ipv6 in obj_in_have["ipv6"]:
if name == "lo":
commands.append(
"delete interfaces loopback lo address " + ipv6
)
else:
commands.append(
"delete interfaces ethernet "
+ name
+ " address "
+ ipv6
)
elif state == "present" and obj_in_have:
if ipv4 and ipv4 not in obj_in_have["ipv4"]:
if name == "lo":
commands.append(
"set interfaces loopback lo address " + ipv4
)
else:
commands.append(
"set interfaces ethernet " + name + " address " + ipv4
)
if ipv6 and ipv6 not in obj_in_have["ipv6"]:
if name == "lo":
commands.append(
"set interfaces loopback lo address " + ipv6
)
else:
commands.append(
"set interfaces ethernet " + name + " address " + ipv6
)
return commands
def map_config_to_obj(module):
obj = []
output = run_commands(module, ["show interfaces"])
lines = re.split(r"\n[e|l]", output[0])[1:]
if len(lines) > 0:
for line in lines:
splitted_line = line.split()
if len(splitted_line) > 0:
ipv4 = []
ipv6 = []
if splitted_line[0].lower().startswith("th"):
name = "e" + splitted_line[0].lower()
elif splitted_line[0].lower().startswith("o"):
name = "l" + splitted_line[0].lower()
for i in splitted_line[1:]:
if ("." in i or ":" in i) and "/" in i:
value = i.split(r"\n")[0]
if is_ipv4(value):
ipv4.append(value)
elif is_ipv6(value):
ipv6.append(value)
obj.append({"name": name, "ipv4": ipv4, "ipv6": ipv6})
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{
"name": module.params["name"],
"ipv4": module.params["ipv4"],
"ipv6": module.params["ipv6"],
"state": module.params["state"],
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
ipv4=dict(),
ipv6=dict(),
state=dict(default="present", choices=["present", "absent"]),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_l3_interfaces.py b/plugins/modules/vyos_l3_interfaces.py
index e96d2bd..39dd23c 100644
--- a/plugins/modules/vyos_l3_interfaces.py
+++ b/plugins/modules/vyos_l3_interfaces.py
@@ -1,576 +1,575 @@
#!/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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_l3_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_l3_interfaces
short_description: L3 interfaces resource module
description: This module manages the L3 interface attributes on VyOS network devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Nilashish Chakraborty (@NilashishC)
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: The provided L3 interfaces configuration.
type: list
elements: dict
suboptions:
name:
description:
- Full name of the interface, e.g. eth0, eth1.
type: str
required: true
ipv4:
description:
- List of IPv4 addresses of the interface.
type: list
elements: dict
suboptions:
address:
description:
- IPv4 address of the interface.
type: str
ipv6:
description:
- List of IPv6 addresses of the interface.
type: list
elements: dict
suboptions:
address:
description:
- IPv6 address of the interface.
type: str
vifs:
description:
- Virtual sub-interfaces L3 configurations.
elements: dict
type: list
suboptions:
vlan_id:
description:
- Identifier for the virtual sub-interface.
type: int
ipv4:
description:
- List of IPv4 addresses of the virtual interface.
type: list
elements: dict
suboptions:
address:
description:
- IPv4 address of the virtual interface.
type: str
ipv6:
description:
- List of IPv6 addresses of the virtual interface.
type: list
elements: dict
suboptions:
address:
description:
- IPv6 address of the virtual 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 VyOS device
by executing the command B(show configuration commands | grep -e eth[2,3]).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- parsed
- gathered
- rendered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos:~$ show configuration commands | grep -e eth[2,3]
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101
# set interfaces ethernet eth3 vif 102
- name: Merge provided configuration with device configuration
vyos.vyos.vyos_l3_interfaces:
config:
- name: eth2
ipv4:
- address: 192.0.2.10/28
- address: 198.51.100.40/27
ipv6:
- address: 2001:db8:100::2/32
- address: 2001:db8:400::10/32
- name: eth3
ipv4:
- address: 203.0.113.65/26
vifs:
- vlan_id: 101
ipv4:
- address: 192.0.2.71/28
- address: 198.51.100.131/25
- vlan_id: 102
ipv6:
- address: 2001:db8:1000::5/38
- address: 2001:db8:1400::3/38
state: merged
# After state:
# -------------
#
# vyos:~$ show configuration commands | grep -e eth[2,3]
# set interfaces ethernet eth2 address '192.0.2.10/28'
# set interfaces ethernet eth2 address '198.51.100.40/27'
# set interfaces ethernet eth2 address '2001:db8:100::2/32'
# set interfaces ethernet eth2 address '2001:db8:400::10/32'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 address '203.0.113.65/26'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101 address '192.0.2.71/28'
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
# set interfaces ethernet eth3 vif 102 address '2001:db8:1000::5/38'
# set interfaces ethernet eth3 vif 102 address '2001:db8:1400::3/38'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
# Using replaced
#
# Before state:
# -------------
#
# vyos:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 address '192.0.2.11/24'
# set interfaces ethernet eth2 address '2001:db8::10/32'
# set interfaces ethernet eth2 address '2001:db8::11/32'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 address '198.51.100.10/24'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
#
- name: Replace device configurations of listed interfaces with provided configurations
vyos.vyos.vyos_l3_interfaces:
config:
- name: eth2
ipv4:
- address: 192.0.2.10/24
- name: eth3
ipv6:
- address: 2001:db8::11/32
state: replaced
# After state:
# -------------
#
# vyos:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 address '2001:db8::11/32'
# set interfaces ethernet eth3 vif 101
# set interfaces ethernet eth3 vif 102
# Using overridden
#
# Before state
# --------------
#
# vyos@vyos-appliance:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 address '192.0.2.11/24'
# set interfaces ethernet eth2 address '2001:db8::10/32'
# set interfaces ethernet eth2 address '2001:db8::11/32'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 address '198.51.100.10/24'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_l3_interfaces:
config:
- name: eth0
ipv4:
- address: dhcp
ipv6:
- address: dhcpv6
state: overridden
# After state
# ------------
#
# vyos@vyos-appliance:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 address 'dhcpv6'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101
# set interfaces ethernet eth3 vif 102
# Using deleted
#
# Before state
# -------------
# vyos@vyos-appliance:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
# set interfaces ethernet eth0 smp-affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 address '192.0.2.11/24'
# set interfaces ethernet eth2 address '2001:db8::10/32'
# set interfaces ethernet eth2 address '2001:db8::11/32'
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
# set interfaces ethernet eth3 address '198.51.100.10/24'
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
- name: Delete L3 attributes of given interfaces (Note - This won't delete the interface
itself)
vyos.vyos.vyos_l3_interfaces:
config:
- name: eth1
- name: eth2
- name: eth3
state: deleted
# After state
# ------------
# vyos@vyos-appliance:~$ show configuration commands | grep eth
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65'
# set interfaces ethernet eth1 smp_affinity 'auto'
# set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79'
# set interfaces ethernet eth2 smp_affinity 'auto'
# set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85'
# set interfaces ethernet eth3 smp_affinity 'auto'
# Using gathered
#
# Before state:
# -------------
#
# vyos:~$ show configuration commands | grep -e eth[2,3,0]
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.11/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 address '2001:db8::10/32'
# set interfaces ethernet eth2 address '2001:db8::12/32'
#
- name: Gather listed l3 interfaces with provided configurations
vyos.vyos.vyos_l3_interfaces:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "ipv4": [
# {
# "address": "192.0.2.11/24"
# },
# {
# "address": "192.0.2.10/24"
# }
# ],
# "ipv6": [
# {
# "address": "2001:db8::10/32"
# },
# {
# "address": "2001:db8::12/32"
# }
# ],
# "name": "eth2"
# },
# {
# "ipv4": [
# {
# "address": "192.0.2.14/24"
# }
# ],
# "name": "eth1"
# },
# {
# "ipv4": [
# {
# "address": "dhcp"
# }
# ],
# "name": "eth0"
# }
# ]
#
#
# After state:
# -------------
#
# vyos:~$ show configuration commands | grep -e eth[2,3]
# set interfaces ethernet eth0 address 'dhcp'
# set interfaces ethernet eth0 duplex 'auto'
# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19'
# set interfaces ethernet eth0 smp_affinity 'auto'
# set interfaces ethernet eth0 speed 'auto'
# set interfaces ethernet eth1 address '192.0.2.14/24'
# set interfaces ethernet eth2 address '192.0.2.11/24'
# set interfaces ethernet eth2 address '192.0.2.10/24'
# set interfaces ethernet eth2 address '2001:db8::10/32'
# set interfaces ethernet eth2 address '2001:db8::12/32'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_l3_interfaces:
config:
- name: eth1
ipv4:
- address: 192.0.2.14/24
- name: eth2
ipv4:
- address: 192.0.2.10/24
- address: 192.0.2.11/24
ipv6:
- address: 2001:db8::10/32
- address: 2001:db8::12/32
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set interfaces ethernet eth1 address '192.0.2.14/24'",
# "set interfaces ethernet eth2 address '192.0.2.11/24'",
# "set interfaces ethernet eth2 address '192.0.2.10/24'",
# "set interfaces ethernet eth2 address '2001:db8::10/32'",
# "set interfaces ethernet eth2 address '2001:db8::12/32'"
# ]
# Using parsed
#
#
- name: parse the provided running configuration
vyos.vyos.vyos_l3_interfaces:
running_config:
"set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth1 address '192.0.2.14/24'
set interfaces ethernet eth2 address '192.0.2.10/24'
set interfaces ethernet eth2 address '192.0.2.11/24'
set interfaces ethernet eth2 address '2001:db8::10/32'
set interfaces ethernet eth2 address '2001:db8::12/32'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "ipv4": [
# {
# "address": "192.0.2.10/24"
# },
# {
# "address": "192.0.2.11/24"
# }
# ],
# "ipv6": [
# {
# "address": "2001:db8::10/32"
# },
# {
# "address": "2001:db8::12/32"
# }
# ],
# "name": "eth2"
# },
# {
# "ipv4": [
# {
# "address": "192.0.2.14/24"
# }
# ],
# "name": "eth1"
# },
# {
# "ipv4": [
# {
# "address": "dhcp"
# }
# ],
# "name": "eth0"
# }
# ]
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['set interfaces ethernet eth1 192.0.2.14/2', 'set interfaces ethernet eth3 vif 101 address 198.51.100.130/25']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import (
L3_interfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.l3_interfaces.l3_interfaces import (
L3_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=L3_interfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = L3_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lag_interfaces.py b/plugins/modules/vyos_lag_interfaces.py
index 14ecbd7..c2868a8 100644
--- a/plugins/modules/vyos_lag_interfaces.py
+++ b/plugins/modules/vyos_lag_interfaces.py
@@ -1,779 +1,778 @@
#!/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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_lag_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lag_interfaces
short_description: LAG interfaces resource module
description: This module manages attributes of link aggregation groups on VyOS network
devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author: Rohit Thakur (@rohitthakur2590)
options:
config:
description: A list of link aggregation group configurations.
type: list
suboptions:
name:
description:
- Name of the link aggregation group (LAG) or bond.
type: str
required: true
mode:
description:
- LAG or bond mode.
type: str
choices:
- 802.3ad
- active-backup
- broadcast
- round-robin
- transmit-load-balance
- adaptive-load-balance
- xor-hash
members:
description:
- List of member interfaces for the LAG (bond).
type: list
suboptions:
member:
description:
- Name of the member interface.
type: str
primary:
description:
- Primary device interfaces for the LAG (bond).
type: str
hash_policy:
description:
- LAG or bonding transmit hash policy.
type: str
choices:
- layer2
- layer2+3
- layer3+4
arp_monitor:
description:
- ARP Link monitoring parameters.
type: dict
suboptions:
interval:
description:
- ARP link monitoring frequency in milliseconds.
type: int
target:
description:
- IP address to use for ARP monitoring.
type: list
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep bond).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- parsed
- gathered
- rendered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2
# set interfaces bonding bond3
#
- name: Merge provided configuration with device configuration
vyos.vyos.vyos_lag_interfaces:
config:
- name: bond2
mode: active-backup
members:
- member: eth2
- member: eth1
hash_policy: layer2
primary: eth2
- name: bond3
mode: active-backup
hash_policy: layer2+3
members:
- member: eth3
primary: eth3
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "name": "bond2"
# },
# {
# "name": "bond3"
# }
# ],
#
# "commands": [
# "set interfaces bonding bond2 hash-policy 'layer2'",
# "set interfaces bonding bond2 mode 'active-backup'",
# "set interfaces ethernet eth2 bond-group bond2",
# "set interfaces ethernet eth1 bond-group bond2",
# "set interfaces bonding bond2 primary 'eth2'",
# "set interfaces bonding bond3 hash-policy 'layer2+3'",
# "set interfaces bonding bond3 mode 'active-backup'",
# "set interfaces ethernet eth3 bond-group bond3",
# "set interfaces bonding bond3 primary 'eth3'"
# ]
#
# "after": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond2",
# "primary": "eth2"
# },
# {
# "hash_policy": "layer2+3",
# "members": [
# {
# "member": "eth3"
# }
# ],
# "mode": "active-backup",
# "name": "bond3",
# "primary": "eth3"
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2 hash-policy 'layer2'
# set interfaces bonding bond2 mode 'active-backup'
# set interfaces bonding bond2 primary 'eth2'
# set interfaces bonding bond3 hash-policy 'layer2+3'
# set interfaces bonding bond3 mode 'active-backup'
# set interfaces bonding bond3 primary 'eth3'
# set interfaces ethernet eth1 bond-group 'bond2'
# set interfaces ethernet eth2 bond-group 'bond2'
# set interfaces ethernet eth3 bond-group 'bond3'
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2 hash-policy 'layer2'
# set interfaces bonding bond2 mode 'active-backup'
# set interfaces bonding bond2 primary 'eth2'
# set interfaces bonding bond3 hash-policy 'layer2+3'
# set interfaces bonding bond3 mode 'active-backup'
# set interfaces bonding bond3 primary 'eth3'
# set interfaces ethernet eth1 bond-group 'bond2'
# set interfaces ethernet eth2 bond-group 'bond2'
# set interfaces ethernet eth3 bond-group 'bond3'
#
- name: Replace device configurations of listed LAGs with provided configurations
vyos.vyos.vyos_lag_interfaces:
config:
- name: bond3
mode: 802.3ad
hash_policy: layer2
members:
- member: eth3
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond2",
# "primary": "eth2"
# },
# {
# "hash_policy": "layer2+3",
# "members": [
# {
# "member": "eth3"
# }
# ],
# "mode": "active-backup",
# "name": "bond3",
# "primary": "eth3"
# }
# ],
#
# "commands": [
# "delete interfaces bonding bond3 primary",
# "set interfaces bonding bond3 hash-policy 'layer2'",
# "set interfaces bonding bond3 mode '802.3ad'"
# ],
#
# "after": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond2",
# "primary": "eth2"
# },
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth3"
# }
# ],
# "mode": "802.3ad",
# "name": "bond3"
# }
# ],
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2 hash-policy 'layer2'
# set interfaces bonding bond2 mode 'active-backup'
# set interfaces bonding bond2 primary 'eth2'
# set interfaces bonding bond3 hash-policy 'layer2'
# set interfaces bonding bond3 mode '802.3ad'
# set interfaces ethernet eth1 bond-group 'bond2'
# set interfaces ethernet eth2 bond-group 'bond2'
# set interfaces ethernet eth3 bond-group 'bond3'
# Using overridden
#
# Before state
# --------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2 hash-policy 'layer2'
# set interfaces bonding bond2 mode 'active-backup'
# set interfaces bonding bond2 primary 'eth2'
# set interfaces bonding bond3 hash-policy 'layer2'
# set interfaces bonding bond3 mode '802.3ad'
# set interfaces ethernet eth1 bond-group 'bond2'
# set interfaces ethernet eth2 bond-group 'bond2'
# set interfaces ethernet eth3 bond-group 'bond3'
#
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_lag_interfaces:
config:
- name: bond3
mode: active-backup
members:
- member: eth1
- member: eth2
- member: eth3
primary: eth3
hash_policy: layer2
state: overridden
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond2",
# "primary": "eth2"
# },
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth3"
# }
# ],
# "mode": "802.3ad",
# "name": "bond3"
# }
# ],
#
# "commands": [
# "delete interfaces bonding bond2 hash-policy",
# "delete interfaces ethernet eth1 bond-group bond2",
# "delete interfaces ethernet eth2 bond-group bond2",
# "delete interfaces bonding bond2 mode",
# "delete interfaces bonding bond2 primary",
# "set interfaces bonding bond3 mode 'active-backup'",
# "set interfaces ethernet eth1 bond-group bond3",
# "set interfaces ethernet eth2 bond-group bond3",
# "set interfaces bonding bond3 primary 'eth3'"
# ],
#
# "after": [
# {
# "name": "bond2"
# },
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# },
# {
# "member": "eth3"
# }
# ],
# "mode": "active-backup",
# "name": "bond3",
# "primary": "eth3"
# }
# ],
#
#
# After state
# ------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2
# set interfaces bonding bond3 hash-policy 'layer2'
# set interfaces bonding bond3 mode 'active-backup'
# set interfaces bonding bond3 primary 'eth3'
# set interfaces ethernet eth1 bond-group 'bond3'
# set interfaces ethernet eth2 bond-group 'bond3'
# set interfaces ethernet eth3 bond-group 'bond3'
# Using deleted
#
# Before state
# -------------
#
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2 hash-policy 'layer2'
# set interfaces bonding bond2 mode 'active-backup'
# set interfaces bonding bond2 primary 'eth2'
# set interfaces bonding bond3 hash-policy 'layer2+3'
# set interfaces bonding bond3 mode 'active-backup'
# set interfaces bonding bond3 primary 'eth3'
# set interfaces ethernet eth1 bond-group 'bond2'
# set interfaces ethernet eth2 bond-group 'bond2'
# set interfaces ethernet eth3 bond-group 'bond3'
#
- name: Delete LAG attributes of given interfaces (Note This won't delete the interface
itself)
vyos.vyos.vyos_lag_interfaces:
config:
- name: bond2
- name: bond3
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# },
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond2",
# "primary": "eth2"
# },
# {
# "hash_policy": "layer2+3",
# "members": [
# {
# "member": "eth3"
# }
# ],
# "mode": "active-backup",
# "name": "bond3",
# "primary": "eth3"
# }
# ],
# "commands": [
# "delete interfaces bonding bond2 hash-policy",
# "delete interfaces ethernet eth1 bond-group bond2",
# "delete interfaces ethernet eth2 bond-group bond2",
# "delete interfaces bonding bond2 mode",
# "delete interfaces bonding bond2 primary",
# "delete interfaces bonding bond3 hash-policy",
# "delete interfaces ethernet eth3 bond-group bond3",
# "delete interfaces bonding bond3 mode",
# "delete interfaces bonding bond3 primary"
# ],
#
# "after": [
# {
# "name": "bond2"
# },
# {
# "name": "bond3"
# }
# ],
#
# After state
# ------------
# vyos@vyos:~$ show configuration commands | grep bond
# set interfaces bonding bond2
# set interfaces bonding bond3
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep bond
# set interfaces bonding bond0 hash-policy 'layer2'
# set interfaces bonding bond0 mode 'active-backup'
# set interfaces bonding bond0 primary 'eth1'
# set interfaces bonding bond1 hash-policy 'layer2+3'
# set interfaces bonding bond1 mode 'active-backup'
# set interfaces bonding bond1 primary 'eth2'
# set interfaces ethernet eth1 bond-group 'bond0'
# set interfaces ethernet eth2 bond-group 'bond1'
#
- name: Gather listed lag interfaces with provided configurations
vyos.vyos.vyos_lag_interfaces:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "afi": "ipv6",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "This is ipv6 specific rule-set",
# "name": "UPLINK",
# "rules": [
# {
# "action": "accept",
# "description": "Fwipv6-Rule 1 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 1
# },
# {
# "action": "accept",
# "description": "Fwipv6-Rule 2 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 2
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "rule_sets": [
# {
# "default_action": "accept",
# "description": "IPv4 INBOUND rule set",
# "name": "INBOUND",
# "rules": [
# {
# "action": "accept",
# "description": "Rule 101 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 101
# },
# {
# "action": "reject",
# "description": "Rule 102 is configured by Ansible",
# "ipsec": "match-ipsec",
# "number": 102
# },
# {
# "action": "accept",
# "description": "Rule 103 is configured by Ansible",
# "destination": {
# "group": {
# "address_group": "inbound"
# }
# },
# "number": 103,
# "source": {
# "address": "192.0.2.0"
# },
# "state": {
# "established": true,
# "invalid": false,
# "new": false,
# "related": true
# }
# }
# ]
# }
# ]
# }
# ]
#
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep bond
# set interfaces bonding bond0 hash-policy 'layer2'
# set interfaces bonding bond0 mode 'active-backup'
# set interfaces bonding bond0 primary 'eth1'
# set interfaces bonding bond1 hash-policy 'layer2+3'
# set interfaces bonding bond1 mode 'active-backup'
# set interfaces bonding bond1 primary 'eth2'
# set interfaces ethernet eth1 bond-group 'bond0'
# set interfaces ethernet eth2 bond-group 'bond1'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_lag_interfaces:
config:
- name: bond0
hash_policy: layer2
members:
- member: eth1
mode: active-backup
primary: eth1
- name: bond1
hash_policy: layer2+3
members:
- member: eth2
mode: active-backup
primary: eth2
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set interfaces bonding bond0 hash-policy 'layer2'",
# "set interfaces ethernet eth1 bond-group 'bond0'",
# "set interfaces bonding bond0 mode 'active-backup'",
# "set interfaces bonding bond0 primary 'eth1'",
# "set interfaces bonding bond1 hash-policy 'layer2+3'",
# "set interfaces ethernet eth2 bond-group 'bond1'",
# "set interfaces bonding bond1 mode 'active-backup'",
# "set interfaces bonding bond1 primary 'eth2'"
# ]
# Using parsed
#
#
- name: Parsed the commands for provided configuration
vyos.vyos.vyos_l3_interfaces:
running_config:
"set interfaces bonding bond0 hash-policy 'layer2'
set interfaces bonding bond0 mode 'active-backup'
set interfaces bonding bond0 primary 'eth1'
set interfaces bonding bond1 hash-policy 'layer2+3'
set interfaces bonding bond1 mode 'active-backup'
set interfaces bonding bond1 primary 'eth2'
set interfaces ethernet eth1 bond-group 'bond0'
set interfaces ethernet eth2 bond-group 'bond1'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "hash_policy": "layer2",
# "members": [
# {
# "member": "eth1"
# }
# ],
# "mode": "active-backup",
# "name": "bond0",
# "primary": "eth1"
# },
# {
# "hash_policy": "layer2+3",
# "members": [
# {
# "member": "eth2"
# }
# ],
# "mode": "active-backup",
# "name": "bond1",
# "primary": "eth2"
# }
# ]
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- 'set interfaces bonding bond2'
- 'set interfaces bonding bond2 hash-policy layer2'
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import (
Lag_interfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lag_interfaces.lag_interfaces import (
Lag_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Lag_interfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Lag_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_linkagg.py b/plugins/modules/vyos_linkagg.py
index 4f45319..4e63e2f 100644
--- a/plugins/modules/vyos_linkagg.py
+++ b/plugins/modules/vyos_linkagg.py
@@ -1,330 +1,329 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_linkagg
author: Ricardo Carrillo Cruz (@rcarrillocruz)
short_description: (deprecated, removed after 2022-06-01) Manage link aggregation
groups on VyOS network devices
description:
- This module provides declarative management of link aggregation groups on VyOS network
devices.
version_added: 1.0.0
deprecated:
alternative: vyos_lag_interfaces
why: Updated modules released with more functionality.
removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the link aggregation group.
required: true
type: str
mode:
description:
- Mode of the link aggregation group.
choices:
- 802.3ad
- active-backup
- broadcast
- round-robin
- transmit-load-balance
- adaptive-load-balance
- xor-hash
- on
type: str
members:
description:
- List of members of the link aggregation group.
type: list
aggregate:
description: List of link aggregation definitions.
type: list
state:
description:
- State of the link aggregation group.
default: present
choices:
- present
- absent
- up
- down
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: configure link aggregation group
vyos.vyos.vyos_linkagg:
name: bond0
members:
- eth0
- eth1
- name: remove configuration
vyos.vyos.vyos_linkagg:
name: bond0
state: absent
- name: Create aggregate of linkagg definitions
vyos.vyos.vyos_linkagg:
aggregate:
- {name: bond0, members: [eth1]}
- {name: bond1, members: [eth2]}
- name: Remove aggregate of linkagg definitions
vyos.vyos.vyos_linkagg:
aggregate:
- name: bond0
- name: bond1
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces bonding bond0
- set interfaces ethernet eth0 bond-group 'bond0'
- set interfaces ethernet eth1 bond-group 'bond0'
"""
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.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
members = w.get("members") or []
mode = w["mode"]
if mode == "on":
mode = "802.3ad"
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent":
if obj_in_have:
for m in obj_in_have["members"]:
commands.append(
"delete interfaces ethernet " + m + " bond-group"
)
commands.append("delete interfaces bonding " + name)
else:
if not obj_in_have:
commands.append(
"set interfaces bonding " + name + " mode " + mode
)
for m in members:
commands.append(
"set interfaces ethernet " + m + " bond-group " + name
)
if state == "down":
commands.append(
"set interfaces bonding " + name + " disable"
)
else:
if mode != obj_in_have["mode"]:
commands.append(
"set interfaces bonding " + name + " mode " + mode
)
missing_members = list(
set(members) - set(obj_in_have["members"])
)
for m in missing_members:
commands.append(
"set interfaces ethernet " + m + " bond-group " + name
)
if state == "down" and obj_in_have["state"] == "up":
commands.append(
"set interfaces bonding " + name + " disable"
)
elif state == "up" and obj_in_have["state"] == "down":
commands.append(
"delete interfaces bonding " + name + " disable"
)
return commands
def map_config_to_obj(module):
obj = []
output = run_commands(module, ["show interfaces bonding slaves"])
lines = output[0].splitlines()
if len(lines) > 1:
for line in lines[1:]:
splitted_line = line.split()
name = splitted_line[0]
mode = splitted_line[1]
state = splitted_line[2]
if len(splitted_line) > 4:
members = splitted_line[4:]
else:
members = []
obj.append(
{
"name": name,
"mode": mode,
"members": members,
"state": state,
}
)
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{
"name": module.params["name"],
"mode": module.params["mode"],
"members": module.params["members"],
"state": module.params["state"],
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
mode=dict(
choices=[
"802.3ad",
"active-backup",
"broadcast",
"round-robin",
"transmit-load-balance",
"adaptive-load-balance",
"xor-hash",
"on",
],
default="802.3ad",
),
members=dict(type="list"),
state=dict(
default="present", choices=["present", "absent", "up", "down"]
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp.py b/plugins/modules/vyos_lldp.py
index 05869f3..9b138c1 100644
--- a/plugins/modules/vyos_lldp.py
+++ b/plugins/modules/vyos_lldp.py
@@ -1,144 +1,143 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp
author: Ricardo Carrillo Cruz (@rcarrillocruz)
short_description: (deprecated, removed after 2022-06-01) Manage LLDP configuration
on VyOS network devices
description:
- This module provides declarative management of LLDP service on VyOS network devices.
version_added: 1.0.0
deprecated:
alternative: vyos_lldp_global
why: Updated modules released with more functionality.
removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
interfaces:
description:
- Name of the interfaces.
type: list
state:
description:
- State of the link aggregation group.
default: present
choices:
- present
- absent
- enabled
- disabled
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: Enable LLDP service
vyos.vyos.vyos_lldp:
state: present
- name: Disable LLDP service
vyos.vyos.vyos_lldp:
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set service lldp
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def has_lldp(module):
config = get_config(module).splitlines()
if "set service 'lldp'" in config or "set service lldp" in config:
return True
else:
return False
def main():
""" main entry point for module execution
"""
argument_spec = dict(
interfaces=dict(type="list"),
state=dict(
default="present",
choices=["present", "absent", "enabled", "disabled"],
),
)
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(
argument_spec=argument_spec, supports_check_mode=True
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
HAS_LLDP = has_lldp(module)
commands = []
if module.params["state"] == "absent" and HAS_LLDP:
commands.append("delete service lldp")
elif module.params["state"] == "present" and not HAS_LLDP:
commands.append("set service lldp")
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp_global.py b/plugins/modules/vyos_lldp_global.py
index 1706fab..017133e 100644
--- a/plugins/modules/vyos_lldp_global.py
+++ b/plugins/modules/vyos_lldp_global.py
@@ -1,502 +1,501 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_lldp_global
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp_global
short_description: LLDP global resource module
description: This module manages link layer discovery protocol (LLDP) attributes on
VyOS devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: The provided link layer discovery protocol (LLDP) configuration.
type: dict
suboptions:
enable:
description:
- This argument is a boolean value to enable or disable LLDP.
type: bool
address:
description:
- This argument defines management-address.
type: str
snmp:
description:
- This argument enable the SNMP queries to LLDP database.
type: str
legacy_protocols:
description:
- List of the supported legacy protocols.
type: list
choices:
- cdp
- edp
- fdp
- sonmp
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep lldp).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands|grep lldp
#
- name: Merge provided configuration with device configuration
vyos.vyos.vyos_lldp_global:
config:
legacy_protocols:
- fdp
- cdp
snmp: enable
address: 192.0.2.11
state: merged
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": []
#
# "commands": [
# "set service lldp legacy-protocols fdp",
# "set service lldp legacy-protocols cdp",
# "set service lldp snmp enable",
# "set service lldp management-address '192.0.2.11'"
# ]
#
# "after": [
# {
# "snmp": "enable"
# },
# {
# "address": "192.0.2.11"
# },
# {
# "legacy_protocols": [
# "cdp",
# "fdp"
# ]
# }
# {
# "enable": true
# }
# ]
#
# After state:
# -------------
#
# set service lldp legacy-protocols cdp
# set service lldp legacy-protocols fdp
# set service lldp management-address '192.0.2.11'
# set service lldp snmp enable
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp legacy-protocols cdp
# set service lldp legacy-protocols fdp
# set service lldp management-address '192.0.2.11'
# set service lldp snmp enable
#
- name: Replace device configurations with provided configurations
vyos.vyos.vyos_lldp_global:
config:
legacy_protocols:
- edp
- sonmp
- cdp
address: 192.0.2.14
state: replaced
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
#
# "before": [
# {
# "snmp": "enable"
# },
# {
# "address": "192.0.2.11"
# },
# {
# "legacy_protocols": [
# "cdp",
# "fdp"
# ]
# }
# {
# "enable": true
# }
# ]
# "commands": [
# "delete service lldp snmp",
# "delete service lldp legacy-protocols fdp",
# "set service lldp management-address '192.0.2.14'",
# "set service lldp legacy-protocols edp",
# "set service lldp legacy-protocols sonmp"
# ]
#
# "after": [
# {
# "address": "192.0.2.14"
# },
# {
# "legacy_protocols": [
# "cdp",
# "edp",
# "sonmp"
# ]
# }
# {
# "enable": true
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands|grep lldp
# set service lldp legacy-protocols cdp
# set service lldp legacy-protocols edp
# set service lldp legacy-protocols sonmp
# set service lldp management-address '192.0.2.14'
# Using deleted
#
# Before state
# -------------
# vyos@vyos:~$ show configuration commands|grep lldp
# set service lldp legacy-protocols cdp
# set service lldp legacy-protocols edp
# set service lldp legacy-protocols sonmp
# set service lldp management-address '192.0.2.14'
#
- name: Delete attributes of given lldp service (This won't delete the LLDP service
itself)
vyos.vyos.vyos_lldp_global:
config:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": [
# {
# "address": "192.0.2.14"
# },
# {
# "legacy_protocols": [
# "cdp",
# "edp",
# "sonmp"
# ]
# }
# {
# "enable": true
# }
# ]
#
# "commands": [
# "delete service lldp management-address",
# "delete service lldp legacy-protocols"
# ]
#
# "after": [
# {
# "enable": true
# }
# ]
#
# After state
# ------------
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep lldp
# set service lldp legacy-protocols 'cdp'
# set service lldp management-address '192.0.2.17'
#
- name: Gather lldp global config with provided configurations
vyos.vyos.vyos_lldp_global:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "config_trap": true,
# "group": {
# "address_group": [
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.3.1"
# },
# {
# "address": "192.0.3.2"
# }
# ],
# "name": "ENG-HOSTS"
# },
# {
# "description": "Sales office hosts address list",
# "members": [
# {
# "address": "192.0.2.1"
# },
# {
# "address": "192.0.2.2"
# },
# {
# "address": "192.0.2.3"
# }
# ],
# "name": "SALES-HOSTS"
# }
# ],
# "network_group": [
# {
# "description": "This group has the Management network addresses",
# "members": [
# {
# "address": "192.0.1.0/24"
# }
# ],
# "name": "MGMT"
# }
# ]
# },
# "log_martians": true,
# "ping": {
# "all": true,
# "broadcast": true
# },
# "route_redirects": [
# {
# "afi": "ipv4",
# "icmp_redirects": {
# "receive": false,
# "send": true
# },
# "ip_src_route": true
# }
# ],
# "state_policy": [
# {
# "action": "accept",
# "connection_type": "established",
# "log": true
# },
# {
# "action": "reject",
# "connection_type": "invalid"
# }
# ],
# "syn_cookies": true,
# "twa_hazards_protection": true,
# "validation": "strict"
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep lldp
# set service lldp legacy-protocols 'cdp'
# set service lldp management-address '192.0.2.17'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_lldp_global:
config:
address: 192.0.2.17
enable: true
legacy_protocols:
- cdp
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set service lldp legacy-protocols 'cdp'",
# "set service lldp",
# "set service lldp management-address '192.0.2.17'"
# ]
#
# Using parsed
#
#
- name: Parse the provided commands to provide structured configuration
vyos.vyos.vyos_lldp_global:
running_config:
"set service lldp legacy-protocols 'cdp'
set service lldp legacy-protocols 'fdp'
set service lldp management-address '192.0.2.11'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": {
# "address": "192.0.2.11",
# "enable": true,
# "legacy_protocols": [
# "cdp",
# "fdp"
# ]
# }
#
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- set service lldp legacy-protocols sonmp
- set service lldp management-address '192.0.2.14'
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import (
Lldp_globalArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_global.lldp_global import (
Lldp_global,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Lldp_globalArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Lldp_global(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp_interface.py b/plugins/modules/vyos_lldp_interface.py
index ea51956..2fd1bc5 100644
--- a/plugins/modules/vyos_lldp_interface.py
+++ b/plugins/modules/vyos_lldp_interface.py
@@ -1,266 +1,265 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp_interface
author: Ricardo Carrillo Cruz (@rcarrillocruz)
short_description: (deprecated, removed after 2022-06-01) Manage LLDP interfaces configuration
on VyOS network devices
description:
- This module provides declarative management of LLDP interfaces configuration on
VyOS network devices.
version_added: 1.0.0
deprecated:
alternative: vyos_lldp_interfaces
why: Updated modules released with more functionality.
removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the interface LLDP should be configured on.
type: str
aggregate:
description: List of interfaces LLDP should be configured on.
type: list
state:
description:
- State of the LLDP configuration.
default: present
choices:
- present
- absent
- enabled
- disabled
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: Enable LLDP on eth1
net_lldp_interface:
state: present
- name: Enable LLDP on specific interfaces
net_lldp_interface:
interfaces:
- eth1
- eth2
state: present
- name: Disable LLDP globally
net_lldp_interface:
state: disabled
- name: Create aggregate of LLDP interface configurations
vyos.vyos.vyos_lldp_interface:
aggregate:
- name: eth1
- name: eth2
state: present
- name: Delete aggregate of LLDP interface configurations
vyos.vyos.vyos_lldp_interface:
aggregate:
- name: eth1
- name: eth2
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set service lldp eth1
- set service lldp eth2 disable
"""
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.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent" and obj_in_have:
commands.append("delete service lldp interface " + name)
elif state in ("present", "enabled"):
if not obj_in_have:
commands.append("set service lldp interface " + name)
elif (
obj_in_have
and obj_in_have["state"] == "disabled"
and state == "enabled"
):
commands.append(
"delete service lldp interface " + name + " disable"
)
elif state == "disabled":
if not obj_in_have:
commands.append("set service lldp interface " + name)
commands.append(
"set service lldp interface " + name + " disable"
)
elif obj_in_have and obj_in_have["state"] != "disabled":
commands.append(
"set service lldp interface " + name + " disable"
)
return commands
def map_config_to_obj(module):
obj = []
config = get_config(module).splitlines()
output = [c for c in config if c.startswith("set service lldp interface")]
for i in output:
splitted_line = i.split()
if len(splitted_line) > 5:
new_obj = {"name": splitted_line[4]}
if splitted_line[5] == "'disable'":
new_obj["state"] = "disabled"
else:
new_obj = {"name": splitted_line[4][1:-1]}
new_obj["state"] = "present"
obj.append(new_obj)
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{"name": module.params["name"], "state": module.params["state"]}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
state=dict(
default="present",
choices=["present", "absent", "enabled", "disabled"],
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp_interfaces.py b/plugins/modules/vyos_lldp_interfaces.py
index 11b3fbe..4db7cb0 100644
--- a/plugins/modules/vyos_lldp_interfaces.py
+++ b/plugins/modules/vyos_lldp_interfaces.py
@@ -1,674 +1,673 @@
#!/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.
#
#############################################
-# pylint: skip-file
"""
The module file for vyos_lldp_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp_interfaces
short_description: LLDP interfaces resource module
description: This module manages attributes of lldp interfaces on VyOS network devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: A list of lldp interfaces configurations.
type: list
suboptions:
name:
description:
- Name of the lldp interface.
type: str
required: true
enable:
description:
- to disable lldp on the interface.
type: bool
default: true
location:
description:
- LLDP-MED location data.
type: dict
suboptions:
civic_based:
description:
- Civic-based location data.
type: dict
suboptions:
ca_info:
description: LLDP-MED address info
type: list
suboptions:
ca_type:
description: LLDP-MED Civic Address type.
type: int
required: true
ca_value:
description: LLDP-MED Civic Address value.
type: str
required: true
country_code:
description: Country Code
type: str
required: true
coordinate_based:
description:
- Coordinate-based location.
type: dict
suboptions:
altitude:
description: Altitude in meters.
type: int
datum:
description: Coordinate datum type.
type: str
choices:
- WGS84
- NAD83
- MLLW
latitude:
description: Latitude.
type: str
required: true
longitude:
description: Longitude.
type: str
required: true
elin:
description: Emergency Call Service ELIN number (between 10-25 numbers).
type: str
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep lldp).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- rendered
- parsed
- gathered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
#
- name: Merge provided configuration with device configuration
vyos.vyos.vyos_lldp_interfaces:
config:
- name: eth1
location:
civic_based:
country_code: US
ca_info:
- ca_type: 0
ca_value: ENGLISH
- name: eth2
location:
coordinate_based:
altitude: 2200
datum: WGS84
longitude: 222.267255W
latitude: 33.524449N
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": []
#
# "commands": [
# "set service lldp interface eth1 location civic-based country-code 'US'",
# "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'",
# "set service lldp interface eth1",
# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'",
# "set service lldp interface eth2 location coordinate-based altitude '2200'",
# "set service lldp interface eth2 location coordinate-based datum 'WGS84'",
# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'",
# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'",
# "set service lldp interface eth2 location coordinate-based altitude '2200'",
# "set service lldp interface eth2 location coordinate-based datum 'WGS84'",
# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'",
# "set service lldp interface eth2"
#
# "after": [
# {
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth2"
# },
# {
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth1"
# }
# ],
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth1 location civic-based country-code 'US'
# set service lldp interface eth2 location coordinate-based altitude '2200'
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
# Using replaced
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth1 location civic-based country-code 'US'
# set service lldp interface eth2 location coordinate-based altitude '2200'
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
#
- name: Replace device configurations of listed LLDP interfaces with provided configurations
vyos.vyos.vyos_lldp_interfaces:
config:
- name: eth2
location:
civic_based:
country_code: US
ca_info:
- ca_type: 0
ca_value: ENGLISH
- name: eth1
location:
coordinate_based:
altitude: 2200
datum: WGS84
longitude: 222.267255W
latitude: 33.524449N
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth2"
# },
# {
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth1"
# }
# ]
#
# "commands": [
# "delete service lldp interface eth2 location",
# "set service lldp interface eth2 'disable'",
# "set service lldp interface eth2 location civic-based country-code 'US'",
# "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'",
# "delete service lldp interface eth1 location",
# "set service lldp interface eth1 'disable'",
# "set service lldp interface eth1 location coordinate-based latitude '33.524449N'",
# "set service lldp interface eth1 location coordinate-based altitude '2200'",
# "set service lldp interface eth1 location coordinate-based datum 'WGS84'",
# "set service lldp interface eth1 location coordinate-based longitude '222.267255W'"
# ]
#
# "after": [
# {
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth2"
# },
# {
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth1"
# }
# ]
#
# After state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp interface eth1 'disable'
# set service lldp interface eth1 location coordinate-based altitude '2200'
# set service lldp interface eth1 location coordinate-based datum 'WGS84'
# set service lldp interface eth1 location coordinate-based latitude '33.524449N'
# set service lldp interface eth1 location coordinate-based longitude '222.267255W'
# set service lldp interface eth2 'disable'
# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth2 location civic-based country-code 'US'
# Using overridden
#
# Before state
# --------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
# set service lldp interface eth1 'disable'
# set service lldp interface eth1 location coordinate-based altitude '2200'
# set service lldp interface eth1 location coordinate-based datum 'WGS84'
# set service lldp interface eth1 location coordinate-based latitude '33.524449N'
# set service lldp interface eth1 location coordinate-based longitude '222.267255W'
# set service lldp interface eth2 'disable'
# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth2 location civic-based country-code 'US'
#
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_lldp_interfaces:
config:
- name: eth2
location:
elin: 0000000911
state: overridden
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": [
# {
# "enable": false,
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth2"
# },
# {
# "enable": false,
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth1"
# }
# ]
#
# "commands": [
# "delete service lldp interface eth2 location",
# "delete service lldp interface eth2 disable",
# "set service lldp interface eth2 location elin 0000000911"
#
#
# "after": [
# {
# "location": {
# "elin": 0000000911
# },
# "name": "eth2"
# }
# ]
#
#
# After state
# ------------
#
# vyos@vyos# run show configuration commands | grep lldp
# set service lldp interface eth2 location elin '0000000911'
# Using deleted
#
# Before state
# -------------
#
# vyos@vyos# run show configuration commands | grep lldp
# set service lldp interface eth2 location elin '0000000911'
#
- name: Delete lldp interface attributes of given interfaces.
vyos.vyos.vyos_lldp_interfaces:
config:
- name: eth2
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
before: [{location: {elin: 0000000911}, name: eth2}]
# "commands": [
# "commands": [
# "delete service lldp interface eth2"
# ]
#
# "after": []
# After state
# ------------
# vyos@vyos# run show configuration commands | grep lldp
# set service 'lldp'
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep lldp
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth1 location civic-based country-code 'US'
# set service lldp interface eth2 location coordinate-based altitude '2200'
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
#
- name: Gather listed lldp interfaces from running configuration
vyos.vyos.vyos_lldp_interfaces:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": [
# {
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth2"
# },
# {
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth1"
# }
# ]
#
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep lldp
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
# set service lldp interface eth1 location civic-based country-code 'US'
# set service lldp interface eth2 location coordinate-based altitude '2200'
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_lldp_interfaces:
config:
- name: eth1
location:
civic_based:
country_code: US
ca_info:
- ca_type: 0
ca_value: ENGLISH
- name: eth2
location:
coordinate_based:
altitude: 2200
datum: WGS84
longitude: 222.267255W
latitude: 33.524449N
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# "set service lldp interface eth1 location civic-based country-code 'US'",
# "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'",
# "set service lldp interface eth1",
# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'",
# "set service lldp interface eth2 location coordinate-based altitude '2200'",
# "set service lldp interface eth2 location coordinate-based datum 'WGS84'",
# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'",
# "set service lldp interface eth2"
# ]
# Using parsed
#
#
- name: Parsed the commands to provide structured configuration.
vyos.vyos.vyos_lldp_interfaces:
running_config:
"set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
set service lldp interface eth1 location civic-based country-code 'US'
set service lldp interface eth2 location coordinate-based altitude '2200'
set service lldp interface eth2 location coordinate-based datum 'WGS84'
set service lldp interface eth2 location coordinate-based latitude '33.524449N'
set service lldp interface eth2 location coordinate-based longitude '222.267255W'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": [
# {
# "location": {
# "coordinate_based": {
# "altitude": 2200,
# "datum": "WGS84",
# "latitude": "33.524449N",
# "longitude": "222.267255W"
# }
# },
# "name": "eth2"
# },
# {
# "location": {
# "civic_based": {
# "ca_info": [
# {
# "ca_type": 0,
# "ca_value": "ENGLISH"
# }
# ],
# "country_code": "US"
# }
# },
# "name": "eth1"
# }
# ]
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- "set service lldp interface eth2 'disable'"
- "delete service lldp interface eth1 location"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import (
Lldp_interfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_interfaces.lldp_interfaces import (
Lldp_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Lldp_interfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Lldp_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_logging.py b/plugins/modules/vyos_logging.py
index 934ec9a..8732647 100644
--- a/plugins/modules/vyos_logging.py
+++ b/plugins/modules/vyos_logging.py
@@ -1,298 +1,297 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
DOCUMENTATION = """
module: vyos_logging
author: Trishna Guha (@trishnaguha)
short_description: Manage logging on network devices
description:
- This module provides declarative management of logging on Vyatta Vyos devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
dest:
description:
- Destination of the logs.
choices:
- console
- file
- global
- host
- user
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.
facility:
description:
- Set logging facility.
level:
description:
- Set logging severity levels.
aggregate:
description: List of logging definitions.
state:
description:
- State of the logging configuration.
default: present
choices:
- present
- absent
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: configure console logging
vyos.vyos.vyos_logging:
dest: console
facility: all
level: crit
- name: remove console logging configuration
vyos.vyos.vyos_logging:
dest: console
state: absent
- name: configure file logging
vyos.vyos.vyos_logging:
dest: file
name: test
facility: local3
level: err
- name: Add logging aggregate
vyos.vyos.vyos_logging:
aggregate:
- {dest: file, name: test1, facility: all, level: info}
- {dest: file, name: test2, facility: news, level: debug}
state: present
- name: Remove logging aggregate
vyos.vyos.vyos_logging:
aggregate:
- {dest: console, facility: all, level: info}
- {dest: console, facility: daemon, level: warning}
- {dest: file, name: test2, facility: news, level: debug}
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- set system syslog global facility all level notice
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def spec_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
dest = w["dest"]
name = w["name"]
facility = w["facility"]
level = w["level"]
state = w["state"]
del w["state"]
if state == "absent" and w in have:
if w["name"]:
commands.append(
"delete system syslog {0} {1} facility {2} level {3}".format(
dest, name, facility, level
)
)
else:
commands.append(
"delete system syslog {0} facility {1} level {2}".format(
dest, facility, level
)
)
elif state == "present" and w not in have:
if w["name"]:
commands.append(
"set system syslog {0} {1} facility {2} level {3}".format(
dest, name, facility, level
)
)
else:
commands.append(
"set system syslog {0} facility {1} level {2}".format(
dest, facility, level
)
)
return commands
def config_to_dict(module):
data = get_config(module)
obj = []
for line in data.split("\n"):
if line.startswith("set system syslog"):
match = re.search(r"set system syslog (\S+)", line, re.M)
dest = match.group(1)
if dest == "host":
match = re.search(r"host (\S+)", line, re.M)
name = match.group(1)
elif dest == "file":
match = re.search(r"file (\S+)", line, re.M)
name = match.group(1)
elif dest == "user":
match = re.search(r"user (\S+)", line, re.M)
name = match.group(1)
else:
name = None
if "facility" in line:
match = re.search(r"facility (\S+)", line, re.M)
facility = match.group(1)
if "level" in line:
match = re.search(r"level (\S+)", line, re.M)
level = match.group(1).strip("'")
obj.append(
{
"dest": dest,
"name": name,
"facility": facility,
"level": level,
}
)
return obj
def map_params_to_obj(module, required_if=None):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
module._check_required_if(required_if, item)
obj.append(item.copy())
else:
if module.params["dest"] not in ("host", "file", "user"):
module.params["name"] = None
obj.append(
{
"dest": module.params["dest"],
"name": module.params["name"],
"facility": module.params["facility"],
"level": module.params["level"],
"state": module.params["state"],
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
dest=dict(
type="str", choices=["console", "file", "global", "host", "user"]
),
name=dict(type="str"),
facility=dict(type="str"),
level=dict(type="str"),
state=dict(default="present", choices=["present", "absent"]),
)
aggregate_spec = deepcopy(element_spec)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_if = [
("dest", "host", ["name", "facility", "level"]),
("dest", "file", ["name", "facility", "level"]),
("dest", "user", ["name", "facility", "level"]),
("dest", "console", ["facility", "level"]),
("dest", "global", ["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
want = map_params_to_obj(module, required_if=required_if)
have = config_to_dict(module)
commands = spec_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py
index 3c2fb8d..f890413 100644
--- a/plugins/modules/vyos_ospfv2.py
+++ b/plugins/modules/vyos_ospfv2.py
@@ -1,1816 +1,1815 @@
#!/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)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_ospfv2
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_ospfv2
short_description: OSPFv2 resource module
description: This resource module configures and manages attributes of OSPFv2 routes
on VyOS network devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: A provided OSPFv2 route configuration.
type: dict
suboptions:
areas:
description: OSPFv2 area.
type: list
elements: dict
suboptions:
area_id:
description: OSPFv2 area identity.
type: str
area_type:
description: Area type.
type: dict
suboptions:
normal:
description: Normal OSPFv2 area.
type: bool
nssa:
description: NSSA OSPFv2 area.
type: dict
suboptions:
set:
description: Enabling NSSA.
type: bool
default_cost:
description: Summary-default cost of NSSA area.
type: int
no_summary:
description: Do not inject inter-area routes into stub.
type: bool
translate:
description: NSSA-ABR.
type: str
choices: [always, candidate, never]
stub:
description: Stub OSPFv2 area.
type: dict
suboptions:
set:
description: Enabling stub.
type: bool
default_cost:
description: Summary-default cost of stub area.
type: int
no_summary:
description: Do not inject inter-area routes into stub.
type: bool
authentication:
description: OSPFv2 area authentication type.
type: str
choices: [plaintext-password, md5]
network:
description: OSPFv2 network.
type: list
elements: dict
suboptions:
address:
required: true
description: OSPFv2 IPv4 network address.
type: str
range:
description: Summarize routes matching prefix (border routers only).
type: list
elements: dict
suboptions:
address:
description: border router IPv4 address.
type: str
cost:
description: Metric for this range.
type: int
not_advertise:
description: Don't advertise this range.
type: bool
substitute:
description: Announce area range (IPv4 address) as another prefix.
type: str
shortcut:
description: Area's shortcut mode.
type: str
choices: [default, disable, enable]
virtual_link:
description: Virtual link address.
type: list
elements: dict
suboptions:
address:
description: virtual link address.
type: str
authentication:
description: OSPFv2 area authentication type.
type: dict
suboptions:
md5:
description: MD5 key id based authentication.
type: list
elements: dict
suboptions:
key_id:
description: MD5 key id.
type: int
md5_key:
description: MD5 key.
type: str
plaintext_password:
description: Plain text password.
type: str
dead_interval:
description: Interval after which a neighbor is declared dead.
type: int
hello_interval:
description: Interval between hello packets.
type: int
retransmit_interval:
description: Interval between retransmitting lost link state advertisements.
type: int
transmit_delay:
description: Link state transmit delay.
type: int
log_adjacency_changes:
description: Log changes in adjacency state.
type: str
choices: [detail]
max_metric:
description: OSPFv2 maximum/infinite-distance metric.
type: dict
suboptions:
router_lsa:
description: Advertise own Router-LSA with infinite distance (stub router).
type: dict
suboptions:
administrative:
description: Administratively apply, for an indefinite period.
type: bool
on_shutdown:
description: Time to advertise self as stub-router.
type: int
on_startup:
description: Time to advertise self as stub-router
type: int
auto_cost:
description: Calculate OSPFv2 interface cost according to bandwidth.
type: dict
suboptions:
reference_bandwidth:
description: Reference bandwidth cost in Mbits/sec.
type: int
default_information:
description: Control distribution of default information.
type: dict
suboptions:
originate:
description: Distribute a default route.
type: dict
suboptions:
always:
description: Always advertise default route.
type: bool
metric:
description: OSPFv2 default metric.
type: int
metric_type:
description: OSPFv2 Metric types for default routes.
type: int
route_map:
description: Route map references.
type: str
default_metric:
description: Metric of redistributed routes
type: int
distance:
description: Administrative distance.
type: dict
suboptions:
global:
description: Global OSPFv2 administrative distance.
type: int
ospf:
description: OSPFv2 administrative distance.
type: dict
suboptions:
external:
description: Distance for external routes.
type: int
inter_area:
description: Distance for inter-area routes.
type: int
intra_area:
description: Distance for intra-area routes.
type: int
mpls_te:
description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters.
type: dict
suboptions:
enabled:
description: Enable MPLS-TE functionality.
type: bool
router_address:
description: Stable IP address of the advertising router.
type: str
neighbor:
description: Neighbor IP address.
type: list
elements: dict
suboptions:
neighbor_id:
description: Identity (number/IP address) of neighbor.
type: str
poll_interval:
description: Seconds between dead neighbor polling interval.
type: int
priority:
description: Neighbor priority.
type: int
parameters:
descriptions: OSPFv2 specific parameters.
type: dict
suboptions:
abr_type:
description: OSPFv2 ABR Type.
type: str
choices: [cisco, ibm, shortcut, standard]
opaque_lsa:
description: Enable the Opaque-LSA capability (rfc2370).
type: bool
rfc1583_compatibility:
description: Enable rfc1583 criteria for handling AS external routes.
type: bool
router_id:
description: Override the default router identifier.
type: str
passive_interface:
description: Suppress routing updates on an interface.
type: list
passive_interface_exclude:
description: Interface to exclude when using passive-interface default.
type: list
redistribute:
description: Redistribute information from another routing protocol.
type: list
elements: dict
suboptions:
route_type:
description: Route type to redistribute.
type: str
choices: [bgp, connected, kernel, rip, static]
metric:
description: Metric for redistribution routes.
type: int
metric_type:
description: OSPFv2 Metric types.
type: int
route_map:
description: Route map references.
type: str
route_map:
description: Filter routes installed in local route map.
type: list
timers:
description: Adjust routing timers.
type: dict
suboptions:
refresh:
description: Adjust refresh parameters.
type: dict
suboptions:
timers:
description: refresh timer.
type: int
throttle:
description: Throttling adaptive timers.
type: dict
suboptions:
spf:
description: OSPFv2 SPF timers.
type: dict
suboptions:
delay:
description: Delay (msec) from first change received till SPF
calculation.
type: int
initial_holdtime:
description: Initial hold time(msec) between consecutive SPF calculations.
type: int
max_holdtime:
description: maximum hold time (sec).
type: int
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep ospf).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- deleted
- parsed
- gathered
- rendered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos# run show configuration commands | grep ospf
#
#
- name: Merge the provided configuration with the existing running configuration
vyos.vyos.vyos_ospfv2:
config:
log_adjacency_changes: detail
max_metric:
router_lsa:
administrative: true
on_shutdown: 10
on_startup: 10
default_information:
originate:
always: true
metric: 10
metric_type: 2
route_map: ingress
mpls_te:
enabled: true
router_address: 192.0.11.11
auto_cost:
reference_bandwidth: 2
neighbor:
- neighbor_id: 192.0.11.12
poll_interval: 10
priority: 2
redistribute:
- route_type: bgp
metric: 10
metric_type: 2
passive_interface:
- eth1
- eth2
parameters:
router_id: 192.0.1.1
opaque_lsa: true
rfc1583_compatibility: true
abr_type: cisco
areas:
- area_id: '2'
area_type:
normal: true
authentication: plaintext-password
shortcut: enable
- area_id: '3'
area_type:
nssa:
set: true
- area_id: '4'
area_type:
stub:
default_cost: 20
network:
- address: 192.0.2.0/24
range:
- address: 192.0.3.0/24
cost: 10
- address: 192.0.4.0/24
cost: 12
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": {}
#
# "commands": [
# "set protocols ospf mpls-te enable",
# "set protocols ospf mpls-te router-address '192.0.11.11'",
# "set protocols ospf redistribute bgp",
# "set protocols ospf redistribute bgp metric-type 2",
# "set protocols ospf redistribute bgp metric 10",
# "set protocols ospf default-information originate metric-type 2",
# "set protocols ospf default-information originate always",
# "set protocols ospf default-information originate metric 10",
# "set protocols ospf default-information originate route-map ingress",
# "set protocols ospf auto-cost reference-bandwidth '2'",
# "set protocols ospf parameters router-id '192.0.1.1'",
# "set protocols ospf parameters opaque-lsa",
# "set protocols ospf parameters abr-type 'cisco'",
# "set protocols ospf parameters rfc1583-compatibility",
# "set protocols ospf passive-interface eth1",
# "set protocols ospf passive-interface eth2",
# "set protocols ospf max-metric router-lsa on-shutdown 10",
# "set protocols ospf max-metric router-lsa administrative",
# "set protocols ospf max-metric router-lsa on-startup 10",
# "set protocols ospf log-adjacency-changes 'detail'",
# "set protocols ospf neighbor 192.0.11.12 priority 2",
# "set protocols ospf neighbor 192.0.11.12 poll-interval 10",
# "set protocols ospf neighbor 192.0.11.12",
# "set protocols ospf area '2'",
# "set protocols ospf area 2 authentication plaintext-password",
# "set protocols ospf area 2 shortcut enable",
# "set protocols ospf area 2 area-type normal",
# "set protocols ospf area '3'",
# "set protocols ospf area 3 area-type nssa",
# "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
# "set protocols ospf area 4 range 192.0.3.0/24",
# "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
# "set protocols ospf area 4 range 192.0.4.0/24",
# "set protocols ospf area 4 area-type stub default-cost 20",
# "set protocols ospf area '4'",
# "set protocols ospf area 4 network 192.0.2.0/24"
# ]
#
# "after": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos# run show configuration commands | grep ospf
#
#
- name: Merge the provided configuration to update existing running configuration
vyos.vyos.vyos_ospfv2:
config:
areas:
- area_id: '2'
area_type:
normal: true
authentication: plaintext-password
shortcut: enable
- area_id: '3'
area_type:
nssa:
set: false
- area_id: '4'
area_type:
stub:
default_cost: 20
network:
- address: 192.0.2.0/24
- address: 192.0.22.0/24
- address: 192.0.32.0/24
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# "commands": [
# "delete protocols ospf area 4 area-type stub",
# "set protocols ospf area 4 network 192.0.22.0/24"
# "set protocols ospf area 4 network 192.0.32.0/24"
# ]
#
# "after": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# },
# {
# "address": "192.0.22.0/24"
# },
# {
# "address": "192.0.32.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 network '192.0.22.0/24'
# set protocols ospf area 4 network '192.0.32.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
# Using replaced
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
#
- name: Replace ospfv2 routes attributes configuration.
vyos.vyos.vyos_ospfv2:
config:
log_adjacency_changes: detail
max_metric:
router_lsa:
administrative: true
on_shutdown: 10
on_startup: 10
default_information:
originate:
always: true
metric: 10
metric_type: 2
route_map: ingress
mpls_te:
enabled: true
router_address: 192.0.22.22
auto_cost:
reference_bandwidth: 2
neighbor:
- neighbor_id: 192.0.11.12
poll_interval: 10
priority: 2
redistribute:
- route_type: bgp
metric: 10
metric_type: 2
passive_interface:
- eth1
parameters:
router_id: 192.0.1.1
opaque_lsa: true
rfc1583_compatibility: true
abr_type: cisco
areas:
- area_id: '2'
area_type:
normal: true
authentication: plaintext-password
shortcut: enable
- area_id: '4'
area_type:
stub:
default_cost: 20
network:
- address: 192.0.2.0/24
- address: 192.0.12.0/24
- address: 192.0.22.0/24
- address: 192.0.32.0/24
range:
- address: 192.0.42.0/24
cost: 10
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# "commands": [
# "delete protocols ospf passive-interface eth2",
# "delete protocols ospf area 3",
# "delete protocols ospf area 4 range 192.0.3.0/24 cost",
# "delete protocols ospf area 4 range 192.0.3.0/24",
# "delete protocols ospf area 4 range 192.0.4.0/24 cost",
# "delete protocols ospf area 4 range 192.0.4.0/24",
# "set protocols ospf mpls-te router-address '192.0.22.22'",
# "set protocols ospf area 4 range 192.0.42.0/24 cost 10",
# "set protocols ospf area 4 range 192.0.42.0/24",
# "set protocols ospf area 4 network 192.0.12.0/24",
# "set protocols ospf area 4 network 192.0.22.0/24",
# "set protocols ospf area 4 network 192.0.32.0/24"
# ]
#
# "after": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.12.0/24"
# },
# {
# "address": "192.0.2.0/24"
# },
# {
# "address": "192.0.22.0/24"
# },
# {
# "address": "192.0.32.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.42.0/24",
# "cost": 10
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.22.22"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 network '192.0.12.0/24'
# set protocols ospf area 4 network '192.0.22.0/24'
# set protocols ospf area 4 network '192.0.32.0/24'
# set protocols ospf area 4 range 192.0.42.0/24 cost '10'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.22.22'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_ospfv2:
config:
log_adjacency_changes: detail
max_metric:
router_lsa:
administrative: true
on_shutdown: 10
on_startup: 10
default_information:
originate:
always: true
metric: 10
metric_type: 2
route_map: ingress
mpls_te:
enabled: true
router_address: 192.0.11.11
auto_cost:
reference_bandwidth: 2
neighbor:
- neighbor_id: 192.0.11.12
poll_interval: 10
priority: 2
redistribute:
- route_type: bgp
metric: 10
metric_type: 2
passive_interface:
- eth1
- eth2
parameters:
router_id: 192.0.1.1
opaque_lsa: true
rfc1583_compatibility: true
abr_type: cisco
areas:
- area_id: '2'
area_type:
normal: true
authentication: plaintext-password
shortcut: enable
- area_id: '3'
area_type:
nssa:
set: true
- area_id: '4'
area_type:
stub:
default_cost: 20
network:
- address: 192.0.2.0/24
range:
- address: 192.0.3.0/24
cost: 10
- address: 192.0.4.0/24
cost: 12
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# [
# "set protocols ospf mpls-te enable",
# "set protocols ospf mpls-te router-address '192.0.11.11'",
# "set protocols ospf redistribute bgp",
# "set protocols ospf redistribute bgp metric-type 2",
# "set protocols ospf redistribute bgp metric 10",
# "set protocols ospf default-information originate metric-type 2",
# "set protocols ospf default-information originate always",
# "set protocols ospf default-information originate metric 10",
# "set protocols ospf default-information originate route-map ingress",
# "set protocols ospf auto-cost reference-bandwidth '2'",
# "set protocols ospf parameters router-id '192.0.1.1'",
# "set protocols ospf parameters opaque-lsa",
# "set protocols ospf parameters abr-type 'cisco'",
# "set protocols ospf parameters rfc1583-compatibility",
# "set protocols ospf passive-interface eth1",
# "set protocols ospf passive-interface eth2",
# "set protocols ospf max-metric router-lsa on-shutdown 10",
# "set protocols ospf max-metric router-lsa administrative",
# "set protocols ospf max-metric router-lsa on-startup 10",
# "set protocols ospf log-adjacency-changes 'detail'",
# "set protocols ospf neighbor 192.0.11.12 priority 2",
# "set protocols ospf neighbor 192.0.11.12 poll-interval 10",
# "set protocols ospf neighbor 192.0.11.12",
# "set protocols ospf area '2'",
# "set protocols ospf area 2 authentication plaintext-password",
# "set protocols ospf area 2 shortcut enable",
# "set protocols ospf area 2 area-type normal",
# "set protocols ospf area '3'",
# "set protocols ospf area 3 area-type nssa",
# "set protocols ospf area 4 range 192.0.3.0/24 cost 10",
# "set protocols ospf area 4 range 192.0.3.0/24",
# "set protocols ospf area 4 range 192.0.4.0/24 cost 12",
# "set protocols ospf area 4 range 192.0.4.0/24",
# "set protocols ospf area 4 area-type stub default-cost 20",
# "set protocols ospf area '4'",
# "set protocols ospf area 4 network 192.0.2.0/24"
# ]
# Using parsed
#
#
- name: Parse the commands for provided structured configuration
vyos.vyos.vyos_ospfv2:
running_config:
"set protocols ospf area 2 area-type 'normal'
set protocols ospf area 2 authentication 'plaintext-password'
set protocols ospf area 2 shortcut 'enable'
set protocols ospf area 3 area-type 'nssa'
set protocols ospf area 4 area-type stub default-cost '20'
set protocols ospf area 4 network '192.0.2.0/24'
set protocols ospf area 4 range 192.0.3.0/24 cost '10'
set protocols ospf area 4 range 192.0.4.0/24 cost '12'
set protocols ospf auto-cost reference-bandwidth '2'
set protocols ospf default-information originate 'always'
set protocols ospf default-information originate metric '10'
set protocols ospf default-information originate metric-type '2'
set protocols ospf default-information originate route-map 'ingress'
set protocols ospf log-adjacency-changes 'detail'
set protocols ospf max-metric router-lsa 'administrative'
set protocols ospf max-metric router-lsa on-shutdown '10'
set protocols ospf max-metric router-lsa on-startup '10'
set protocols ospf mpls-te 'enable'
set protocols ospf mpls-te router-address '192.0.11.11'
set protocols ospf neighbor 192.0.11.12 poll-interval '10'
set protocols ospf neighbor 192.0.11.12 priority '2'
set protocols ospf parameters abr-type 'cisco'
set protocols ospf parameters 'opaque-lsa'
set protocols ospf parameters 'rfc1583-compatibility'
set protocols ospf parameters router-id '192.0.1.1'
set protocols ospf passive-interface 'eth1'
set protocols ospf passive-interface 'eth2'
set protocols ospf redistribute bgp metric '10'
set protocols ospf redistribute bgp metric-type '2'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
# }
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
#
- name: Gather ospfv2 routes config with provided configurations
vyos.vyos.vyos_ospfv2:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
# Using deleted
#
# Before state
# -------------
#
# vyos@192# run show configuration commands | grep ospf
# set protocols ospf area 2 area-type 'normal'
# set protocols ospf area 2 authentication 'plaintext-password'
# set protocols ospf area 2 shortcut 'enable'
# set protocols ospf area 3 area-type 'nssa'
# set protocols ospf area 4 area-type stub default-cost '20'
# set protocols ospf area 4 network '192.0.2.0/24'
# set protocols ospf area 4 range 192.0.3.0/24 cost '10'
# set protocols ospf area 4 range 192.0.4.0/24 cost '12'
# set protocols ospf auto-cost reference-bandwidth '2'
# set protocols ospf default-information originate 'always'
# set protocols ospf default-information originate metric '10'
# set protocols ospf default-information originate metric-type '2'
# set protocols ospf default-information originate route-map 'ingress'
# set protocols ospf log-adjacency-changes 'detail'
# set protocols ospf max-metric router-lsa 'administrative'
# set protocols ospf max-metric router-lsa on-shutdown '10'
# set protocols ospf max-metric router-lsa on-startup '10'
# set protocols ospf mpls-te 'enable'
# set protocols ospf mpls-te router-address '192.0.11.11'
# set protocols ospf neighbor 192.0.11.12 poll-interval '10'
# set protocols ospf neighbor 192.0.11.12 priority '2'
# set protocols ospf parameters abr-type 'cisco'
# set protocols ospf parameters 'opaque-lsa'
# set protocols ospf parameters 'rfc1583-compatibility'
# set protocols ospf parameters router-id '192.0.1.1'
# set protocols ospf passive-interface 'eth1'
# set protocols ospf passive-interface 'eth2'
# set protocols ospf redistribute bgp metric '10'
# set protocols ospf redistribute bgp metric-type '2'
#
- name: Delete attributes of ospfv2 routes.
vyos.vyos.vyos_ospfv2:
config:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": {
# "areas": [
# {
# "area_id": "2",
# "area_type": {
# "normal": true
# },
# "authentication": "plaintext-password",
# "shortcut": "enable"
# },
# {
# "area_id": "3",
# "area_type": {
# "nssa": {
# "set": true
# }
# }
# },
# {
# "area_id": "4",
# "area_type": {
# "stub": {
# "default_cost": 20,
# "set": true
# }
# },
# "network": [
# {
# "address": "192.0.2.0/24"
# }
# ],
# "range": [
# {
# "address": "192.0.3.0/24",
# "cost": 10
# },
# {
# "address": "192.0.4.0/24",
# "cost": 12
# }
# ]
# }
# ],
# "auto_cost": {
# "reference_bandwidth": 2
# },
# "default_information": {
# "originate": {
# "always": true,
# "metric": 10,
# "metric_type": 2,
# "route_map": "ingress"
# }
# },
# "log_adjacency_changes": "detail",
# "max_metric": {
# "router_lsa": {
# "administrative": true,
# "on_shutdown": 10,
# "on_startup": 10
# }
# },
# "mpls_te": {
# "enabled": true,
# "router_address": "192.0.11.11"
# },
# "neighbor": [
# {
# "neighbor_id": "192.0.11.12",
# "poll_interval": 10,
# "priority": 2
# }
# ],
# "parameters": {
# "abr_type": "cisco",
# "opaque_lsa": true,
# "rfc1583_compatibility": true,
# "router_id": "192.0.1.1"
# },
# "passive_interface": [
# "eth2",
# "eth1"
# ],
# "redistribute": [
# {
# "metric": 10,
# "metric_type": 2,
# "route_type": "bgp"
# }
# ]
# }
# "commands": [
# "delete protocols ospf"
# ]
#
# "after": {}
# After state
# ------------
# vyos@192# run show configuration commands | grep ospf
#
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- "set protocols ospf parameters router-id 192.0.1.1"
- "set protocols ospf passive-interface 'eth1'"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import (
Ospfv2Args,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import (
Ospfv2,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Ospfv2Args.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Ospfv2(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_ospfv3.py b/plugins/modules/vyos_ospfv3.py
index 1833e13..20296a0 100644
--- a/plugins/modules/vyos_ospfv3.py
+++ b/plugins/modules/vyos_ospfv3.py
@@ -1,668 +1,667 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# pylint: skip-file
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for vyos_ospfv3
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_ospfv3
short_description: OSPFV3 resource module
description: This resource module configures and manages attributes of OSPFv3 routes
on VyOS network devices.
version_added: 1.0.0
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
author:
- Rohit Thakur (@rohitthakur2590)
options:
config:
description: A provided OSPFv3 route configuration.
type: dict
suboptions:
areas:
description: OSPFv3 area.
type: list
elements: dict
suboptions:
area_id:
description: OSPFv3 Area name/identity.
type: str
export_list:
description: Name of export-list.
type: str
import_list:
description: Name of import-list.
type: str
range:
description: Summarize routes matching prefix (border routers only).
type: list
elements: dict
suboptions:
address:
description: border router IPv4 address.
type: str
advertise:
description: Advertise this range.
type: bool
not_advertise:
description: Don't advertise this range.
type: bool
parameters:
descriptions: OSPFv3 specific parameters.
type: dict
suboptions:
router_id:
description: Override the default router identifier.
type: str
redistribute:
description: Redistribute information from another routing protocol.
type: list
elements: dict
suboptions:
route_type:
description: Route type to redistribute.
type: str
choices: [bgp, connected, kernel, ripng, static]
route_map:
description: Route map references.
type: str
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the VyOS device
by executing the command B(show configuration commands | grep ospfv3).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- deleted
- parsed
- gathered
- rendered
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos# run show configuration commands | grep ospfv3
#
#
- name: Merge the provided configuration with the exisiting running configuration
vyos.vyos.vyos_ospfv3:
config:
redistribute:
- route_type: bgp
parameters:
router_id: 192.0.2.10
areas:
- area_id: '2'
export_list: export1
import_list: import1
range:
- address: 2001:db10::/32
- address: 2001:db20::/32
- address: 2001:db30::/32
- area_id: '3'
range:
- address: 2001:db40::/32
state: merged
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# before": {}
#
# "commands": [
# "set protocols ospfv3 redistribute bgp",
# "set protocols ospfv3 parameters router-id '192.0.2.10'",
# "set protocols ospfv3 area 2 range 2001:db10::/32",
# "set protocols ospfv3 area 2 range 2001:db20::/32",
# "set protocols ospfv3 area 2 range 2001:db30::/32",
# "set protocols ospfv3 area '2'",
# "set protocols ospfv3 area 2 export-list export1",
# "set protocols ospfv3 area 2 import-list import1",
# "set protocols ospfv3 area '3'",
# "set protocols ospfv3 area 3 range 2001:db40::/32"
# ]
#
# "after": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db20::/32"
# },
# {
# "address": "2001:db30::/32"
# }
# ]
# },
# {
# "area_id": "3",
# "range": [
# {
# "address": "2001:db40::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db20::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 3 range '2001:db40::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
# Using replaced
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db20::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 3 range '2001:db40::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
#
- name: Replace ospfv3 routes attributes configuration.
vyos.vyos.vyos_ospfv3:
config:
redistribute:
- route_type: bgp
parameters:
router_id: 192.0.2.10
areas:
- area_id: '2'
export_list: export1
import_list: import1
range:
- address: 2001:db10::/32
- address: 2001:db30::/32
- address: 2001:db50::/32
- area_id: '4'
range:
- address: 2001:db60::/32
state: replaced
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "before": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db20::/32"
# },
# {
# "address": "2001:db30::/32"
# }
# ]
# },
# {
# "area_id": "3",
# "range": [
# {
# "address": "2001:db40::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
#
# "commands": [
# "delete protocols ospfv3 area 2 range 2001:db20::/32",
# "delete protocols ospfv3 area 3",
# "set protocols ospfv3 area 2 range 2001:db50::/32",
# "set protocols ospfv3 area '4'",
# "set protocols ospfv3 area 4 range 2001:db60::/32"
# ]
#
# "after": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db30::/32"
# },
# {
# "address": "2001:db50::/32"
# }
# ]
# },
# {
# "area_id": "4",
# "range": [
# {
# "address": "2001:db60::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 2 range '2001:db50::/32'
# set protocols ospfv3 area 4 range '2001:db60::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
# Using rendered
#
#
- name: Render the commands for provided configuration
vyos.vyos.vyos_ospfv3:
config:
redistribute:
- route_type: bgp
parameters:
router_id: 192.0.2.10
areas:
- area_id: '2'
export_list: export1
import_list: import1
range:
- address: 2001:db10::/32
- address: 2001:db20::/32
- address: 2001:db30::/32
- area_id: '3'
range:
- address: 2001:db40::/32
state: rendered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "rendered": [
# [
# "set protocols ospfv3 redistribute bgp",
# "set protocols ospfv3 parameters router-id '192.0.2.10'",
# "set protocols ospfv3 area 2 range 2001:db10::/32",
# "set protocols ospfv3 area 2 range 2001:db20::/32",
# "set protocols ospfv3 area 2 range 2001:db30::/32",
# "set protocols ospfv3 area '2'",
# "set protocols ospfv3 area 2 export-list export1",
# "set protocols ospfv3 area 2 import-list import1",
# "set protocols ospfv3 area '3'",
# "set protocols ospfv3 area 3 range 2001:db40::/32"
# ]
# Using parsed
#
#
- name: Parse the commands to provide structured configuration.
vyos.vyos.vyos_ospfv3:
running_config:
"set protocols ospfv3 area 2 export-list 'export1'
set protocols ospfv3 area 2 import-list 'import1'
set protocols ospfv3 area 2 range '2001:db10::/32'
set protocols ospfv3 area 2 range '2001:db20::/32'
set protocols ospfv3 area 2 range '2001:db30::/32'
set protocols ospfv3 area 3 range '2001:db40::/32'
set protocols ospfv3 parameters router-id '192.0.2.10'
set protocols ospfv3 redistribute 'bgp'"
state: parsed
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
#
# "parsed": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db20::/32"
# },
# {
# "address": "2001:db30::/32"
# }
# ]
# },
# {
# "area_id": "3",
# "range": [
# {
# "address": "2001:db40::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
# Using gathered
#
# Before state:
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db20::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 3 range '2001:db40::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
#
- name: Gather ospfv3 routes config with provided configurations
vyos.vyos.vyos_ospfv3:
config:
state: gathered
#
#
# -------------------------
# Module Execution Result
# -------------------------
#
# "gathered": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db20::/32"
# },
# {
# "address": "2001:db30::/32"
# }
# ]
# },
# {
# "area_id": "3",
# "range": [
# {
# "address": "2001:db40::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
#
# After state:
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db20::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 3 range '2001:db40::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
# Using deleted
#
# Before state
# -------------
#
# vyos@192# run show configuration commands | grep ospfv3
# set protocols ospfv3 area 2 export-list 'export1'
# set protocols ospfv3 area 2 import-list 'import1'
# set protocols ospfv3 area 2 range '2001:db10::/32'
# set protocols ospfv3 area 2 range '2001:db20::/32'
# set protocols ospfv3 area 2 range '2001:db30::/32'
# set protocols ospfv3 area 3 range '2001:db40::/32'
# set protocols ospfv3 parameters router-id '192.0.2.10'
# set protocols ospfv3 redistribute 'bgp'
#
- name: Delete attributes of ospfv3 routes.
vyos.vyos.vyos_ospfv3:
config:
state: deleted
#
#
# ------------------------
# Module Execution Results
# ------------------------
#
# "before": {
# "areas": [
# {
# "area_id": "2",
# "export_list": "export1",
# "import_list": "import1",
# "range": [
# {
# "address": "2001:db10::/32"
# },
# {
# "address": "2001:db20::/32"
# },
# {
# "address": "2001:db30::/32"
# }
# ]
# },
# {
# "area_id": "3",
# "range": [
# {
# "address": "2001:db40::/32"
# }
# ]
# }
# ],
# "parameters": {
# "router_id": "192.0.2.10"
# },
# "redistribute": [
# {
# "route_type": "bgp"
# }
# ]
# }
# "commands": [
# "delete protocols ospfv3"
# ]
#
# "after": {}
# After state
# ------------
# vyos@192# run show configuration commands | grep ospfv3
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- "set protocols ospf parameters router-id 192.0.1.1"
- "set protocols ospfv3 area 2 range '2001:db10::/32'"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import (
Ospfv3Args,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import (
Ospfv3,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "rendered", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Ospfv3Args.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
)
result = Ospfv3(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_ping.py b/plugins/modules/vyos_ping.py
index 9bd31a5..9131917 100644
--- a/plugins/modules/vyos_ping.py
+++ b/plugins/modules/vyos_ping.py
@@ -1,260 +1,259 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-# pylint: skip-file
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_ping
short_description: Tests reachability using ping from VyOS network devices
description:
- Tests reachability using ping from a VyOS device to a remote destination.
- Tested against VyOS 1.1.8 (helium)
- For a general purpose network module, see the M(net_ping) module.
- For Windows targets, use the M(win_ping) module instead.
- For targets running Python, use the M(ping) module instead.
version_added: 1.0.0
author:
- Nilashish Chakraborty (@NilashishC)
options:
dest:
description:
- The IP Address or hostname (resolvable by the device) of the remote node.
required: true
count:
description:
- Number of packets to send to check reachability.
type: int
default: 5
source:
description:
- The source interface or IP Address to use while sending the ping packet(s).
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.
choices:
- absent
- present
default: present
notes:
- Tested against VyOS 1.1.8 (helium).
- For a general purpose network module, see the M(net_ping) module.
- For Windows targets, use the M(win_ping) module instead.
- For targets running Python, use the M(ping) module instead.
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
extends_documentation_fragment:
- vyos.vyos.vyos
"""
EXAMPLES = """
- name: Test reachability to 10.10.10.10
vyos.vyos.vyos_ping:
dest: 10.10.10.10
- name: Test reachability to 10.20.20.20 using source and ttl set
vyos.vyos.vyos_ping:
dest: 10.20.20.20
source: eth0
ttl: 128
- name: Test unreachability to 10.30.30.30 using interval
vyos.vyos.vyos_ping:
dest: 10.30.30.30
interval: 3
state: absent
- name: Test reachability to 10.40.40.40 setting count and source
vyos.vyos.vyos_ping:
dest: 10.40.40.40
source: eth1
count: 20
size: 512
"""
RETURN = """
commands:
description: List of commands sent.
returned: always
type: list
sample: ["ping 10.8.38.44 count 10 interface eth0 ttl 128"]
packet_loss:
description: Percentage of packets lost.
returned: always
type: str
sample: "0%"
packets_rx:
description: Packets successfully received.
returned: always
type: int
sample: 20
packets_tx:
description: Packets successfully transmitted.
returned: always
type: int
sample: 20
rtt:
description: The round trip time (RTT) stats.
returned: when ping succeeds
type: dict
sample: {"avg": 2, "max": 8, "min": 1, "mdev": 24}
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
import re
def main():
""" main entry point for module execution
"""
argument_spec = dict(
count=dict(type="int", default=5),
dest=dict(type="str", required=True),
source=dict(type="str"),
ttl=dict(type="int"),
size=dict(type="int"),
interval=dict(type="int"),
state=dict(
type="str", choices=["absent", "present"], default="present"
),
)
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(argument_spec=argument_spec)
count = module.params["count"]
dest = module.params["dest"]
source = module.params["source"]
size = module.params["size"]
ttl = module.params["ttl"]
interval = module.params["interval"]
warnings = list()
results = {}
if warnings:
results["warnings"] = warnings
results["commands"] = [
build_ping(dest, count, size, interval, source, ttl)
]
ping_results = run_commands(module, commands=results["commands"])
ping_results_list = ping_results[0].split("\n")
rtt_info, rate_info = None, None
for line in ping_results_list:
if line.startswith("rtt"):
rtt_info = line
if line.startswith("%s packets transmitted" % count):
rate_info = line
if rtt_info:
rtt = parse_rtt(rtt_info)
for k, v in rtt.items():
if rtt[k] is not None:
rtt[k] = int(v)
results["rtt"] = rtt
pkt_loss, rx, tx = parse_rate(rate_info)
results["packet_loss"] = str(pkt_loss) + "%"
results["packets_rx"] = int(rx)
results["packets_tx"] = int(tx)
validate_results(module, pkt_loss, results)
module.exit_json(**results)
def build_ping(dest, count, size=None, interval=None, source=None, ttl=None):
cmd = "ping {0} count {1}".format(dest, str(count))
if source:
cmd += " interface {0}".format(source)
if ttl:
cmd += " ttl {0}".format(str(ttl))
if size:
cmd += " size {0}".format(str(size))
if interval:
cmd += " interval {0}".format(str(interval))
return cmd
def parse_rate(rate_info):
rate_re = re.compile(
r"(?P\d+) (?:\w+) (?:\w+), (?P\d+) (?:\w+), (?P\d+)% (?:\w+) (?:\w+), (?:\w+) (?P