diff options
author | CaptTrews <capttrews@gmail.com> | 2020-02-19 19:31:27 +0000 |
---|---|---|
committer | CaptTrews <capttrews@gmail.com> | 2020-02-19 19:31:27 +0000 |
commit | 45f223636c73ba69d3fea3c8aab8edd41de01388 (patch) | |
tree | c5ff33aa0e4770a13340a52b1e2bda7531541a5f | |
parent | f63b5c97edbf598f7b2a4c044386de3dddfda100 (diff) | |
download | vyos-ansible-collection-45f223636c73ba69d3fea3c8aab8edd41de01388.tar.gz vyos-ansible-collection-45f223636c73ba69d3fea3c8aab8edd41de01388.zip |
Updated from network content collector
Signed-off-by: CaptTrews <capttrews@gmail.com>
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) |