summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaptTrews <capttrews@gmail.com>2020-02-19 19:31:27 +0000
committerCaptTrews <capttrews@gmail.com>2020-02-19 19:31:27 +0000
commit45f223636c73ba69d3fea3c8aab8edd41de01388 (patch)
treec5ff33aa0e4770a13340a52b1e2bda7531541a5f
parentf63b5c97edbf598f7b2a4c044386de3dddfda100 (diff)
downloadvyos-ansible-collection-45f223636c73ba69d3fea3c8aab8edd41de01388.tar.gz
vyos-ansible-collection-45f223636c73ba69d3fea3c8aab8edd41de01388.zip
Updated from network content collector
Signed-off-by: CaptTrews <capttrews@gmail.com>
-rw-r--r--plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py263
-rw-r--r--plugins/module_utils/network/vyos/argspec/static_routes/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py99
-rw-r--r--plugins/module_utils/network/vyos/config/firewall_rules/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py1034
-rw-r--r--plugins/module_utils/network/vyos/config/static_routes/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/static_routes/static_routes.py627
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py9
-rw-r--r--plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py380
-rw-r--r--plugins/module_utils/network/vyos/facts/static_routes/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/static_routes/static_routes.py181
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py51
-rw-r--r--plugins/modules/vyos_facts.py2
-rw-r--r--plugins/modules/vyos_firewall_rules.py1565
-rw-r--r--plugins/modules/vyos_static_route.py12
-rw-r--r--plugins/modules/vyos_static_routes.py1156
-rw-r--r--tests/integration/targets/vyos_firewall_rules/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_firewall_rules/meta/main.yaml3
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tasks/cli.yaml19
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tasks/main.yaml4
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/_parsed_config.cfg25
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/_populate.yaml27
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/_remove_config.yaml6
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/deleted.yaml60
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_afi.yaml54
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_all.yaml50
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_rule.yaml58
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/empty_config.yaml60
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/gathered.yaml34
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/merged.yaml102
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/overridden.yaml69
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/parsed.yaml41
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/rendered.yaml73
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/replaced.yaml78
-rw-r--r--tests/integration/targets/vyos_firewall_rules/tests/cli/rtt.yaml101
-rw-r--r--tests/integration/targets/vyos_firewall_rules/vars/main.yaml312
-rw-r--r--tests/integration/targets/vyos_static_routes/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_static_routes/meta/main.yaml2
-rw-r--r--tests/integration/targets/vyos_static_routes/tasks/cli.yaml19
-rw-r--r--tests/integration/targets/vyos_static_routes/tasks/main.yaml4
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg6
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/_populate.yaml12
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml6
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/deleted.yaml62
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml56
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml50
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml68
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml60
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/gathered.yaml34
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/merged.yaml78
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/overridden.yaml61
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/parsed.yaml41
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/rendered.yaml62
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/replaced.yaml69
-rw-r--r--tests/integration/targets/vyos_static_routes/tests/cli/rtt.yaml90
-rw-r--r--tests/integration/targets/vyos_static_routes/vars/main.yaml147
-rw-r--r--tests/sanity/ignore-2.10.txt10
-rw-r--r--tests/sanity/ignore-2.9.txt10
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg13
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_static_routes_config.cfg2
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_firewall_rules.py1039
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_static_route.py8
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_static_routes.py293
65 files changed, 8836 insertions, 27 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py
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
new file mode 100644
index 0000000..a018cc0
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
@@ -0,0 +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)
+
+#############################################
+# 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/static_routes/__init__.py b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py
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
new file mode 100644
index 0000000..8ecd955
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py
@@ -0,0 +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)
+
+#############################################
+# 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_rules/__init__.py b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py
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
new file mode 100644
index 0000000..e58593f
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
@@ -0,0 +1,1034 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The vyos_firewall_rules class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__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:
+ w_rules = rs.get("rules") or []
+ h_rules = h.get("rules") or []
+ if w_rules and h_rules:
+ for rule in w_rules:
+ if self.search_r_sets_in_have(
+ h_rules, rule["number"], "rules"
+ ):
+ commands.append(
+ self._add_r_base_attrib(
+ w["afi"],
+ rs["name"],
+ "number",
+ rule,
+ opr=False,
+ )
+ )
+ else:
+ 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/static_routes/__init__.py b/plugins/module_utils/network/vyos/config/static_routes/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/static_routes/__init__.py
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
new file mode 100644
index 0000000..e93d4ee
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py
@@ -0,0 +1,627 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The vyos_static_routes class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ 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 == "merged" or self.state == "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
+ )
+ )
+ for r in routes:
+ h_route = self.search_route_in_have(have, r["dest"])
+ if h_route:
+ commands.extend(
+ self._render_updates(r, h_route, opr=False)
+ )
+ 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 b5816c2..8f0a3bb 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -27,6 +27,12 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_
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.legacy.base import (
Default,
Neighbors,
@@ -41,6 +47,8 @@ FACT_RESOURCE_SUBSETS = dict(
lag_interfaces=Lag_interfacesFacts,
lldp_global=Lldp_globalFacts,
lldp_interfaces=Lldp_interfacesFacts,
+ static_routes=Static_routesFacts,
+ firewall_rules=Firewall_rulesFacts,
)
@@ -72,5 +80,4 @@ class Facts(FactsBase):
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_rules/__init__.py b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py
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
new file mode 100644
index 0000000..971ea6f
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
@@ -0,0 +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)
+"""
+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/static_routes/__init__.py b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py
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
new file mode 100644
index 0000000..0004947
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py
@@ -0,0 +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)
+"""
+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 6504bcd..402adfc 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -8,6 +8,9 @@ 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"):
@@ -87,6 +90,27 @@ def get_lst_diff_for_dicts(want, have, lst):
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
@@ -178,3 +202,30 @@ def is_dict_element_present(dict, key):
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"
diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py
index 9eaa278..19fb727 100644
--- a/plugins/modules/vyos_facts.py
+++ b/plugins/modules/vyos_facts.py
@@ -47,7 +47,7 @@ options:
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'.
+ 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules'.
required: false
"""
diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py
new file mode 100644
index 0000000..a9e676b
--- /dev/null
+++ b/plugins/modules/vyos_firewall_rules.py
@@ -0,0 +1,1565 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for vyos_firewall_rules
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["preview"],
+ "supported_by": "network",
+}
+
+DOCUMENTATION = """module: vyos_firewall_rules
+short_description: Manage firewall rule-set attributes on VyOS devices
+description: This module manages firewall rule-set attributes on VyOS devices
+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).
+ - <text> 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> MAC address to match.
+ - <!MAC address> 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:
+ - 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
+ - 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_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 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_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 deleted to delete the the firewall rules based on afi
+#
+# 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_firewall_rules:
+ config:
+ - afi: ipv4
+ 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 deleted to delete the the firewall rules based on rule number/id
+#
+# 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_firewall_rules:
+ config:
+ - afi: ipv4
+ rule_sets:
+ - name: 'Downlink'
+ rules:
+ - number: 501
+ 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 ipv6-name Downlink rule 501"
+# ]
+#
+# "after": [
+# {
+# "afi": "ipv4",
+# "rule_sets": [
+# {
+# "default_action": "accept",
+# "description": "IPv4 INBOUND rule set",
+# "name": "Downlink",
+# "rules": [
+# {
+# "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 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 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_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_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_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_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_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: Render the commands for provided configuration
+ 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", "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_static_route.py b/plugins/modules/vyos_static_route.py
index e0c40e7..af9a1e3 100644
--- a/plugins/modules/vyos_static_route.py
+++ b/plugins/modules/vyos_static_route.py
@@ -21,7 +21,7 @@
ANSIBLE_METADATA = {
"metadata_version": "1.1",
- "status": ["preview"],
+ "status": ["deprecated"],
"supported_by": "network",
}
@@ -32,6 +32,10 @@ short_description: Manage static IP routes on Vyatta VyOS network devices
description:
- This module provides declarative management of static IP routes on Vyatta VyOS network
devices.
+deprecated:
+ removed_in: '2.13'
+ alternative: vyos_static_routes
+ why: Updated modules released with more functionality.
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).
@@ -40,17 +44,22 @@ options:
description:
- Network prefix of the static route. C(mask) param should be ignored if C(prefix)
is provided with C(mask) value C(prefix/mask).
+ type: str
mask:
description:
- Network prefix mask of the static route.
+ type: str
next_hop:
description:
- Next hop IP of the static route.
+ type: str
admin_distance:
description:
- Admin distance of the static route.
+ type: int
aggregate:
description: List of static route definitions
+ type: list
state:
description:
- State of the static route configuration.
@@ -58,6 +67,7 @@ options:
choices:
- present
- absent
+ type: str
extends_documentation_fragment:
- vyos.vyos.vyos
"""
diff --git a/plugins/modules/vyos_static_routes.py b/plugins/modules/vyos_static_routes.py
new file mode 100644
index 0000000..6e50203
--- /dev/null
+++ b/plugins/modules/vyos_static_routes.py
@@ -0,0 +1,1156 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for vyos_static_routes
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["preview"],
+ "supported_by": "network",
+}
+
+DOCUMENTATION = """module: vyos_static_routes
+short_description: Manages attributes of static routes on VyOS network devices.
+description: This module manages attributes of static routes on VyOS network devices.
+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 static route configuration.
+ type: list
+ elements: dict
+ suboptions:
+ address_families:
+ description: A dictionary specifying the address family to which the static
+ route(s) belong.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Specifies the type of route.
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ required: true
+ routes:
+ description: A ditionary that specify the static route configurations.
+ type: list
+ elements: dict
+ suboptions:
+ dest:
+ description:
+ - An IPv4/v6 address in CIDR notation that specifies the destination
+ network for the static route.
+ type: str
+ required: true
+ blackhole_config:
+ description:
+ - Configured to silently discard packets.
+ type: dict
+ suboptions:
+ type:
+ description:
+ - This is to configure only blackhole.
+ type: str
+ distance:
+ description:
+ - Distance for the route.
+ type: int
+ next_hops:
+ description:
+ - Next hops to the specified destination.
+ type: list
+ elements: dict
+ suboptions:
+ forward_router_address:
+ description:
+ - The IP address of the next hop that can be used to reach the
+ destination network.
+ type: str
+ required: true
+ enabled:
+ description:
+ - Disable IPv4/v6 next-hop static route.
+ type: bool
+ admin_distance:
+ description:
+ - Distance value for the route.
+ type: int
+ interface:
+ description:
+ - Name of the outgoing interface.
+ type: str
+ 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 'static route')
+ type: str
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+#
+# Before state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands | grep static
+#
+- name: Merge the provided configuration with the exisiting running configuration
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: 'blackhole'
+ next_hops:
+ - forward_router_address: 192.0.2.6
+ - forward_router_address: 192.0.2.7
+ - address_families:
+ - afi: 'ipv6'
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
+ state: merged
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# before": []
+#
+# "commands": [
+# "set protocols static route 192.0.2.32/28",
+# "set protocols static route 192.0.2.32/28 blackhole",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'",
+# "set protocols static route6 2001:db8:1000::/36",
+# "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'",
+# "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'",
+# "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'"
+# ]
+#
+# "after": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+# After state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+
+
+# Using replaced
+#
+# Before state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route 192.0.2.33/28 'blackhole'
+# set protocols static route 192.0.2.33/28 next-hop '192.0.2.3'
+# set protocols static route 192.0.2.33/28 next-hop '192.0.2.4'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Replace device configurations of listed static routes with provided configurations
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 192.0.2.7
+ enabled: false
+ - forward_router_address: 192.0.2.9
+ state: replaced
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# },
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.33/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.3"
+# },
+# {
+# "forward_router_address": "192.0.2.4"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+# "commands": [
+# "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.6'",
+# "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.7'",
+# "set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable'",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'",
+# "set protocols static route 192.0.2.32/28 blackhole distance '2'"
+# ]
+#
+# "after": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "enabled": false,
+# "forward_router_address": "192.0.2.7"
+# },
+# {
+# "forward_router_address": "192.0.2.9"
+# }
+# ]
+# },
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.33/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.3"
+# },
+# {
+# "forward_router_address": "192.0.2.4"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+# After state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 blackhole distance '2'
+# set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+# set protocols static route 192.0.2.33/28 'blackhole'
+# set protocols static route 192.0.2.33/28 next-hop '192.0.2.3'
+# set protocols static route 192.0.2.33/28 next-hop '192.0.2.4'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+
+
+# Using overridden
+#
+# Before state
+# --------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 blackhole distance '2'
+# set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Overrides all device configuration with provided configuration
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: 198.0.2.48/28
+ next_hops:
+ - forward_router_address: 192.0.2.18
+ state: overridden
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "enabled": false,
+# "forward_router_address": "192.0.2.7"
+# },
+# {
+# "forward_router_address": "192.0.2.9"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+# "commands": [
+# "delete protocols static route 192.0.2.32/28",
+# "delete protocols static route6 2001:db8:1000::/36",
+# "set protocols static route 198.0.2.48/28",
+# "set protocols static route 198.0.2.48/28 next-hop '192.0.2.18'"
+#
+#
+# "after": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "dest": "198.0.2.48/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.18"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+#
+# After state
+# ------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 198.0.2.48/28 next-hop '192.0.2.18'
+
+
+# Using deleted to delete static route based on destination
+#
+# Before state
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Delete static route per destination.
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: '192.0.2.32/28'
+ - afi: 'ipv6'
+ routes:
+ - dest: '2001:db8:1000::/36'
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# "commands": [
+# "delete protocols static route 192.0.2.32/28",
+# "delete protocols static route6 2001:db8:1000::/36"
+# ]
+#
+# "after": []
+# After state
+# ------------
+# vyos@vyos# run show configuration commands | grep static
+# set protocols 'static'
+
+
+# Using deleted to delete static route based on afi
+#
+# Before state
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Delete static route based on afi.
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ - afi: 'ipv6'
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# "commands": [
+# "delete protocols static route",
+# "delete protocols static route6"
+# ]
+#
+# "after": []
+# After state
+# ------------
+# vyos@vyos# run show configuration commands | grep static
+# set protocols 'static'
+
+
+# Using deleted to delete all the static routes when passes config is empty
+#
+# Before state
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Delete all the static routes.
+ vyos_static_routes:
+ config:
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# "commands": [
+# "delete protocols static route",
+# "delete protocols static route6"
+# ]
+#
+# "after": []
+# After state
+# ------------
+# vyos@vyos# run show configuration commands | grep static
+# set protocols 'static'
+
+
+# Using deleted to delete static route based on next-hop
+#
+# Before state
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Delete static routes per next-hops
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: '192.0.2.32/28'
+ next-hops:
+ - forward_router_address: '192.0.2.6'
+ - afi: 'ipv6'
+ routes:
+ - dest: '2001:db8:1000::/36'
+ next-hops:
+ - forward_router_address: '2001:db8:2000:2::1'
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# "commands": [
+# "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.6'",
+# "delete protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'"
+# ]
+#
+# "after": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# After state
+# ------------
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+
+
+# Using rendered
+#
+#
+- name: Render the commands for provided configuration
+ vyos_static_routes:
+ config:
+ - address_families:
+ - afi: 'ipv4'
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: 'blackhole'
+ next_hops:
+ - forward_router_address: 192.0.2.6
+ - forward_router_address: 192.0.2.7
+ - address_families:
+ - afi: 'ipv6'
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
+ state: rendered
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+#
+# "rendered": [
+# "set protocols static route 192.0.2.32/28",
+# "set protocols static route 192.0.2.32/28 blackhole",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'",
+# "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'",
+# "set protocols static route6 2001:db8:1000::/36",
+# "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'",
+# "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'",
+# "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'"
+# ]
+
+
+# Using parsed
+#
+#
+- name: Render the commands for provided configuration
+ vyos_static_routes:
+ running_config:
+ "set protocols static route 192.0.2.32/28 'blackhole'
+ set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+ set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+ set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+ set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+ set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'"
+ state: parsed
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+#
+# "parsed": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+
+
+# Using gathered
+#
+# Before state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+#
+- name: Gather listed static routes with provided configurations
+ vyos_static_routes:
+ config:
+ state: gathered
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "gathered": [
+# {
+# "address_families": [
+# {
+# "afi": "ipv4",
+# "routes": [
+# {
+# "blackhole_config": {
+# "type": "blackhole"
+# },
+# "dest": "192.0.2.32/28",
+# "next_hops": [
+# {
+# "forward_router_address": "192.0.2.6"
+# },
+# {
+# "forward_router_address": "192.0.2.7"
+# }
+# ]
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "routes": [
+# {
+# "blackhole_config": {
+# "distance": 2
+# },
+# "dest": "2001:db8:1000::/36",
+# "next_hops": [
+# {
+# "forward_router_address": "2001:db8:2000:2::1"
+# },
+# {
+# "forward_router_address": "2001:db8:2000:2::2"
+# }
+# ]
+# }
+# ]
+# }
+# ]
+# }
+# ]
+#
+#
+# After state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands| grep static
+# set protocols static route 192.0.2.32/28 'blackhole'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'
+# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+# set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+
+
+"""
+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 protocols static route 192.0.2.32/28 next-hop '192.0.2.6'"
+ - "set protocols static route 192.0.2.32/28 'blackhole'"
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+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.config.static_routes.static_routes import (
+ Static_routes,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Static_routesArgs.argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ mutually_exclusive=mutually_exclusive,
+ )
+ result = Static_routes(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/integration/targets/vyos_firewall_rules/defaults/main.yaml b/tests/integration/targets/vyos_firewall_rules/defaults/main.yaml
new file mode 100644
index 0000000..852a6be
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: '[^_].*'
+test_items: []
diff --git a/tests/integration/targets/vyos_firewall_rules/meta/main.yaml b/tests/integration/targets/vyos_firewall_rules/meta/main.yaml
new file mode 100644
index 0000000..7413320
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/meta/main.yaml
@@ -0,0 +1,3 @@
+---
+dependencies:
+ - prepare_vyos_tests
diff --git a/tests/integration/targets/vyos_firewall_rules/tasks/cli.yaml b/tests/integration/targets/vyos_firewall_rules/tasks/cli.yaml
new file mode 100644
index 0000000..93eb2fe
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tasks/cli.yaml
@@ -0,0 +1,19 @@
+---
+- name: Collect all cli test cases
+ find:
+ paths: '{{ role_path }}/tests/cli'
+ patterns: '{{ testcase }}.yaml'
+ use_regex: true
+ register: test_cases
+ delegate_to: localhost
+
+- name: Set test_items
+ set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: Run test case (connection=ansible.netcommon.network_cli)
+ include: '{{ test_case_to_run }}'
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
+ with_items: '{{ test_items }}'
+ loop_control:
+ loop_var: test_case_to_run
diff --git a/tests/integration/targets/vyos_firewall_rules/tasks/main.yaml b/tests/integration/targets/vyos_firewall_rules/tasks/main.yaml
new file mode 100644
index 0000000..a3db933
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tasks/main.yaml
@@ -0,0 +1,4 @@
+---
+- include: cli.yaml
+ tags:
+ - cli
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_firewall_rules/tests/cli/_parsed_config.cfg
new file mode 100644
index 0000000..b54c109
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/_parsed_config.cfg
@@ -0,0 +1,25 @@
+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'
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/_populate.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/_populate.yaml
new file mode 100644
index 0000000..551736e
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/_populate.yaml
@@ -0,0 +1,27 @@
+---
+- name: Setup
+ vars:
+ lines: "set firewall group address-group 'inbound'\nset firewall ipv6-name UPLINK\
+ \ default-action 'accept'\nset firewall ipv6-name UPLINK description 'This\
+ \ is ipv6 specific rule-set'\nset firewall ipv6-name UPLINK rule 1 action\
+ \ 'accept'\nset firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule\
+ \ 1 is configured by Ansible'\nset firewall ipv6-name UPLINK rule 1 ipsec\
+ \ 'match-ipsec'\nset firewall ipv6-name UPLINK rule 2 action 'accept'\nset\
+ \ firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured\
+ \ by Ansible'\nset firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'\n\
+ set firewall name INBOUND default-action 'accept'\nset firewall name INBOUND\
+ \ description 'IPv4 INBOUND rule set'\nset firewall name INBOUND rule 101\
+ \ action 'accept'\nset firewall name INBOUND rule 101 description 'Rule 101\
+ \ is configured by Ansible'\nset firewall name INBOUND rule 101 ipsec 'match-ipsec'\n\
+ set firewall name INBOUND rule 102 action 'reject'\nset firewall name INBOUND\
+ \ rule 102 description 'Rule 102 is configured by Ansible'\nset firewall name\
+ \ INBOUND rule 102 ipsec 'match-ipsec'\nset firewall name INBOUND rule 103\
+ \ action 'accept'\nset firewall name INBOUND rule 103 description 'Rule 103\
+ \ is configured by Ansible'\nset firewall name INBOUND rule 103 destination\
+ \ group address-group 'inbound'\nset firewall name INBOUND rule 103 source\
+ \ address '192.0.2.0'\nset firewall name INBOUND rule 103 state established\
+ \ 'enable'\nset firewall name INBOUND rule 103 state invalid 'disable'\nset\
+ \ firewall name INBOUND rule 103 state new 'disable'\nset firewall name INBOUND\
+ \ rule 103 state related 'enable'\n"
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/_remove_config.yaml
new file mode 100644
index 0000000..acb0803
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/_remove_config.yaml
@@ -0,0 +1,6 @@
+---
+- name: Remove Config
+ vars:
+ lines: "delete firewall ipv6-name\ndelete firewall name\n"
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted.yaml
new file mode 100644
index 0000000..7acfe65
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted.yaml
@@ -0,0 +1,60 @@
+---
+- debug:
+ msg: Start vyos_firewall_rules deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete firewall rule set.
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ config:
+
+ - afi: ipv6
+ rule_sets:
+
+ - name: UPLINK
+
+ - afi: ipv4
+ rule_sets:
+
+ - name: INBOUND
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_rs['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_rs['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_rs['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_afi.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_afi.yaml
new file mode 100644
index 0000000..e20670d
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_afi.yaml
@@ -0,0 +1,54 @@
+---
+- debug:
+ msg: Start vyos_firewall_rules deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete firewall rule.
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ config:
+
+ - afi: ipv6
+
+ - afi: ipv4
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['after'])\
+ \ |length == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['before'])\
+ \ |length == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_all.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_all.yaml
new file mode 100644
index 0000000..16e563c
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_all.yaml
@@ -0,0 +1,50 @@
+---
+- debug:
+ msg: Start vyos_firewall_rules deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete all the firewall rules.
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ config:
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['after'])\
+ \ |length == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['before'])\
+ \ |length == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_rule.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_rule.yaml
new file mode 100644
index 0000000..d77e2a9
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/deleted_rule.yaml
@@ -0,0 +1,58 @@
+---
+- debug:
+ msg: Start vyos_firewall_rules deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete firewall rule.
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ config:
+
+ - afi: ipv6
+ rule_sets:
+
+ - name: UPLINK
+ rules:
+
+ - number: 1
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_r['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_r['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_r['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/empty_config.yaml
new file mode 100644
index 0000000..c30cf03
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/empty_config.yaml
@@ -0,0 +1,60 @@
+---
+- debug:
+ msg: START vyos_firewall_rules empty_config integration tests on connection={{
+ ansible_connection }}
+
+- name: Merged with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_firewall_rules:
+ config:
+ state: merged
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state merged'
+
+- name: Replaced with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_firewall_rules:
+ config:
+ state: replaced
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state replaced'
+
+- name: Overridden with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_firewall_rules:
+ config:
+ state: overridden
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state overridden'
+
+- name: Parsed with empty running_config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_firewall_rules:
+ running_config:
+ state: parsed
+
+- assert:
+ that:
+ - result.msg == 'value of running_config parameter must not be empty for state
+ parsed'
+
+- name: Rendered with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_firewall_rules:
+ config:
+ state: rendered
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state rendered'
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/gathered.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/gathered.yaml
new file mode 100644
index 0000000..cdc8e51
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/gathered.yaml
@@ -0,0 +1,34 @@
+---
+- debug:
+ msg: START vyos_firewall_rules gathered integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ config:
+ state: gathered
+
+ - name: Assert that gathered dicts was correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['gathered']) |length == 0\
+ \ }}"
+
+ - name: Gather the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/merged.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/merged.yaml
new file mode 100644
index 0000000..adf7e47
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/merged.yaml
@@ -0,0 +1,102 @@
+---
+- debug:
+ msg: START vyos_firewall_rules merged integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _populate.yaml
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ 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
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that: "{{ merged['before'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ merged['commands'] | symmetric_difference(result['commands']) |length\
+ \ == 0 }}"
+
+ - name: Assert that after dicts was correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Merge the provided configuration with the existing running configuration
+ (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/overridden.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/overridden.yaml
new file mode 100644
index 0000000..6acc951
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/overridden.yaml
@@ -0,0 +1,69 @@
+---
+- debug:
+ msg: START vyos_firewall_rules overridden integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Overrides all device configuration with provided configuration
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ 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
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that correct commands were generated
+ assert:
+ that:
+ - "{{ overridden['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that after dicts were correctly generated
+ assert:
+ that:
+ - "{{ overridden['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Overrides all device configuration with provided configurations (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ overridden['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/parsed.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/parsed.yaml
new file mode 100644
index 0000000..a793ac5
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/parsed.yaml
@@ -0,0 +1,41 @@
+---
+- debug:
+ msg: START vyos_firewall_rules parsed integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Gather firewall_rules facts
+ register: firewall_rules_facts
+ vyos.vyos.vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - firewall_rules
+
+ - name: Provide the running configuration for parsing (config to be parsed)
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ running_config: "{{ lookup('file', '_parsed_config.cfg') }}"
+ state: parsed
+
+ - name: Assert that correct parsing done
+ assert:
+ that: "{{ ansible_facts['network_resources']['firewall_rules'] | symmetric_difference(result['parsed'])\
+ \ |length == 0 }}"
+
+ - name: Gather the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/rendered.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/rendered.yaml
new file mode 100644
index 0000000..f000998
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/rendered.yaml
@@ -0,0 +1,73 @@
+---
+- debug:
+ msg: START vyos_firewall_rules rendered integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Structure provided configuration into device specific commands
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ 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
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\
+ \ |length == 0 }}"
+
+ - name: Structure provided configuration into device specific commands (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/replaced.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/replaced.yaml
new file mode 100644
index 0000000..eba1689
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/replaced.yaml
@@ -0,0 +1,78 @@
+---
+- debug:
+ msg: START vyos_firewall_rules replaced integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Replace device configurations of listed firewall rules with provided
+ configurations
+ register: result
+ vyos.vyos.vyos_firewall_rules: &id001
+ 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
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ replaced['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that before dicts are correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that after dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Replace device configurations of listed firewall rules with provided
+ configurarions (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_firewall_rules: *id001
+
+ - name: Assert that task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/tests/cli/rtt.yaml b/tests/integration/targets/vyos_firewall_rules/tests/cli/rtt.yaml
new file mode 100644
index 0000000..762086f
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/tests/cli/rtt.yaml
@@ -0,0 +1,101 @@
+---
+- debug:
+ msg: START vyos_firewall_rules round trip integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Apply the provided configuration (base config)
+ register: base_config
+ 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
+ state: merged
+
+ - name: Gather firewall_rules facts
+ vyos.vyos.vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - firewall_rules
+
+ - name: Apply the provided configuration (config to be reverted)
+ register: result
+ vyos.vyos.vyos_firewall_rules:
+ config:
+
+ - afi: ipv4
+ rule_sets:
+
+ - name: INBOUND
+ description: IPv4 INBOUND rule set
+ default_action: accept
+ rules:
+
+ - number: 103
+ action: accept
+ description: Rule 103 is configured by Ansible
+ source:
+ address: 192.0.2.0
+ state:
+ established: true
+ new: false
+ invalid: false
+ related: true
+ state: merged
+
+ - name: Assert that changes were applied
+ assert:
+ that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Revert back to base config using facts round trip
+ register: revert
+ vyos.vyos.vyos_firewall_rules:
+ config: "{{ ansible_facts['network_resources']['firewall_rules'] }}"
+ state: overridden
+
+ - name: Assert that config was reverted
+ assert:
+ that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_firewall_rules/vars/main.yaml b/tests/integration/targets/vyos_firewall_rules/vars/main.yaml
new file mode 100644
index 0000000..c15a101
--- /dev/null
+++ b/tests/integration/targets/vyos_firewall_rules/vars/main.yaml
@@ -0,0 +1,312 @@
+---
+merged:
+ 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:
+ - 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
+populate:
+ - 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
+replaced:
+ 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:
+ - 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
+overridden:
+ before:
+ - 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
+ 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:
+ - 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
+rendered:
+ commands:
+ - 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'
+deleted_rs:
+ commands:
+ - delete firewall ipv6-name UPLINK
+ - delete firewall name INBOUND
+ after: []
+deleted_afi_all:
+ commands:
+ - delete firewall ipv6-name
+ - delete firewall name
+ after: []
+deleted_r:
+ commands:
+ - delete firewall ipv6-name UPLINK rule 1
+ after:
+ - afi: ipv6
+ rule_sets:
+ - name: UPLINK
+ description: This is ipv6 specific rule-set
+ default_action: accept
+ rules:
+ - 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
+round_trip:
+ after:
+ - 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
+ source:
+ address: 192.0.2.0
+ state:
+ established: true
+ new: false
+ invalid: false
+ related: true
diff --git a/tests/integration/targets/vyos_static_routes/defaults/main.yaml b/tests/integration/targets/vyos_static_routes/defaults/main.yaml
new file mode 100644
index 0000000..852a6be
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: '[^_].*'
+test_items: []
diff --git a/tests/integration/targets/vyos_static_routes/meta/main.yaml b/tests/integration/targets/vyos_static_routes/meta/main.yaml
new file mode 100644
index 0000000..91da2a7
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/meta/main.yaml
@@ -0,0 +1,2 @@
+---
+...
diff --git a/tests/integration/targets/vyos_static_routes/tasks/cli.yaml b/tests/integration/targets/vyos_static_routes/tasks/cli.yaml
new file mode 100644
index 0000000..93eb2fe
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tasks/cli.yaml
@@ -0,0 +1,19 @@
+---
+- name: Collect all cli test cases
+ find:
+ paths: '{{ role_path }}/tests/cli'
+ patterns: '{{ testcase }}.yaml'
+ use_regex: true
+ register: test_cases
+ delegate_to: localhost
+
+- name: Set test_items
+ set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: Run test case (connection=ansible.netcommon.network_cli)
+ include: '{{ test_case_to_run }}'
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
+ with_items: '{{ test_items }}'
+ loop_control:
+ loop_var: test_case_to_run
diff --git a/tests/integration/targets/vyos_static_routes/tasks/main.yaml b/tests/integration/targets/vyos_static_routes/tasks/main.yaml
new file mode 100644
index 0000000..a3db933
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tasks/main.yaml
@@ -0,0 +1,4 @@
+---
+- include: cli.yaml
+ tags:
+ - cli
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg
new file mode 100644
index 0000000..b2ecd4e
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg
@@ -0,0 +1,6 @@
+set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'
+set protocols static route 192.0.2.32/28 blackhole
+set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/_populate.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/_populate.yaml
new file mode 100644
index 0000000..f292e5d
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/_populate.yaml
@@ -0,0 +1,12 @@
+---
+- name: Setup
+ vars:
+ lines: "set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'\nset\
+ \ protocols static route 192.0.2.32/28 next-hop '192.0.2.9'\nset protocols\
+ \ static route 192.0.2.32/28 blackhole\nset protocols static route 192.0.2.32/28\n\
+ set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'\n\
+ set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'\n\
+ set protocols static route6 2001:db8:1000::/36 blackhole distance '2'\nset\
+ \ protocols static route6 2001:db8:1000::/36\n"
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml
new file mode 100644
index 0000000..5a5cccb
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml
@@ -0,0 +1,6 @@
+---
+- name: Remove Config
+ vars:
+ lines: "delete protocols static route\ndelete protocols static route6\n"
+ ansible.netcommon.cli_config:
+ config: '{{ lines }}'
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/deleted.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/deleted.yaml
new file mode 100644
index 0000000..7f098f5
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/deleted.yaml
@@ -0,0 +1,62 @@
+---
+- debug:
+ msg: Start vyos_static_routes deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete static route based on destiation.
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+
+ - afi: ipv6
+ routes:
+
+ - dest: 2001:db8:1000::/36
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_dest['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_dest['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_dest['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml
new file mode 100644
index 0000000..221f1b5
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml
@@ -0,0 +1,56 @@
+---
+- debug:
+ msg: Start vyos_static_routes deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete static route based on afi.
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+
+ - afi: ipv6
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['after'])\
+ \ |length == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['before'])\
+ \ |length == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml
new file mode 100644
index 0000000..e10f1bc
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml
@@ -0,0 +1,50 @@
+---
+- debug:
+ msg: Start vyos_static_routes deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete all the static routes.
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['after'])\
+ \ |length == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_afi_all['after'] | symmetric_difference(result['before'])\
+ \ |length == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml
new file mode 100644
index 0000000..f6075d2
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml
@@ -0,0 +1,68 @@
+---
+- debug:
+ msg: Start vyos_static_routes deleted integration tests ansible_connection={{
+ ansible_connection }}
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Delete static route based on next_hop.
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ next_hops:
+
+ - forward_router_address: 192.0.2.9
+
+ - afi: ipv6
+ routes:
+
+ - dest: 2001:db8:1000::/36
+ next_hops:
+
+ - forward_router_address: 2001:db8:2000:2::1
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_nh['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_nh['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Delete attributes of given interfaces (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_nh['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml
new file mode 100644
index 0000000..f58ef39
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml
@@ -0,0 +1,60 @@
+---
+- debug:
+ msg: START vyos_static_routes empty_config integration tests on connection={{
+ ansible_connection }}
+
+- name: Merged with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_static_routes:
+ config:
+ state: merged
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state merged'
+
+- name: Replaced with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_static_routes:
+ config:
+ state: replaced
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state replaced'
+
+- name: Overridden with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_static_routes:
+ config:
+ state: overridden
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state overridden'
+
+- name: Parsed with empty running_config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_static_routes:
+ running_config:
+ state: parsed
+
+- assert:
+ that:
+ - result.msg == 'value of running_config parameter must not be empty for state
+ parsed'
+
+- name: Rendered with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_static_routes:
+ config:
+ state: rendered
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state rendered'
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/gathered.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/gathered.yaml
new file mode 100644
index 0000000..d3b84d1
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/gathered.yaml
@@ -0,0 +1,34 @@
+---
+- debug:
+ msg: START vyos_static_routes gathered integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+ state: gathered
+
+ - name: Assert that gathered dicts was correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['gathered']) |length == 0\
+ \ }}"
+
+ - name: Gather the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/merged.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/merged.yaml
new file mode 100644
index 0000000..999ae86
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/merged.yaml
@@ -0,0 +1,78 @@
+---
+- debug:
+ msg: START vyos_static_routes merged integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Merge the provided configuration with the exisiting running configuration
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+
+ - forward_router_address: 192.0.2.10
+
+ - forward_router_address: 192.0.2.9
+
+ - address_families:
+
+ - afi: ipv6
+ routes:
+
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+
+ - forward_router_address: 2001:db8:2000:2::1
+
+ - forward_router_address: 2001:db8:2000:2::2
+ state: merged
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that: "{{ merged['before'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ merged['commands'] | symmetric_difference(result['commands']) |length\
+ \ == 0 }}"
+
+ - name: Assert that after dicts was correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Merge the provided configuration with the existing running configuration
+ (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ merged['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/overridden.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/overridden.yaml
new file mode 100644
index 0000000..a9112a5
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/overridden.yaml
@@ -0,0 +1,61 @@
+---
+- debug:
+ msg: START vyos_static_routes overridden integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Overrides all device configuration with provided configuration
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 198.0.2.48/28
+ next_hops:
+
+ - forward_router_address: 192.0.2.18
+ state: overridden
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that correct commands were generated
+ assert:
+ that:
+ - "{{ overridden['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that after dicts were correctly generated
+ assert:
+ that:
+ - "{{ overridden['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Overrides all device configuration with provided configurations (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "{{ overridden['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/parsed.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/parsed.yaml
new file mode 100644
index 0000000..4b6e434
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/parsed.yaml
@@ -0,0 +1,41 @@
+---
+- debug:
+ msg: START vyos_static_routes parsed integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Gather static_routes facts
+ register: static_routes_facts
+ vyos.vyos.vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - static_routes
+
+ - name: Provide the running configuration for parsing (config to be parsed)
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ running_config: "{{ lookup('file', '_parsed_config.cfg') }}"
+ state: parsed
+
+ - name: Assert that correct parsing done
+ assert:
+ that: "{{ ansible_facts['network_resources']['static_routes'] | symmetric_difference(result['parsed'])\
+ \ |length == 0 }}"
+
+ - name: Gather the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/rendered.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/rendered.yaml
new file mode 100644
index 0000000..ff18523
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/rendered.yaml
@@ -0,0 +1,62 @@
+---
+- debug:
+ msg: START vyos_static_routes rendered integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Structure provided configuration into device specific commands
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+
+ - forward_router_address: 192.0.2.10
+
+ - forward_router_address: 192.0.2.9
+
+ - address_families:
+
+ - afi: ipv6
+ routes:
+
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+
+ - forward_router_address: 2001:db8:2000:2::1
+
+ - forward_router_address: 2001:db8:2000:2::2
+ state: rendered
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\
+ \ |length == 0 }}"
+
+ - name: Structure provided configuration into device specific commands (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/replaced.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/replaced.yaml
new file mode 100644
index 0000000..80ed801
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/replaced.yaml
@@ -0,0 +1,69 @@
+---
+- debug:
+ msg: START vyos_static_routes replaced integration tests on connection={{ ansible_connection
+ }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+ - name: Replace device configurations of listed static routes with provided
+ configurations
+ register: result
+ vyos.vyos.vyos_static_routes: &id001
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ distance: 2
+ next_hops:
+
+ - forward_router_address: 192.0.2.7
+
+ - forward_router_address: 192.0.2.8
+
+ - forward_router_address: 192.0.2.9
+ state: replaced
+
+ - name: Assert that correct set of commands were generated
+ assert:
+ that:
+ - "{{ replaced['commands'] | symmetric_difference(result['commands'])\
+ \ |length == 0 }}"
+
+ - name: Assert that before dicts are correctly generated
+ assert:
+ that:
+ - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+
+ - name: Assert that after dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Replace device configurations of listed static routes with provided
+ configurarions (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_static_routes: *id001
+
+ - name: Assert that task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ - name: Assert that before dict is correctly generated
+ assert:
+ that:
+ - "{{ replaced['after'] | symmetric_difference(result['before']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/tests/cli/rtt.yaml b/tests/integration/targets/vyos_static_routes/tests/cli/rtt.yaml
new file mode 100644
index 0000000..340fde9
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/tests/cli/rtt.yaml
@@ -0,0 +1,90 @@
+---
+- debug:
+ msg: START vyos_static_routes round trip integration tests on connection={{
+ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+
+ - name: Apply the provided configuration (base config)
+ register: base_config
+ vyos.vyos.vyos_static_routes:
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+
+ - forward_router_address: 192.0.2.10
+
+ - forward_router_address: 192.0.2.9
+
+ - address_families:
+
+ - afi: ipv6
+ routes:
+
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+
+ - forward_router_address: 2001:db8:2000:2::1
+
+ - forward_router_address: 2001:db8:2000:2::2
+ state: merged
+
+ - name: Gather static_routes facts
+ vyos.vyos.vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - static_routes
+
+ - name: Apply the provided configuration (config to be reverted)
+ register: result
+ vyos.vyos.vyos_static_routes:
+ config:
+
+ - address_families:
+
+ - afi: ipv4
+ routes:
+
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ distance: 2
+ next_hops:
+
+ - forward_router_address: 192.0.2.7
+
+ - forward_router_address: 192.0.2.8
+
+ - forward_router_address: 192.0.2.9
+ state: merged
+
+ - name: Assert that changes were applied
+ assert:
+ that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length\
+ \ == 0 }}"
+
+ - name: Revert back to base config using facts round trip
+ register: revert
+ vyos.vyos.vyos_static_routes:
+ config: "{{ ansible_facts['network_resources']['static_routes'] }}"
+ state: overridden
+
+ - name: Assert that config was reverted
+ assert:
+ that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length\
+ \ == 0 }}"
+ always:
+
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_static_routes/vars/main.yaml b/tests/integration/targets/vyos_static_routes/vars/main.yaml
new file mode 100644
index 0000000..93b875f
--- /dev/null
+++ b/tests/integration/targets/vyos_static_routes/vars/main.yaml
@@ -0,0 +1,147 @@
+---
+merged:
+ before: []
+ commands:
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+ - set protocols static route 192.0.2.32/28 blackhole
+ - set protocols static route 192.0.2.32/28
+ - set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+ - set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+ - set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+ - set protocols static route6 2001:db8:1000::/36
+ after:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+ - forward_router_address: 192.0.2.9
+ - forward_router_address: 192.0.2.10
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
+populate:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+ - forward_router_address: 192.0.2.9
+ - forward_router_address: 192.0.2.10
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
+replaced:
+ commands:
+ - delete protocols static route 192.0.2.32/28 next-hop '192.0.2.10'
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.8'
+ - set protocols static route 192.0.2.32/28 blackhole distance '2'
+ after:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 192.0.2.7
+ - forward_router_address: 192.0.2.8
+ - forward_router_address: 192.0.2.9
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
+overridden:
+ commands:
+ - delete protocols static route 192.0.2.32/28
+ - delete protocols static route6 2001:db8:1000::/36
+ - set protocols static route 198.0.2.48/28 next-hop '192.0.2.18'
+ - set protocols static route 198.0.2.48/28
+ after:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 198.0.2.48/28
+ next_hops:
+ - forward_router_address: 192.0.2.18
+rendered:
+ commands:
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'
+ - set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+ - set protocols static route 192.0.2.32/28 blackhole
+ - set protocols static route 192.0.2.32/28
+ - set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+ - set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'
+ - set protocols static route6 2001:db8:1000::/36 blackhole distance '2'
+ - set protocols static route6 2001:db8:1000::/36
+deleted_dest:
+ commands:
+ - delete protocols static route 192.0.2.32/28
+ - delete protocols static route6 2001:db8:1000::/36
+ after: []
+deleted_nh:
+ commands:
+ - delete protocols static route 192.0.2.32/28 next-hop '192.0.2.9'
+ - delete protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'
+ after:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ type: blackhole
+ next_hops:
+ - forward_router_address: 192.0.2.10
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::2
+deleted_afi_all:
+ commands:
+ - delete protocols static route
+ - delete protocols static route6
+ after: []
+round_trip:
+ after:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.32/28
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 192.0.2.7
+ - forward_router_address: 192.0.2.8
+ - forward_router_address: 192.0.2.9
+ - forward_router_address: 192.0.2.10
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8:1000::/36
+ blackhole_config:
+ distance: 2
+ next_hops:
+ - forward_router_address: 2001:db8:2000:2::1
+ - forward_router_address: 2001:db8:2000:2::2
diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt
index d25476f..a62f497 100644
--- a/tests/sanity/ignore-2.10.txt
+++ b/tests/sanity/ignore-2.10.txt
@@ -45,16 +45,6 @@ plugins/modules/vyos_logging.py validate-modules:undocumented-parameter
plugins/modules/vyos_ping.py validate-modules:doc-default-does-not-match-spec
plugins/modules/vyos_ping.py validate-modules:doc-required-mismatch
plugins/modules/vyos_ping.py validate-modules:parameter-type-not-in-doc
-plugins/modules/vyos_static_route.py future-import-boilerplate
-plugins/modules/vyos_static_route.py metaclass-boilerplate
-plugins/modules/vyos_static_route.py validate-modules:doc-choices-do-not-match-spec
-plugins/modules/vyos_static_route.py validate-modules:doc-default-does-not-match-spec
-plugins/modules/vyos_static_route.py validate-modules:doc-elements-mismatch
-plugins/modules/vyos_static_route.py validate-modules:doc-missing-type
-plugins/modules/vyos_static_route.py validate-modules:doc-required-mismatch
-plugins/modules/vyos_static_route.py validate-modules:missing-suboption-docs
-plugins/modules/vyos_static_route.py validate-modules:parameter-type-not-in-doc
-plugins/modules/vyos_static_route.py validate-modules:undocumented-parameter
plugins/modules/vyos_system.py future-import-boilerplate
plugins/modules/vyos_system.py metaclass-boilerplate
plugins/modules/vyos_system.py validate-modules:doc-default-does-not-match-spec
diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt
index d25476f..a62f497 100644
--- a/tests/sanity/ignore-2.9.txt
+++ b/tests/sanity/ignore-2.9.txt
@@ -45,16 +45,6 @@ plugins/modules/vyos_logging.py validate-modules:undocumented-parameter
plugins/modules/vyos_ping.py validate-modules:doc-default-does-not-match-spec
plugins/modules/vyos_ping.py validate-modules:doc-required-mismatch
plugins/modules/vyos_ping.py validate-modules:parameter-type-not-in-doc
-plugins/modules/vyos_static_route.py future-import-boilerplate
-plugins/modules/vyos_static_route.py metaclass-boilerplate
-plugins/modules/vyos_static_route.py validate-modules:doc-choices-do-not-match-spec
-plugins/modules/vyos_static_route.py validate-modules:doc-default-does-not-match-spec
-plugins/modules/vyos_static_route.py validate-modules:doc-elements-mismatch
-plugins/modules/vyos_static_route.py validate-modules:doc-missing-type
-plugins/modules/vyos_static_route.py validate-modules:doc-required-mismatch
-plugins/modules/vyos_static_route.py validate-modules:missing-suboption-docs
-plugins/modules/vyos_static_route.py validate-modules:parameter-type-not-in-doc
-plugins/modules/vyos_static_route.py validate-modules:undocumented-parameter
plugins/modules/vyos_system.py future-import-boilerplate
plugins/modules/vyos_system.py metaclass-boilerplate
plugins/modules/vyos_system.py validate-modules:doc-default-does-not-match-spec
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg
new file mode 100644
index 0000000..f65b386
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg
@@ -0,0 +1,13 @@
+set firewall name V4-INGRESS default-action 'accept'
+set firewall ipv6-name V6-INGRESS default-action 'accept'
+set firewall name V4-INGRESS description 'This is IPv4 V4-INGRESS rule set'
+set firewall name V4-INGRESS enable-default-log
+set firewall name V4-INGRESS rule 101 protocol 'icmp'
+set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'
+set firewall name V4-INGRESS rule 101 fragment 'match-frag'
+set firewall name V4-INGRESS rule 101
+set firewall name V4-INGRESS rule 101 disabled
+set firewall name V4-INGRESS rule 101 action 'accept'
+set firewall name V4-INGRESS rule 101 ipsec 'match-ipsec'
+set firewall name V4-EGRESS default-action 'reject'
+set firewall ipv6-name V6-EGRESS default-action 'reject'
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_static_routes_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_static_routes_config.cfg
new file mode 100644
index 0000000..0411dc9
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_static_routes_config.cfg
@@ -0,0 +1,2 @@
+'set protocols static route 192.0.2.32/28 next-hop 192.0.2.9'
+'set protocols static route 192.0.2.32/28 next-hop 192.0.2.10'
diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py
new file mode 100644
index 0000000..86fcc65
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py
@@ -0,0 +1,1039 @@
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch
+from ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_rules
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import (
+ set_module_args,
+)
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosFirewallRulesModule(TestVyosModule):
+
+ module = vyos_firewall_rules
+
+ def setUp(self):
+ super(TestVyosFirewallRulesModule, self).setUp()
+ self.mock_get_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config"
+ )
+ self.get_config = self.mock_get_config.start()
+
+ self.mock_load_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config"
+ )
+ self.load_config = self.mock_load_config.start()
+
+ self.mock_get_resource_connection_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection"
+ )
+ self.get_resource_connection_config = (
+ self.mock_get_resource_connection_config.start()
+ )
+
+ self.mock_get_resource_connection_facts = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection"
+ )
+ self.get_resource_connection_facts = (
+ self.mock_get_resource_connection_facts.start()
+ )
+ self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data"
+ )
+
+ self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data"
+ )
+ self.execute_show_command = self.mock_execute_show_command.start()
+
+ def tearDown(self):
+ super(TestVyosFirewallRulesModule, self).tearDown()
+ self.mock_get_resource_connection_config.stop()
+ self.mock_get_resource_connection_facts.stop()
+ self.mock_get_config.stop()
+ self.mock_load_config.stop()
+ self.mock_execute_show_command.stop()
+
+ def load_fixtures(self, commands=None):
+ def load_from_file(*args, **kwargs):
+ return load_fixture("vyos_firewall_rules_config.cfg")
+
+ self.execute_show_command.side_effect = load_from_file
+
+ def test_vyos_firewall_rule_set_01_merged(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="V6-INBOUND",
+ description="This is IPv6 INBOUND rule set",
+ default_action="reject",
+ enable_default_log=True,
+ rules=[],
+ ),
+ dict(
+ name="V6-OUTBOUND",
+ description="This is IPv6 OUTBOUND rule set",
+ default_action="accept",
+ enable_default_log=False,
+ rules=[],
+ ),
+ ],
+ ),
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INBOUND",
+ description="This is IPv4 INBOUND rule set",
+ default_action="reject",
+ enable_default_log=True,
+ rules=[],
+ ),
+ dict(
+ name="V4-OUTBOUND",
+ description="This is IPv4 OUTBOUND rule set",
+ default_action="accept",
+ enable_default_log=False,
+ rules=[],
+ ),
+ ],
+ ),
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name V6-INBOUND default-action 'reject'",
+ "set firewall ipv6-name V6-INBOUND description 'This is IPv6 INBOUND rule set'",
+ "set firewall ipv6-name V6-INBOUND enable-default-log",
+ "set firewall ipv6-name V6-OUTBOUND default-action 'accept'",
+ "set firewall ipv6-name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'",
+ "set firewall name V4-INBOUND default-action 'reject'",
+ "set firewall name V4-INBOUND description 'This is IPv4 INBOUND rule set'",
+ "set firewall name V4-INBOUND enable-default-log",
+ "set firewall name V4-OUTBOUND default-action 'accept'",
+ "set firewall name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_rule_set_02_merged(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="V6-INBOUND",
+ description="This is IPv6 INBOUND rule set",
+ default_action="reject",
+ enable_default_log=True,
+ rules=[],
+ ),
+ dict(
+ name="V6-OUTBOUND",
+ description="This is IPv6 OUTBOUND rule set",
+ default_action="accept",
+ enable_default_log=False,
+ rules=[],
+ ),
+ ],
+ ),
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INBOUND",
+ description="This is IPv4 INBOUND rule set",
+ default_action="reject",
+ enable_default_log=True,
+ rules=[],
+ ),
+ dict(
+ name="V4-OUTBOUND",
+ description="This is IPv4 OUTBOUND rule set",
+ default_action="accept",
+ enable_default_log=False,
+ rules=[],
+ ),
+ ],
+ ),
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name V6-INBOUND default-action 'reject'",
+ "set firewall ipv6-name V6-INBOUND description 'This is IPv6 INBOUND rule set'",
+ "set firewall ipv6-name V6-INBOUND enable-default-log",
+ "set firewall ipv6-name V6-OUTBOUND default-action 'accept'",
+ "set firewall ipv6-name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'",
+ "set firewall name V4-INBOUND default-action 'reject'",
+ "set firewall name V4-INBOUND description 'This is IPv4 INBOUND rule set'",
+ "set firewall name V4-INBOUND enable-default-log",
+ "set firewall name V4-OUTBOUND default-action 'accept'",
+ "set firewall name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ description="This is IPv4 INBOUND rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ fragment="match-frag",
+ disabled=True,
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND default-action 'accept'",
+ "set firewall name INBOUND description 'This is IPv4 INBOUND rule set'",
+ "set firewall name INBOUND enable-default-log",
+ "set firewall name INBOUND rule 101 protocol 'icmp'",
+ "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'",
+ "set firewall name INBOUND rule 101 fragment 'match-frag'",
+ "set firewall name INBOUND rule 101",
+ "set firewall name INBOUND rule 101 disabled",
+ "set firewall name INBOUND rule 101 action 'accept'",
+ "set firewall name INBOUND rule 101 ipsec 'match-ipsec'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_02(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ protocol="tcp",
+ source=dict(
+ address="192.0.2.0",
+ mac_address="38:00:25:19:76:0c",
+ port=2127,
+ ),
+ destination=dict(
+ address="192.0.1.0", port=2124
+ ),
+ limit=dict(
+ burst=10,
+ rate=dict(
+ number=20, unit="second"
+ ),
+ ),
+ recent=dict(count=10, time=20),
+ state=dict(
+ established=True,
+ related=True,
+ invalid=True,
+ new=True,
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND rule 101 protocol 'tcp'",
+ "set firewall name INBOUND rule 101 destination address 192.0.1.0",
+ "set firewall name INBOUND rule 101 destination port 2124",
+ "set firewall name INBOUND rule 101",
+ "set firewall name INBOUND rule 101 source address 192.0.2.0",
+ "set firewall name INBOUND rule 101 source mac-address 38:00:25:19:76:0c",
+ "set firewall name INBOUND rule 101 source port 2127",
+ "set firewall name INBOUND rule 101 state new enable",
+ "set firewall name INBOUND rule 101 state invalid enable",
+ "set firewall name INBOUND rule 101 state related enable",
+ "set firewall name INBOUND rule 101 state established enable",
+ "set firewall name INBOUND rule 101 limit burst 10",
+ "set firewall name INBOUND rule 101 limit rate 20/second",
+ "set firewall name INBOUND rule 101 recent count 10",
+ "set firewall name INBOUND rule 101 recent time 20",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_03(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ destination=dict(
+ group=dict(
+ address_group="OUT-ADDR-GROUP",
+ network_group="OUT-NET-GROUP",
+ port_group="OUT-PORT-GROUP",
+ )
+ ),
+ source=dict(
+ group=dict(
+ address_group="IN-ADDR-GROUP",
+ network_group="IN-NET-GROUP",
+ port_group="IN-PORT-GROUP",
+ )
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND rule 101 source group address-group IN-ADDR-GROUP",
+ "set firewall name INBOUND rule 101 source group network-group IN-NET-GROUP",
+ "set firewall name INBOUND rule 101 source group port-group IN-PORT-GROUP",
+ "set firewall name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP",
+ "set firewall name INBOUND rule 101 destination group network-group OUT-NET-GROUP",
+ "set firewall name INBOUND rule 101 destination group port-group OUT-PORT-GROUP",
+ "set firewall name INBOUND rule 101",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_04(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ time=dict(
+ monthdays="2",
+ startdate="2020-01-24",
+ starttime="13:20:00",
+ stopdate="2020-01-28",
+ stoptime="13:30:00",
+ weekdays="!Sat,Sun",
+ utc=True,
+ ),
+ tcp=dict(flags="ALL"),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND rule 101",
+ "set firewall name INBOUND rule 101 tcp flags ALL",
+ "set firewall name INBOUND rule 101 time utc",
+ "set firewall name INBOUND rule 101 time monthdays 2",
+ "set firewall name INBOUND rule 101 time startdate 2020-01-24",
+ "set firewall name INBOUND rule 101 time stopdate 2020-01-28",
+ "set firewall name INBOUND rule 101 time weekdays !Sat,Sun",
+ "set firewall name INBOUND rule 101 time stoptime 13:30:00",
+ "set firewall name INBOUND rule 101 time starttime 13:20:00",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v6_rule_sets_rule_merged_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ description="This is IPv6 INBOUND rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ disabled=True,
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name INBOUND default-action 'accept'",
+ "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set'",
+ "set firewall ipv6-name INBOUND enable-default-log",
+ "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'",
+ "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'",
+ "set firewall ipv6-name INBOUND rule 101",
+ "set firewall ipv6-name INBOUND rule 101 disabled",
+ "set firewall ipv6-name INBOUND rule 101 action 'accept'",
+ "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v6_rule_sets_rule_merged_02(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ protocol="tcp",
+ source=dict(
+ address="2001:db8::12",
+ mac_address="38:00:25:19:76:0c",
+ port=2127,
+ ),
+ destination=dict(
+ address="2001:db8::11", port=2124
+ ),
+ limit=dict(
+ burst=10,
+ rate=dict(
+ number=20, unit="second"
+ ),
+ ),
+ recent=dict(count=10, time=20),
+ state=dict(
+ established=True,
+ related=True,
+ invalid=True,
+ new=True,
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name INBOUND rule 101 protocol 'tcp'",
+ "set firewall ipv6-name INBOUND rule 101 destination address 2001:db8::11",
+ "set firewall ipv6-name INBOUND rule 101 destination port 2124",
+ "set firewall ipv6-name INBOUND rule 101",
+ "set firewall ipv6-name INBOUND rule 101 source address 2001:db8::12",
+ "set firewall ipv6-name INBOUND rule 101 source mac-address 38:00:25:19:76:0c",
+ "set firewall ipv6-name INBOUND rule 101 source port 2127",
+ "set firewall ipv6-name INBOUND rule 101 state new enable",
+ "set firewall ipv6-name INBOUND rule 101 state invalid enable",
+ "set firewall ipv6-name INBOUND rule 101 state related enable",
+ "set firewall ipv6-name INBOUND rule 101 state established enable",
+ "set firewall ipv6-name INBOUND rule 101 limit burst 10",
+ "set firewall ipv6-name INBOUND rule 101 recent count 10",
+ "set firewall ipv6-name INBOUND rule 101 recent time 20",
+ "set firewall ipv6-name INBOUND rule 101 limit rate 20/second",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v6_rule_sets_rule_merged_03(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ destination=dict(
+ group=dict(
+ address_group="OUT-ADDR-GROUP",
+ network_group="OUT-NET-GROUP",
+ port_group="OUT-PORT-GROUP",
+ )
+ ),
+ source=dict(
+ group=dict(
+ address_group="IN-ADDR-GROUP",
+ network_group="IN-NET-GROUP",
+ port_group="IN-PORT-GROUP",
+ )
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name INBOUND rule 101 source group address-group IN-ADDR-GROUP",
+ "set firewall ipv6-name INBOUND rule 101 source group network-group IN-NET-GROUP",
+ "set firewall ipv6-name INBOUND rule 101 source group port-group IN-PORT-GROUP",
+ "set firewall ipv6-name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP",
+ "set firewall ipv6-name INBOUND rule 101 destination group network-group OUT-NET-GROUP",
+ "set firewall ipv6-name INBOUND rule 101 destination group port-group OUT-PORT-GROUP",
+ "set firewall ipv6-name INBOUND rule 101",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v6_rule_sets_rule_merged_04(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ time=dict(
+ monthdays="2",
+ startdate="2020-01-24",
+ starttime="13:20:00",
+ stopdate="2020-01-28",
+ stoptime="13:30:00",
+ weekdays="!Sat,Sun",
+ utc=True,
+ ),
+ tcp=dict(flags="ALL"),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name INBOUND rule 101",
+ "set firewall ipv6-name INBOUND rule 101 tcp flags ALL",
+ "set firewall ipv6-name INBOUND rule 101 time utc",
+ "set firewall ipv6-name INBOUND rule 101 time monthdays 2",
+ "set firewall ipv6-name INBOUND rule 101 time startdate 2020-01-24",
+ "set firewall ipv6-name INBOUND rule 101 time stopdate 2020-01-28",
+ "set firewall ipv6-name INBOUND rule 101 time weekdays !Sat,Sun",
+ "set firewall ipv6-name INBOUND rule 101 time stoptime 13:30:00",
+ "set firewall ipv6-name INBOUND rule 101 time starttime 13:20:00",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ protocol="icmp",
+ icmp=dict(
+ type_name="port-unreachable"
+ ),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall ipv6-name INBOUND rule 101 icmpv6 type port-unreachable",
+ "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'",
+ "set firewall ipv6-name INBOUND rule 101",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ protocol="icmp",
+ icmp=dict(type=1, code=1),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND rule 101 icmp type 1",
+ "set firewall name INBOUND rule 101 icmp code 1",
+ "set firewall name INBOUND rule 101 protocol 'icmp'",
+ "set firewall name INBOUND rule 101",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="INBOUND",
+ rules=[
+ dict(
+ number="101",
+ protocol="icmp",
+ icmp=dict(type_name="echo-request"),
+ )
+ ],
+ ),
+ ],
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set firewall name INBOUND rule 101 icmp type-name echo-request",
+ "set firewall name INBOUND rule 101 protocol 'icmp'",
+ "set firewall name INBOUND rule 101",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4_rule_sets_del_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS"),])
+ ],
+ state="deleted",
+ )
+ )
+ commands = ["delete firewall name V4-INGRESS"]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_del_02(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS"),]),
+ dict(afi="ipv6", rule_sets=[dict(name="V6-INGRESS"),]),
+ ],
+ state="deleted",
+ )
+ )
+ commands = [
+ "delete firewall name V4-INGRESS",
+ "delete firewall ipv6-name V6-INGRESS",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_del_03(self):
+ set_module_args(dict(config=[], state="deleted"))
+ commands = ["delete firewall name", "delete firewall ipv6-name"]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_del_04(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(afi="ipv4", rule_sets=[dict(name="V4-ING"),]),
+ dict(afi="ipv6", rule_sets=[dict(name="V6-ING"),]),
+ ],
+ state="deleted",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INGRESS",
+ description="This is IPv4 INGRESS rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="reject",
+ description="Rule 101 is configured by Ansible RM",
+ ipsec="match-ipsec",
+ protocol="tcp",
+ fragment="match-frag",
+ disabled=False,
+ ),
+ dict(
+ number="102",
+ action="accept",
+ description="Rule 102 is configured by Ansible RM",
+ protocol="icmp",
+ disabled=True,
+ ),
+ ],
+ ),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="V6-INGRESS",
+ default_action="accept",
+ description="This rule-set is configured by Ansible RM",
+ ),
+ dict(
+ name="V6-EGRESS",
+ default_action="reject",
+ description="This rule-set is configured by Ansible RM",
+ ),
+ ],
+ ),
+ ],
+ state="replaced",
+ )
+ )
+ commands = [
+ "delete firewall name V4-INGRESS rule 101 disabled",
+ "delete firewall name V4-EGRESS default-action",
+ "set firewall name V4-INGRESS description 'This is IPv4 INGRESS rule set'",
+ "set firewall name V4-INGRESS rule 101 protocol 'tcp'",
+ "set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible RM'",
+ "set firewall name V4-INGRESS rule 101 action 'reject'",
+ "set firewall name V4-INGRESS rule 102 disabled",
+ "set firewall name V4-INGRESS rule 102 action 'accept'",
+ "set firewall name V4-INGRESS rule 102 protocol 'icmp'",
+ "set firewall name V4-INGRESS rule 102 description 'Rule 102 is configured by Ansible RM'",
+ "set firewall name V4-INGRESS rule 102",
+ "set firewall ipv6-name V6-INGRESS description 'This rule-set is configured by Ansible RM'",
+ "set firewall ipv6-name V6-EGRESS description 'This rule-set is configured by Ansible RM'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INGRESS",
+ description="This is IPv4 V4-INGRESS rule set",
+ default_action="accept",
+ enable_default_log=False,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ fragment="match-frag",
+ disabled=True,
+ ),
+ ],
+ ),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(name="V6-INGRESS", default_action="accept",),
+ dict(name="V6-EGRESS", default_action="reject",),
+ ],
+ ),
+ ],
+ state="replaced",
+ )
+ )
+ commands = [
+ "delete firewall name V4-INGRESS enable-default-log",
+ "delete firewall name V4-EGRESS default-action",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INGRESS",
+ description="This is IPv4 V4-INGRESS rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ fragment="match-frag",
+ disabled=True,
+ )
+ ],
+ ),
+ dict(name="V4-EGRESS", default_action="reject",),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(name="V6-INGRESS", default_action="accept",),
+ dict(name="V6-EGRESS", default_action="reject",),
+ ],
+ ),
+ ],
+ state="replaced",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INGRESS",
+ description="This is IPv4 V4-INGRESS rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ fragment="match-frag",
+ disabled=True,
+ )
+ ],
+ ),
+ dict(name="V4-EGRESS", default_action="reject",),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(name="V6-INGRESS", default_action="accept",),
+ dict(name="V6-EGRESS", default_action="reject",),
+ ],
+ ),
+ ],
+ state="merged",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-IN",
+ description="This is IPv4 INGRESS rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="1",
+ action="reject",
+ description="Rule 1 is configured by Ansible RM",
+ ipsec="match-ipsec",
+ protocol="tcp",
+ fragment="match-frag",
+ disabled=False,
+ ),
+ dict(
+ number="2",
+ action="accept",
+ description="Rule 102 is configured by Ansible RM",
+ protocol="icmp",
+ disabled=True,
+ ),
+ ],
+ ),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(
+ name="V6-IN",
+ default_action="accept",
+ description="This rule-set is configured by Ansible RM",
+ ),
+ dict(
+ name="V6-EG",
+ default_action="reject",
+ description="This rule-set is configured by Ansible RM",
+ ),
+ ],
+ ),
+ ],
+ state="overridden",
+ )
+ )
+ commands = [
+ "delete firewall ipv6-name V6-INGRESS",
+ "delete firewall ipv6-name V6-EGRESS",
+ "delete firewall name V4-INGRESS",
+ "delete firewall name V4-EGRESS",
+ "set firewall name V4-IN default-action 'accept'",
+ "set firewall name V4-IN description 'This is IPv4 INGRESS rule set'",
+ "set firewall name V4-IN enable-default-log",
+ "set firewall name V4-IN rule 1 protocol 'tcp'",
+ "set firewall name V4-IN rule 1 description 'Rule 1 is configured by Ansible RM'",
+ "set firewall name V4-IN rule 1 fragment 'match-frag'",
+ "set firewall name V4-IN rule 1",
+ "set firewall name V4-IN rule 1 action 'reject'",
+ "set firewall name V4-IN rule 1 ipsec 'match-ipsec'",
+ "set firewall name V4-IN rule 2 disabled",
+ "set firewall name V4-IN rule 2 action 'accept'",
+ "set firewall name V4-IN rule 2 protocol 'icmp'",
+ "set firewall name V4-IN rule 2 description 'Rule 102 is configured by Ansible RM'",
+ "set firewall name V4-IN rule 2",
+ "set firewall ipv6-name V6-IN default-action 'accept'",
+ "set firewall ipv6-name V6-IN description 'This rule-set is configured by Ansible RM'",
+ "set firewall ipv6-name V6-EG default-action 'reject'",
+ "set firewall ipv6-name V6-EG description 'This rule-set is configured by Ansible RM'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ afi="ipv4",
+ rule_sets=[
+ dict(
+ name="V4-INGRESS",
+ description="This is IPv4 V4-INGRESS rule set",
+ default_action="accept",
+ enable_default_log=True,
+ rules=[
+ dict(
+ number="101",
+ action="accept",
+ description="Rule 101 is configured by Ansible",
+ ipsec="match-ipsec",
+ protocol="icmp",
+ fragment="match-frag",
+ disabled=True,
+ )
+ ],
+ ),
+ dict(name="V4-EGRESS", default_action="reject",),
+ ],
+ ),
+ dict(
+ afi="ipv6",
+ rule_sets=[
+ dict(name="V6-INGRESS", default_action="accept",),
+ dict(name="V6-EGRESS", default_action="reject",),
+ ],
+ ),
+ ],
+ state="overridden",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
diff --git a/tests/unit/modules/network/vyos/test_vyos_static_route.py b/tests/unit/modules/network/vyos/test_vyos_static_route.py
index e020ca5..762508c 100644
--- a/tests/unit/modules/network/vyos/test_vyos_static_route.py
+++ b/tests/unit/modules/network/vyos/test_vyos_static_route.py
@@ -21,7 +21,7 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch
-from ansible_collections.vyos.vyos.plugins.modules import vyos_static_route
+from ansible.modules.network.vyos import _vyos_static_route
from ansible_collections.vyos.vyos.tests.unit.modules.utils import (
set_module_args,
)
@@ -30,18 +30,18 @@ from .vyos_module import TestVyosModule
class TestVyosStaticRouteModule(TestVyosModule):
- module = vyos_static_route
+ module = _vyos_static_route
def setUp(self):
super(TestVyosStaticRouteModule, self).setUp()
self.mock_get_config = patch(
- "ansible_collections.vyos.vyos.plugins.modules.vyos_static_route.get_config"
+ "ansible.modules.network.vyos._vyos_static_route.get_config"
)
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch(
- "ansible_collections.vyos.vyos.plugins.modules.vyos_static_route.load_config"
+ "ansible.modules.network.vyos._vyos_static_route.load_config"
)
self.load_config = self.mock_load_config.start()
diff --git a/tests/unit/modules/network/vyos/test_vyos_static_routes.py b/tests/unit/modules/network/vyos/test_vyos_static_routes.py
new file mode 100644
index 0000000..3646d61
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_static_routes.py
@@ -0,0 +1,293 @@
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch
+from ansible_collections.vyos.vyos.plugins.modules import vyos_static_routes
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import (
+ set_module_args,
+)
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosStaticRoutesModule(TestVyosModule):
+
+ module = vyos_static_routes
+
+ def setUp(self):
+ super(TestVyosStaticRoutesModule, self).setUp()
+ self.mock_get_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config"
+ )
+ self.get_config = self.mock_get_config.start()
+
+ self.mock_load_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config"
+ )
+ self.load_config = self.mock_load_config.start()
+
+ self.mock_get_resource_connection_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection"
+ )
+ self.get_resource_connection_config = (
+ self.mock_get_resource_connection_config.start()
+ )
+
+ self.mock_get_resource_connection_facts = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection"
+ )
+ self.get_resource_connection_facts = (
+ self.mock_get_resource_connection_facts.start()
+ )
+
+ self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data"
+ )
+ self.execute_show_command = self.mock_execute_show_command.start()
+
+ def tearDown(self):
+ super(TestVyosStaticRoutesModule, self).tearDown()
+ self.mock_get_resource_connection_config.stop()
+ self.mock_get_resource_connection_facts.stop()
+ self.mock_get_config.stop()
+ self.mock_load_config.stop()
+ self.mock_execute_show_command.stop()
+
+ def load_fixtures(self, commands=None):
+ def load_from_file(*args, **kwargs):
+ return load_fixture("vyos_static_routes_config.cfg")
+
+ self.execute_show_command.side_effect = load_from_file
+
+ def test_vyos_static_routes_merged(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.48/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="merged",
+ )
+ )
+ commands = [
+ "set protocols static route 192.0.2.48/28",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_static_routes_merged_idempotent(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.32/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="merged",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_static_routes_replaced(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.48/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="replaced",
+ )
+ )
+ commands = [
+ "set protocols static route 192.0.2.48/28",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_static_routes_replaced_idempotent(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.32/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="replaced",
+ )
+ )
+
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_static_routes_overridden(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.48/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="overridden",
+ )
+ )
+ commands = [
+ "delete protocols static route 192.0.2.32/28",
+ "set protocols static route 192.0.2.48/28",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'",
+ "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_static_routes_overridden_idempotent(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4",
+ routes=[
+ dict(
+ dest="192.0.2.32/28",
+ next_hops=[
+ dict(
+ forward_router_address="192.0.2.9"
+ ),
+ dict(
+ forward_router_address="192.0.2.10"
+ ),
+ ],
+ )
+ ],
+ )
+ ]
+ )
+ ],
+ state="overridden",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_static_routes_deleted(self):
+ set_module_args(
+ dict(
+ config=[
+ dict(
+ address_families=[
+ dict(
+ afi="ipv4", routes=[dict(dest="192.0.2.32/28")]
+ )
+ ]
+ )
+ ],
+ state="deleted",
+ )
+ )
+ commands = ["delete protocols static route 192.0.2.32/28"]
+ self.execute_module(changed=True, commands=commands)