diff --git a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/__init__.py b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py new file mode 100644 index 0000000..f083485 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py @@ -0,0 +1,85 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# +""" +The arg spec for the vyos_firewall_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Firewall_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_firewall_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "access_rules": { + "elements": "dict", + "options": { + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + "rules": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "local", "out"], + "required": True, + "type": "str", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/firewall_interfaces/__init__.py b/plugins/module_utils/network/vyos/config/firewall_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py new file mode 100644 index 0000000..b436bfa --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py @@ -0,0 +1,455 @@ +# +# -*- 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_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + dict_diff, + remove_empties, + search_obj_in_list, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) + + +class Firewall_interfaces(ConfigBase): + """ + The vyos_firewall_interfaces class + """ + + gather_subset = [ + "!all", + "!min", + ] + + gather_network_resources = [ + "firewall_interfaces", + ] + + def __init__(self, module): + super(Firewall_interfaces, self).__init__(module) + + def get_firewall_interfaces_facts(self, data=None): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources, data=data + ) + firewall_interfaces_facts = facts["ansible_network_resources"].get( + "firewall_interfaces" + ) + if not firewall_interfaces_facts: + return [] + return firewall_interfaces_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_firewall_interfaces_facts = ( + self.get_firewall_interfaces_facts() + ) + else: + existing_firewall_interfaces_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend( + self.set_config(existing_firewall_interfaces_facts) + ) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_firewall_interfaces_facts = ( + self.get_firewall_interfaces_facts() + ) + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_firewall_interfaces_facts( + data=running_config + ) + else: + changed_firewall_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_firewall_interfaces_facts + if result["changed"]: + result["after"] = changed_firewall_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_firewall_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_firewall_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_firewall_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, w, h): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if ( + self.state in ("merged", "replaced", "overridden", "rendered") + and not w + ): + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + self.state + ) + ) + if self.state == "overridden": + commands.extend(self._state_overridden(w, h)) + elif self.state == "deleted": + commands.extend(self._state_deleted(w, h)) + elif w: + if self.state == "merged" or self.state == "rendered": + commands.extend(self._state_merged(w, h)) + elif self.state == "replaced": + commands.extend(self._state_replaced(w, h)) + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + for h in have: + w = search_obj_in_list(h["name"], want) + commands.extend(self._render_access_rules(h, w, opr=False)) + commands.extend(self._state_merged(want, have)) + return commands + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + for h_ar in have: + w_ar = search_obj_in_list(h_ar["name"], want) + if not w_ar and "access_rules" in h_ar: + commands.append( + self._compute_command(name=h_ar["name"], opr=False) + ) + else: + h_rules = h_ar.get("access_rules") or [] + key = "direction" + if w_ar: + w_rules = w_ar.get("access_rules") or [] + if not w_rules and h_rules: + commands.append( + self._compute_command( + name=h_ar["name"], opr=False + ) + ) + if h_rules: + for h_rule in h_rules: + w_rule = search_obj_in_list( + h_rule["afi"], w_rules, key="afi" + ) + have_rules = h_rule.get("rules") or [] + if w_rule: + want_rules = w_rule.get("rules") or [] + for h in have_rules: + if key in h: + w = search_obj_in_list( + h[key], want_rules, key=key + ) + if ( + not w + or key not in w + or ( + "name" in h + and w + and "name" not in w + ) + ): + commands.append( + self._compute_command( + afi=h_rule["afi"], + name=h_ar["name"], + attrib=h[key], + opr=False, + ) + ) + + commands.extend(self._state_merged(want, have)) + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + for w in want: + h = search_obj_in_list(w["name"], have) + commands.extend(self._render_access_rules(w, h)) + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for w in want: + h = search_obj_in_list(w["name"], have) + if h and "access_rules" in h: + commands.extend(self._delete_access_rules(w, h, opr=False)) + elif have: + for h in have: + if "access_rules" in h: + commands.append( + self._compute_command(name=h["name"], opr=False) + ) + return commands + + def _delete_access_rules(self, want, have, opr=False): + """ + This function forms the delete commands based on the 'opr' type + for 'access_rules' attributes. + :param want: desired config. + :param have: target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_rules = {} + w_rs = deepcopy(remove_empties(want)) + w_rules = w_rs.get("access_rules") or [] + if have: + h_rs = deepcopy(remove_empties(have)) + h_rules = h_rs.get("access_rules") or [] + + # if all firewall config needed to be deleted for specific interface + # when operation is delete. + if not w_rules and h_rules: + commands.append(self._compute_command(name=want["name"], opr=opr)) + if w_rules: + for w in w_rules: + h = search_obj_in_list(w["afi"], h_rules, key="afi") + commands.extend(self._delete_rules(want["name"], w, h)) + return commands + + def _delete_rules(self, name, want, have, opr=False): + """ + This function forms the delete commands based on the 'opr' type + for rules attributes. + :param name: interface id/name. + :param want: desired config. + :param have: target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_rules = [] + key = "direction" + w_rules = want.get("rules") or [] + if have: + h_rules = have.get("rules") or [] + # when rule set needed to be removed on + # (inbound|outbound|local interface) + if h_rules and not w_rules: + for h in h_rules: + if key in h: + commands.append( + self._compute_command( + afi=want["afi"], name=name, attrib=h[key], opr=opr + ) + ) + for w in w_rules: + h = search_obj_in_list(w[key], h_rules, key=key) + if ( + key in w + and h + and key in h + and "name" in w + and "name" in h + and w["name"] == h["name"] + ): + commands.append( + self._compute_command( + afi=want["afi"], + name=name, + attrib=w[key], + value=w["name"], + opr=opr, + ) + ) + return commands + + def _render_access_rules(self, want, have, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for 'access_rules' attributes. + :param want: desired config. + :param have: target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_rules = {} + w_rs = deepcopy(remove_empties(want)) + w_rules = w_rs.get("access_rules") or [] + if have: + h_rs = deepcopy(remove_empties(have)) + h_rules = h_rs.get("access_rules") or [] + if w_rules: + for w in w_rules: + h = search_obj_in_list(w["afi"], h_rules, key="afi") + commands.extend(self._render_rules(want["name"], w, h, opr)) + return commands + + def _render_rules(self, name, want, have, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for rules attributes. + :param name: interface id/name. + :param want: desired config. + :param have: target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_rules = [] + key = "direction" + w_rules = want.get("rules") or [] + if have: + h_rules = have.get("rules") or [] + for w in w_rules: + h = search_obj_in_list(w[key], h_rules, key=key) + if key in w: + if opr: + if "name" in w and not ( + h and h[key] == w[key] and h["name"] == w["name"] + ): + commands.append( + self._compute_command( + afi=want["afi"], + name=name, + attrib=w[key], + value=w["name"], + ) + ) + elif not (h and key in h): + commands.append( + self._compute_command( + afi=want["afi"], name=name, attrib=w[key] + ) + ) + elif not opr: + if ( + not h + or key not in h + or ("name" in w and h and "name" not in h) + ): + commands.append( + self._compute_command( + afi=want["afi"], + name=name, + attrib=w[key], + opr=opr, + ) + ) + return commands + + def _compute_command( + self, afi=None, name=None, attrib=None, value=None, opr=True + ): + """ + This function construct the add/delete command based on passed attributes. + :param afi: address type. + :param name: interface name. + :param attrib: attribute name. + :param value: attribute value. + :param opr: operation flag. + :return: generated command. + """ + if not opr: + cmd = "delete interfaces ethernet" + " " + name + " firewall" + else: + cmd = "set interfaces ethernet" + " " + name + " firewall" + if attrib: + cmd += " " + attrib + if afi: + cmd += " " + self._get_fw_type(afi) + if value: + cmd += " '" + str(value) + "'" + return cmd + + def _get_fw_type(self, afi): + """ + This function returns the firewall rule-set type based on IP address. + :param afi: address type + :return: rule-set type. + """ + return "ipv6-name" if afi == "ipv6" else "name" diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 6e6a82b..ff3d098 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -1,87 +1,92 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The facts class for vyos this file validates each subset of facts and selectively calls the appropriate facts gathering function """ from __future__ import absolute_import, division, print_function __metaclass__ = type + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( FactsBase, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( InterfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( L3_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( Lag_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( Lldp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( Firewall_rulesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( Static_routesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import ( Firewall_globalFacts, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import ( + Firewall_interfacesFacts, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Default, Neighbors, Config, ) FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts, lag_interfaces=Lag_interfacesFacts, lldp_global=Lldp_globalFacts, lldp_interfaces=Lldp_interfacesFacts, static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts, firewall_global=Firewall_globalFacts, + firewall_interfaces=Firewall_interfacesFacts, ) class Facts(FactsBase): """ The fact class for vyos """ VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) def __init__(self, module): super(Facts, self).__init__(module) def get_facts( self, legacy_facts_type=None, resource_facts_type=None, data=None ): """ Collect the facts for vyos :param legacy_facts_type: List of legacy facts types :param resource_facts_type: List of resource fact types :param data: previously collected conf :rtype: dict :return: the facts gathered """ if self.VALID_RESOURCE_SUBSETS: self.get_network_resources_facts( FACT_RESOURCE_SUBSETS, resource_facts_type, data ) if self.VALID_LEGACY_GATHER_SUBSETS: self.get_network_legacy_facts( FACT_LEGACY_SUBSETS, legacy_facts_type ) return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/facts/firewall_interfaces/__init__.py b/plugins/module_utils/network/vyos/facts/firewall_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py new file mode 100644 index 0000000..4640748 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py @@ -0,0 +1,196 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos firewall_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( + Firewall_interfacesArgs, +) + + +class Firewall_interfacesFacts(object): + """ The vyos firewall_interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Firewall_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection): + return connection.get_config() + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for firewall_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section ^interface') + # using mock data instead + data = self.get_device_data(connection) + objs = [] + interfaces = findall( + r"^set interfaces ethernet (?:\'*)(\S+)(?:\'*)", data, M + ) + if interfaces: + objs = self.get_names(data, interfaces) + ansible_facts["ansible_network_resources"].pop( + "firewall_interfaces", None + ) + facts = {} + if objs: + facts["firewall_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["firewall_interfaces"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def get_names(self, data, interfaces): + """ + This function performs following: + - Form regex to fetch 'interface name' from interfaces firewall data. + - Form the name list. + :param data: configuration. + :param rules: list of interfaces. + :return: generated firewall interfaces configuration. + """ + names = [] + for r in set(interfaces): + int_regex = r" %s .+$" % r.strip("'") + cfg = findall(int_regex, data, M) + fi = self.render_config(cfg) + fi["name"] = r.strip("'") + names.append(fi) + if names: + names = sorted(names, key=lambda i: i["name"]) + return names + + def render_config(self, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + conf = "\n".join(filter(lambda x: "firewall" in x, conf)) + config = {"access_rules": self.parse_access_rules(conf)} + return config + + def parse_access_rules(self, conf): + """ + This function forms the regex to fetch the 'access-rules' + for specific interface. + :param conf: configuration data. + :return: generated access-rules list configuration. + """ + ar_lst = [] + v4_ar = findall(r"^.*(in|out|local) name .*$", conf, M) + v6_ar = findall(r"^.*(in|out|local) ipv6-name .*$", conf, M) + if v4_ar: + v4_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " name", conf, M)) + config = self.parse_int_rules(v4_conf, "ipv4") + if config: + ar_lst.append(config) + if v6_ar: + v6_conf = "\n".join( + findall(r"(^.*?%s.*?$)" % " ipv6-name", conf, M) + ) + config = self.parse_int_rules(v6_conf, "ipv6") + if config: + ar_lst.append(config) + if ar_lst: + ar_lst = sorted(ar_lst, key=lambda i: i["afi"]) + else: + empty_rules = findall(r"^.*(in|out|local).*", conf, M) + if empty_rules: + ar_lst.append({"afi": "ipv4", "rules": []}) + ar_lst.append({"afi": "ipv6", "rules": []}) + return ar_lst + + def parse_int_rules(self, conf, afi): + """ + This function forms the regex to fetch the 'access-rules' + for specific interface based on ip-type. + :param conf: configuration data. + :param rules: rules configured per interface. + :param afi: ip address type. + :return: generated rule configuration dictionary. + """ + r_lst = [] + config = {} + rules = ["in", "out", "local"] + for r in set(rules): + fr = {} + r_regex = r" %s .+$" % r + cfg = "\n".join(findall(r_regex, conf, M)) + if cfg: + fr = self.parse_rules(cfg, afi) + else: + out = search(r"^.*firewall " + "'" + r + "'" + "(.*)", conf, M) + if out: + fr = {"direction": r} + if fr: + r_lst.append(fr) + if r_lst: + r_lst = sorted(r_lst, key=lambda i: i["direction"]) + config = {"afi": afi, "rules": r_lst} + return config + + def parse_rules(self, conf, afi): + """ + This function triggers the parsing of 'rule' attributes. + a_lst is a list having rule attributes which doesn't + have further sub attributes. + :param conf: configuration. + :param afi: ip address type. + :return: generated rule configuration dictionary. + """ + cfg = {} + out = findall(r"[^\s]+", conf, M) + if out: + cfg["direction"] = out[0].strip("'") + if afi == "ipv6": + out = findall(r"[^\s]+ ipv6-name (?:\'*)(\S+)(?:\'*)", conf, M) + if out: + cfg["name"] = str(out[0]).strip("'") + else: + out = findall(r"[^\s]+ name (?:\'*)(\S+)(?:\'*)", conf, M) + if out: + cfg["name"] = out[-1].strip("'") + return cfg diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 402adfc..4635234 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -1,231 +1,232 @@ # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # utils from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( ipaddress, ) def search_obj_in_list(name, lst, key="name"): - for item in lst: - if item[key] == name: - return item + if lst: + for item in lst: + if item[key] == name: + return item return None def get_interface_type(interface): """Gets the type of interface """ if interface.startswith("eth"): return "ethernet" elif interface.startswith("bond"): return "bonding" elif interface.startswith("vti"): return "vti" elif interface.startswith("lo"): return "loopback" def dict_delete(base, comparable): """ This function generates a dict containing key, value pairs for keys that are present in the `base` dict but not present in the `comparable` dict. :param base: dict object to base the diff on :param comparable: dict object to compare against base :returns: new dict object with key, value pairs that needs to be deleted. """ to_delete = dict() for key in base: if isinstance(base[key], dict): sub_diff = dict_delete(base[key], comparable.get(key, {})) if sub_diff: to_delete[key] = sub_diff else: if key not in comparable: to_delete[key] = base[key] return to_delete def diff_list_of_dicts(want, have): diff = [] set_w = set(tuple(d.items()) for d in want) set_h = set(tuple(d.items()) for d in have) difference = set_w.difference(set_h) for element in difference: diff.append(dict((x, y) for x, y in element)) return diff def get_lst_diff_for_dicts(want, have, lst): """ This function generates a list containing values that are only in want and not in list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the diff on :return: new list object with values which are only in want. """ if not have: diff = want.get(lst) or [] else: want_elements = want.get(lst) or {} have_elements = have.get(lst) or {} diff = list_diff_want_only(want_elements, have_elements) return diff def get_lst_same_for_dicts(want, have, lst): """ This function generates a list containing values that are common for list in want and list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the comparison on :return: new list object with values which are common in want and have. """ diff = None if want and have: want_list = want.get(lst) or {} have_list = have.get(lst) or {} diff = [ i for i in want_list and have_list if i in have_list and i in want_list ] return diff def list_diff_have_only(want_list, have_list): """ This function generated the list containing values that are only in have list. :param want_list: :param have_list: :return: new list with values which are only in have list """ if have_list and not want_list: diff = have_list elif not have_list: diff = None else: diff = [ i for i in have_list + want_list if i in have_list and i not in want_list ] return diff def list_diff_want_only(want_list, have_list): """ This function generated the list containing values that are only in want list. :param want_list: :param have_list: :return: new list with values which are only in want list """ if have_list and not want_list: diff = None elif not have_list: diff = want_list else: diff = [ i for i in have_list + want_list if i in want_list and i not in have_list ] return diff def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2): """ This function return the dict object if it exist in list. :param d_val1: :param d_val2: :param lst: :param key1: :param key2: :return: """ obj = next( ( item for item in lst if item[key1] == d_val1 and item[key2] == d_val2 ), None, ) if obj: return obj else: return None def key_value_in_dict(have_key, have_value, want_dict): """ This function checks whether the key and values exist in dict :param have_key: :param have_value: :param want_dict: :return: """ for key, value in iteritems(want_dict): if key == have_key and value == have_value: return True return False def is_dict_element_present(dict, key): """ This function checks whether the key is present in dict. :param dict: :param key: :return: """ for item in dict: if item == key: return True return False def get_ip_address_version(address): """ This function returns the version of IP address :param address: IP address :return: """ try: address = unicode(address) except NameError: address = str(address) version = ipaddress.ip_address(address.split("/")[0]).version return version def get_route_type(address): """ This function returns the route type based on IP address :param address: :return: """ version = get_ip_address_version(address) if version == 6: return "route6" elif version == 4: return "route" diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index eec4c3b..4a64066 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -1,174 +1,175 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_facts """ ANSIBLE_METADATA = { "metadata_version": "1.1", "status": [u"preview"], "supported_by": "network", } DOCUMENTATION = """module: vyos_facts short_description: Get facts about vyos devices. description: - Collects facts from network devices running the vyos operating system. This module places the facts gathered in the fact tree keyed by the respective resource name. The facts module will always collect a base set of facts from the device and can enable or disable collection of additional facts. author: - Nathaniel Case (@qalthos) - Nilashish Chakraborty (@Nilashishc) - Rohit Thakur (@rohitthakur2590) extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: gather_subset: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include all, default, config, and neighbors. Can specify a list of values to include a larger subset. Values can also be used with an initial C(M(!)) to specify that a specific subset should not be collected. required: false default: '!config' gather_network_resources: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include all and the resources like interfaces. Can specify a list of values to include a larger subset. Values can also be used with an initial C(M(!)) to specify that a specific subset should not be collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', - 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global'. + 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', + 'firewall_interfaces'. required: false """ EXAMPLES = """ # Gather all facts - vyos_facts: gather_subset: all gather_network_resources: all # collect only the config and default facts - vyos_facts: gather_subset: config # collect everything exception the config - vyos_facts: gather_subset: "!config" # Collect only the interfaces facts - vyos_facts: gather_subset: - '!all' - '!min' gather_network_resources: - interfaces # Do not collect interfaces facts - vyos_facts: gather_network_resources: - "!interfaces" # Collect interfaces and minimal default facts - vyos_facts: gather_subset: min gather_network_resources: interfaces """ RETURN = """ ansible_net_config: description: The running-config from the device returned: when config is configured type: str ansible_net_commits: description: The set of available configuration revisions returned: when present type: list ansible_net_hostname: description: The configured system hostname returned: always type: str ansible_net_model: description: The device model string returned: always type: str ansible_net_serialnum: description: The serial number of the device returned: always type: str ansible_net_version: description: The version of the software running returned: always type: str ansible_net_neighbors: description: The set of LLDP neighbors returned: when interface is configured type: list ansible_net_gather_subset: description: The list of subsets gathered by the module returned: always type: list ansible_net_api: description: The name of the transport returned: always type: str ansible_net_python_version: description: The Python version Ansible controller is using returned: always type: str ansible_net_gather_network_resources: description: The list of fact resource subsets collected from the device returned: always type: list """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, ) def main(): """ Main entry point for module execution :returns: ansible_facts """ argument_spec = FactsArgs.argument_spec argument_spec.update(vyos_argument_spec) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) warnings = [] if module.params["gather_subset"] == "!config": warnings.append( "default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards" ) result = Facts(module).get_facts() ansible_facts, additional_warnings = result warnings.extend(additional_warnings) module.exit_json(ansible_facts=ansible_facts, warnings=warnings) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_interfaces.py b/plugins/modules/vyos_firewall_interfaces.py new file mode 100644 index 0000000..1c2ce98 --- /dev/null +++ b/plugins/modules/vyos_firewall_interfaces.py @@ -0,0 +1,1289 @@ +#!/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_interfaces +""" + +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_interfaces +short_description: Manage firewall rules attributes of interfaces on VyOS devices +description: Manage firewall rules of interfaces on VyOS network devices. +author: +- Rohit Thakur (@rohitthakur2590) +options: + config: + description: A list of firewall rules options for interfaces. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier for the interface. + type: str + required: true + access_rules: + description: + - Specifies firewall rules attached to the interfaces. + type: list + elements: dict + suboptions: + afi: + description: + - Specifies the AFI for the Firewall rules to be configured on this interface. + type: str + choices: + - ipv4 + - ipv6 + required: true + rules: + description: + - Specifies the firewall rules for the provided AFI. + type: list + elements: dict + suboptions: + name: + description: + - Specifies the name of the IPv4/IPv6 Firewall rule for the interface. + type: str + direction: + description: + - Specifies the direction of packets that the firewall rule will be + applied on. + type: str + choices: + - in + - local + - out + required: true + running_config: + description: + - The module, by default, will connect to the remote device and retrieve the current + running-config to use as a base for comparing against the contents of source. + There are times when it is not desirable to have the task get the current running-config + for every task in a playbook. The I(running_config) argument allows the implementer + to pass in the configuration to use as the base config for comparison. This + value of this option should be the output received from device by executing + command C(show configuration commands | grep 'firewall' + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - rendered + - gathered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# +- name: Merge the provided configuration with the existing running configuration + vyos_firewall_interfaces: + config: + - access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'in' + - name: 'OUTBOUND' + direction: 'out' + - name: 'LOCAL' + direction: 'local' + - afi: 'ipv6' + rules: + - name: 'V6-LOCAL' + direction: 'local' + name: 'eth1' + - access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'in' + - name: 'OUTBOUND' + direction: 'out' + - name: 'LOCAL' + direction: 'local' + - afi: 'ipv6' + rules: + - name: 'V6-LOCAL' + direction: 'local' + name: 'eth3' + state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# "name": "eth0" +# }, +# { +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ] +# +# "commands": [ +# "set interfaces ethernet eth1 firewall in name 'INBOUND'", +# "set interfaces ethernet eth1 firewall out name 'OUTBOUND'", +# "set interfaces ethernet eth1 firewall local name 'LOCAL'", +# "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'", +# "set interfaces ethernet eth3 firewall in name 'INBOUND'", +# "set interfaces ethernet eth3 firewall out name 'OUTBOUND'", +# "set interfaces ethernet eth3 firewall local name 'LOCAL'", +# "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'" +# ] +# +# "after": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' + + +# Using merged +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' +# +- name: Merge the provided configuration with the existing running configuration + vyos_firewall_interfaces: + config: + - access_rules: + - afi: 'ipv4' + rules: + - name: 'OUTBOUND' + direction: 'in' + - name: 'INBOUND' + direction: 'out' + name: 'eth1' + state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# "commands": [ +# "set interfaces ethernet eth1 firewall in name 'OUTBOUND'", +# "set interfaces ethernet eth1 firewall out name 'INBOUND'" +# ] +# +# "after": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "OUTBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "INBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'OUTBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'INBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' +# +- name: Replace device configurations of listed firewall interfaces with provided configurations + vyos_firewall_interfaces: + config: + - name: 'eth1' + access_rules: + - afi: 'ipv4' + rules: + - name: 'OUTBOUND' + direction: 'out' + - afi: 'ipv6' + rules: + - name: 'V6-LOCAL' + direction: 'local' + - name: 'eth3' + access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'in' + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# "commands": [ +# "delete interfaces ethernet eth1 firewall in name", +# "delete interfaces ethernet eth1 firewall local name", +# "delete interfaces ethernet eth3 firewall local name", +# "delete interfaces ethernet eth3 firewall out name", +# "delete interfaces ethernet eth3 firewall local ipv6-name" +# ] +# +# "after": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall 'in' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall 'local' +# set interfaces ethernet eth3 firewall 'out' + + +# Using overridden +# +# Before state +# -------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall 'in' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall 'local' +# set interfaces ethernet eth3 firewall 'out' +# +- name: Overrides all device configuration with provided configuration + vyos_firewall_interfaces: + config: + - name: 'eth3' + access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'out' + state: overridden +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before":[ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# "commands": [ +# "delete interfaces ethernet eth1 firewall", +# "delete interfaces ethernet eth3 firewall in name", +# "set interfaces ethernet eth3 firewall out name 'INBOUND'" +# +# +# "after": [ +# { +# "name": "eth0" +# }, +# { +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "out", +# "name": "INBOUND" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# +# After state +# ------------ +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth3 firewall 'in' +# set interfaces ethernet eth3 firewall 'local' +# set interfaces ethernet eth3 firewall out name 'INBOUND' + + +# Using deleted per interface name +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' +# +- name: Delete firewall interfaces based on interface name. + vyos_firewall_interfaces: + config: + - name: 'eth1' + - name: 'eth3' + state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# "commands": [ +# "delete interfaces ethernet eth1 firewall", +# "delete interfaces ethernet eth3 firewall" +# ] +# +# "after": [ +# { +# "name": "eth0" +# }, +# { +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' + + +# Using deleted per afi +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' +# +- name: Delete firewall interfaces config per afi. + vyos_firewall_interfaces: + config: + - name: 'eth1' + access_rules: + - afi: 'ipv4' + - afi: 'ipv6' + state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "commands": [ +# "delete interfaces ethernet eth1 firewall in name", +# "delete interfaces ethernet eth1 firewall out name", +# "delete interfaces ethernet eth1 firewall local name", +# "delete interfaces ethernet eth1 firewall local ipv6-name" +# ] +# +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' + + +# Using deleted without config +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall in name 'INBOUND' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall local name 'LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth3 firewall local name 'LOCAL' +# set interfaces ethernet eth3 firewall out name 'OUTBOUND' +# +- name: Delete firewall interfaces config when empty config provided. + vyos_firewall_interfaces: + config: + state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "commands": [ +# "delete interfaces ethernet eth1 firewall", +# "delete interfaces ethernet eth1 firewall" +# ] +# +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' + + +# Using parsed +# +# +- name: Parse the provided configuration + vyos_firewall_interfaces: + running_config: + "set interfaces ethernet eth1 firewall in name 'INBOUND' + set interfaces ethernet eth1 firewall out name 'OUTBOUND' + set interfaces ethernet eth1 firewall local name 'LOCAL' + set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' + set interfaces ethernet eth2 firewall in name 'INBOUND' + set interfaces ethernet eth2 firewall out name 'OUTBOUND' + set interfaces ethernet eth2 firewall local name 'LOCAL' + set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# }, +# { +# "direction": "local", +# "name": "LOCAL" +# }, +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ] + + +# Using gathered +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall 'in' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall 'local' +# set interfaces ethernet eth3 firewall 'out' +# +- name: Gather listed firewall interfaces. + vyos_firewall_interfaces: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "name": "eth0" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "out", +# "name": "OUTBOUND" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "rules": [ +# { +# "direction": "local", +# "name": "V6-LOCAL" +# } +# ] +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "access_rules": [ +# { +# "afi": "ipv4", +# "rules": [ +# { +# "direction": "in", +# "name": "INBOUND" +# } +# ] +# } +# ], +# "name": "eth3" +# } +# ] +# +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall ipv6-name 'V6-LOCAL' +# set firewall name 'INBOUND' +# set firewall name 'LOCAL' +# set firewall name 'OUTBOUND' +# set interfaces ethernet eth1 firewall 'in' +# set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +# set interfaces ethernet eth1 firewall out name 'OUTBOUND' +# set interfaces ethernet eth3 firewall in name 'INBOUND' +# set interfaces ethernet eth3 firewall 'local' +# set interfaces ethernet eth3 firewall 'out' + + +# Using rendered +# +# +- name: Render the commands for provided configuration + vyos_firewall_interfaces: + config: + - name: 'eth2' + access_rules: + - afi: 'ipv4' + rules: + - direction: 'in' + name: 'INGRESS' + - direction: 'out' + name: 'OUTGRESS' + - direction: 'local' + name: 'DROP' + state: rendered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +# "set interfaces ethernet eth2 firewall in name 'INGRESS'", +# "set interfaces ethernet eth2 firewall out name 'OUTGRESS'", +# "set interfaces ethernet eth2 firewall local name 'DROP'", +# "set interfaces ethernet eth2 firewall local ipv6-name 'LOCAL'" +# ] + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'" + - "set interfaces ethernet eth3 firewall in name 'INBOUND'" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( + Firewall_interfacesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_interfaces.firewall_interfaces import ( + Firewall_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Firewall_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Firewall_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/vyos_firewall_interfaces/1 b/tests/integration/targets/vyos_firewall_interfaces/1 new file mode 100644 index 0000000..70ae90f --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/1 @@ -0,0 +1,120 @@ +--- +merged: + before: [] + + + commands: + - "set interfaces ethernet eth1 firewall in name 'INBOUND'" + - "set interfaces ethernet eth1 firewall out name 'OUTBOUND'" + - "set interfaces ethernet eth1 firewall local name 'LOCAL'" + - "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'" + - "set interfaces ethernet eth3 firewall in name 'INBOUND'" + - "set interfaces ethernet eth3 firewall out name 'OUTBOUND'" + - "set interfaces ethernet eth3 firewall local name 'LOCAL'" + - "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'" + + after: + - name: 'eth1' + access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'in' + - name: 'OUBOUND' + direction: 'out' + - afi: 'ipv6' + rules: + - name: 'V6-LOCAL' + direction: 'local' + +populate: + - name: 'eth1' + access_rules: + - afi: 'ipv4' + rules: + - name: 'INBOUND' + direction: 'in' + - name: 'OUBOUND' + direction: 'out' + - afi: 'ipv6' + rules: + - name: 'LOCAL' + direction: 'local' + +replaced: + commands: + - "delete service lldp interface eth2 location" + - "set service lldp interface eth2 'disable'" + - "set service lldp interface eth2 location civic-based country-code 'US'" + - "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'" + - "delete service lldp interface eth1 location" + - "set service lldp interface eth1 'disable'" + - "set service lldp interface eth1 location coordinate-based latitude '33.524449N'" + - "set service lldp interface eth1 location coordinate-based altitude '2200'" + - "set service lldp interface eth1 location coordinate-based datum 'WGS84'" + - "set service lldp interface eth1 location coordinate-based longitude '222.267255W'" + + after: + - name: 'eth2' + enable: false + location: + civic_based: + country_code: 'US' + ca_info: + - ca_type: 0 + ca_value: 'ENGLISH' + + - name: 'eth1' + enable: false + location: + coordinate_based: + altitude: 2200 + datum: 'WGS84' + longitude: '222.267255W' + latitude: '33.524449N' + +populate_intf: + - name: 'eth2' + enable: false + location: + civic_based: + country_code: 'US' + ca_info: + - ca_type: 0 + ca_value: 'ENGLISH' + +overridden: + commands: + - "delete service lldp interface eth2 location" + - "delete service lldp interface eth2 'disable'" + - "set service lldp interface eth2 location elin '0000000911'" + + after: + - name: 'eth2' + location: + elin: 0000000911 + +deleted: + commands: + - "delete service lldp interface eth1" + - "delete service lldp interface eth2" + + after: [] + +round_trip: + after: + - name: 'eth1' + location: + civic_based: + country_code: 'US' + ca_info: + - ca_type: 0 + ca_value: 'ENGLISH' + + - name: 'eth2' + location: + coordinate_based: + altitude: 2200 + datum: 'WGS84' + longitude: '222.267255W' + latitude: '33.524449N' diff --git a/tests/integration/targets/vyos_firewall_interfaces/defaults/main.yaml b/tests/integration/targets/vyos_firewall_interfaces/defaults/main.yaml new file mode 100644 index 0000000..852a6be --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: '[^_].*' +test_items: [] diff --git a/tests/integration/targets/vyos_firewall_interfaces/meta/main.yaml b/tests/integration/targets/vyos_firewall_interfaces/meta/main.yaml new file mode 100644 index 0000000..7413320 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_vyos_tests diff --git a/tests/integration/targets/vyos_firewall_interfaces/tasks/cli.yaml b/tests/integration/targets/vyos_firewall_interfaces/tasks/cli.yaml new file mode 100644 index 0000000..93eb2fe --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/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_interfaces/tasks/main.yaml b/tests/integration/targets/vyos_firewall_interfaces/tasks/main.yaml new file mode 100644 index 0000000..a3db933 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tasks/main.yaml @@ -0,0 +1,4 @@ +--- +- include: cli.yaml + tags: + - cli diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_parsed_config.cfg new file mode 100644 index 0000000..54696e8 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_parsed_config.cfg @@ -0,0 +1,10 @@ +set interfaces ethernet eth1 firewall in name 'INBOUND' +set interfaces ethernet eth1 firewall out name 'OUTBOUND' +set interfaces ethernet eth1 firewall local name 'LOCAL' +set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth2 firewall in name 'INBOUND' +set interfaces ethernet eth2 firewall out name 'OUTBOUND' +set interfaces ethernet eth2 firewall local name 'LOCAL' +set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth0 + diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate.yaml new file mode 100644 index 0000000..3e60c41 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate.yaml @@ -0,0 +1,12 @@ +--- +- name: Setup + vars: + lines: "set interfaces ethernet eth1 firewall in name 'INBOUND' \nset interfaces\ + \ ethernet eth1 firewall out name 'OUTBOUND' \nset interfaces ethernet eth1\ + \ firewall local name 'LOCAL' \nset interfaces ethernet eth1 firewall local\ + \ ipv6-name 'V6-LOCAL'\nset interfaces ethernet eth2 firewall in name 'INBOUND'\n\ + set interfaces ethernet eth2 firewall out name 'OUTBOUND'\nset interfaces\ + \ ethernet eth2 firewall local name 'LOCAL' \nset interfaces ethernet eth2\ + \ firewall local ipv6-name 'V6-LOCAL'\n" + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate_rule_sets.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate_rule_sets.yaml new file mode 100644 index 0000000..40a7f4a --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_populate_rule_sets.yaml @@ -0,0 +1,7 @@ +--- +- name: Setup + vars: + lines: "set firewall name 'INBOUND'\nset firewall name 'OUTBOUND'\nset firewall\ + \ name 'LOCAL'\nset firewall ipv6-name 'V6-LOCAL'\n" + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml new file mode 100644 index 0000000..38a1800 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml @@ -0,0 +1,7 @@ +--- +- name: Remove Config + vars: + lines: "delete interfaces ethernet eth1 firewall\ndelete interfaces ethernet\ + \ eth2 firewall\n" + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml new file mode 100644 index 0000000..0844749 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml @@ -0,0 +1,7 @@ +--- +- name: Remove Config + vars: + lines: "delete firewall name INBOUND\ndelete firewall name OUTBOUND\ndelete\ + \ firewall name LOCAL\ndelete firewall ipv6-name V6-LOCAL\n" + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted.yaml new file mode 100644 index 0000000..078dd89 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted.yaml @@ -0,0 +1,58 @@ +--- +- debug: + msg: Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Delete attributes of given firewall rules. + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + + - name: eth2 + 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['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted['after'] | symmetric_difference(result['after']) |length\ + \ == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *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['after'] | symmetric_difference(result['before']) |length\ + \ == 0 }}" + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_afi.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_afi.yaml new file mode 100644 index 0000000..f7db06b --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_afi.yaml @@ -0,0 +1,68 @@ +--- +- debug: + msg: Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Delete firewall interfaces based on IP address type provided. + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + + - afi: ipv6 + + - name: eth2 + access_rules: + + - 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['commands'] | symmetric_difference(result['commands'])\ + \ |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_afi['after'] | symmetric_difference(result['after']) |length\ + \ == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *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['after'] | symmetric_difference(result['before']) |length\ + \ == 0 }}" + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_all.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_all.yaml new file mode 100644 index 0000000..d85654c --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_all.yaml @@ -0,0 +1,54 @@ +--- +- debug: + msg: Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Delete all the firewall interfaces. + register: result + vyos.vyos.vyos_firewall_interfaces: &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['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted['after'] | symmetric_difference(result['after']) |length\ + \ == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *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['after'] | symmetric_difference(result['before']) |length\ + \ == 0 }}" + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_single.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_single.yaml new file mode 100644 index 0000000..f7e91f8 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/deleted_single.yaml @@ -0,0 +1,63 @@ +--- +- debug: + msg: Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Delete firewall interface. + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - direction: in + 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_single['commands'] | symmetric_difference(result['commands'])\ + \ |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_single['after'] | symmetric_difference(result['after'])\ + \ |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *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_single['after'] | symmetric_difference(result['before'])\ + \ |length == 0 }}" + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/empty_config.yaml new file mode 100644 index 0000000..66c49bf --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/empty_config.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_firewall_interfaces 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_interfaces: + 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_interfaces: + 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_interfaces: + 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_interfaces: + 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_interfaces: + 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_interfaces/tests/cli/gathered.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/gathered.yaml new file mode 100644 index 0000000..fd8dd74 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/gathered.yaml @@ -0,0 +1,40 @@ +--- +- debug: + msg: START vyos_firewall_interfaces gathered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_firewall_interfaces: &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_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged.yaml new file mode 100644 index 0000000..0c28eab --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged.yaml @@ -0,0 +1,96 @@ +--- +- debug: + msg: START vyos_firewall_interfaces merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _remove_config.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - direction: in + name: INBOUND + + - direction: local + name: LOCAL + + - direction: out + name: OUTBOUND + + - afi: ipv6 + rules: + + - direction: local + name: V6-LOCAL + + - name: eth2 + access_rules: + + - afi: ipv4 + rules: + + - direction: in + name: INBOUND + + - direction: local + name: LOCAL + + - direction: out + name: OUTBOUND + + - afi: ipv6 + rules: + + - direction: local + name: V6-LOCAL + 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_interfaces: *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 + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged_edit.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged_edit.yaml new file mode 100644 index 0000000..873f4c4 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/merged_edit.yaml @@ -0,0 +1,70 @@ +--- +- debug: + msg: START vyos_firewall_interfaces merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - direction: in + name: OUTBOUND + + - direction: out + name: INBOUND + state: merged + + - name: Assert that before dicts were correctly generated + assert: + that: "{{ populate | symmetric_difference(result['before']) |length == 0\ + \ }}" + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ merged_edit['commands'] | symmetric_difference(result['commands'])\ + \ |length == 0 }}" + + - name: Assert that after dicts was correctly generated + assert: + that: + - "{{ merged_edit['after'] | symmetric_difference(result['after']) |length\ + \ == 0 }}" + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *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_edit['after'] | symmetric_difference(result['before']) |length\ + \ == 0 }}" + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/overridden.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/overridden.yaml new file mode 100644 index 0000000..01d30b6 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/overridden.yaml @@ -0,0 +1,66 @@ +--- +- debug: + msg: START vyos_firewall_interfaces overridden integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Overrides all device configuration with provided configuration + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth2 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: out + 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_interfaces: *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 + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/parsed.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/parsed.yaml new file mode 100644 index 0000000..c7032e1 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/parsed.yaml @@ -0,0 +1,47 @@ +--- +- debug: + msg: START vyos_firewall_interfaces parsed integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Gather firewall_interfaces facts + register: firewall_interfaces_facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - firewall_interfaces + + - name: Provide the running configuration for parsing (config to be parsed) + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + running_config: "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + + - name: Assert that correct parsing done + assert: + that: "{{ ansible_facts['network_resources']['firewall_interfaces'] | symmetric_difference(result['parsed'])\ + \ |length == 0 }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + register: result + vyos.vyos.vyos_firewall_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: + + - include_tasks: _remove_config.yaml + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rendered.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rendered.yaml new file mode 100644 index 0000000..022dd50 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rendered.yaml @@ -0,0 +1,72 @@ +--- +- debug: + msg: START vyos_firewall_interfaces rendered integration tests on connection={{ + ansible_connection }} + +- block: + + - name: Structure provided configuration into device specific commands + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: in + + - name: OUTBOUND + direction: out + + - name: LOCAL + direction: local + + - afi: ipv6 + rules: + + - name: V6-LOCAL + direction: local + + - name: eth2 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: in + + - name: OUTBOUND + direction: out + + - name: LOCAL + direction: local + + - afi: ipv6 + rules: + + - name: V6-LOCAL + direction: local + 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_interfaces: *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_interfaces/tests/cli/replaced.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/replaced.yaml new file mode 100644 index 0000000..b2b0067 --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/replaced.yaml @@ -0,0 +1,83 @@ +--- +- debug: + msg: START vyos_firewall_interfaces replaced integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Replace device configurations of listed firewall rules with provided + configurations + register: result + vyos.vyos.vyos_firewall_interfaces: &id001 + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - name: OUTBOUND + direction: out + + - afi: ipv6 + rules: + + - name: V6-LOCAL + direction: local + + - name: eth2 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: in + 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_interfaces: *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 + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rtt.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rtt.yaml new file mode 100644 index 0000000..9155b7c --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/rtt.yaml @@ -0,0 +1,94 @@ +--- +- debug: + msg: START vyos_firewall_interfaces round trip integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_firewall_config.yaml + +- include_tasks: _populate_rule_sets.yaml + +- block: + + - name: Apply the provided configuration (base config) + register: base_config + vyos.vyos.vyos_firewall_interfaces: + config: + + - name: eth1 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: in + + - name: OUTBOUND + direction: out + + - name: LOCAL + direction: local + + - afi: ipv6 + rules: + + - name: V6-LOCAL + direction: local + state: merged + + - name: Gather firewall_interfaces facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - firewall_interfaces + + - name: Apply the provided configuration (config to be reverted) + register: result + vyos.vyos.vyos_firewall_interfaces: + config: + + - name: eth2 + access_rules: + + - afi: ipv4 + rules: + + - name: INBOUND + direction: in + + - name: OUTBOUND + direction: out + + - name: LOCAL + direction: local + + - afi: ipv6 + rules: + + - name: V6-LOCAL + direction: local + 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_interfaces: + config: "{{ ansible_facts['network_resources']['firewall_interfaces'] }}" + 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 + + - include_tasks: _remove_firewall_config.yaml diff --git a/tests/integration/targets/vyos_firewall_interfaces/vars/main.yaml b/tests/integration/targets/vyos_firewall_interfaces/vars/main.yaml new file mode 100644 index 0000000..45be6db --- /dev/null +++ b/tests/integration/targets/vyos_firewall_interfaces/vars/main.yaml @@ -0,0 +1,279 @@ +--- +merged: + before: + - name: eth0 + - name: eth1 + - name: eth2 + commands: + - set interfaces ethernet eth1 firewall in name 'INBOUND' + - set interfaces ethernet eth1 firewall out name 'OUTBOUND' + - set interfaces ethernet eth1 firewall local name 'LOCAL' + - set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' + - set interfaces ethernet eth2 firewall in name 'INBOUND' + - set interfaces ethernet eth2 firewall out name 'OUTBOUND' + - set interfaces ethernet eth2 firewall local name 'LOCAL' + - set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth2 +populate: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth2 +merged_edit: + commands: + - set interfaces ethernet eth1 firewall in name 'OUTBOUND' + - set interfaces ethernet eth1 firewall out name 'INBOUND' + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: OUTBOUND + - direction: local + name: LOCAL + - direction: out + name: INBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth2 +replaced: + commands: + - delete interfaces ethernet eth2 firewall out name + - delete interfaces ethernet eth2 firewall local name + - delete interfaces ethernet eth2 firewall local ipv6-name + - delete interfaces ethernet eth1 firewall local name + - delete interfaces ethernet eth1 firewall in name + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + name: eth2 +overridden: + before: + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - name: INBOUND + direction: in + - name: LOCAL + direction: local + - name: OUTBOUND + direction: out + - afi: ipv6 + rules: + - name: V6-LOCAL + direction: local + name: eth2 + commands: + - delete interfaces ethernet eth1 firewall + - delete interfaces ethernet eth2 firewall in name + - delete interfaces ethernet eth2 firewall local name + - delete interfaces ethernet eth2 firewall local ipv6-name + - set interfaces ethernet eth2 firewall out name 'INBOUND' + after: + - name: eth0 + - name: eth1 + - access_rules: + - afi: ipv4 + rules: + - name: INBOUND + direction: out + name: eth2 +deleted: + commands: + - delete interfaces ethernet eth1 firewall + - delete interfaces ethernet eth2 firewall + after: + - name: eth0 + - name: eth1 + - name: eth2 +deleted_afi: + commands: + - delete interfaces ethernet eth1 firewall in name + - delete interfaces ethernet eth1 firewall local name + - delete interfaces ethernet eth1 firewall out name + - delete interfaces ethernet eth1 firewall local ipv6-name + - delete interfaces ethernet eth2 firewall in name + - delete interfaces ethernet eth2 firewall local name + - delete interfaces ethernet eth2 firewall out name + - delete interfaces ethernet eth2 firewall local ipv6-name + after: + - name: eth0 + - access_rules: + - afi: ipv4 + - afi: ipv6 + name: eth1 + - access_rules: + - afi: ipv4 + - afi: ipv6 + name: eth2 +deleted_single: + commands: + - delete interfaces ethernet eth1 firewall in name 'INBOUND' + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth2 +rendered: + commands: + - set interfaces ethernet eth1 firewall in name 'INBOUND' + - set interfaces ethernet eth1 firewall out name 'OUTBOUND' + - set interfaces ethernet eth1 firewall local name 'LOCAL' + - set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' + - set interfaces ethernet eth2 firewall in name 'INBOUND' + - set interfaces ethernet eth2 firewall out name 'OUTBOUND' + - set interfaces ethernet eth2 firewall local name 'LOCAL' + - set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' +round_trip: + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - name: eth2 + access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_interfaces_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_interfaces_config.cfg new file mode 100644 index 0000000..24704d2 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_interfaces_config.cfg @@ -0,0 +1,8 @@ +set interfaces ethernet eth0 firewall in name 'INBOUND' +set interfaces ethernet eth0 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth0 firewall local name 'LOCAL' +set interfaces ethernet eth0 firewall out name 'OUTBOUND' +set interfaces ethernet eth2 firewall in name 'INBOUND' +set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth2 firewall local name 'LOCAL' +set interfaces ethernet eth2 firewall out name 'OUTBOUND' diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py new file mode 100644 index 0000000..1ffeae4 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py @@ -0,0 +1,432 @@ +# (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 . + +# 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, + MagicMock, +) +from ansible_collections.vyos.vyos.plugins.modules import ( + vyos_firewall_interfaces, +) +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( + set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosFirewallInterfacesModule(TestVyosModule): + + module = vyos_firewall_interfaces + + def setUp(self): + super(TestVyosFirewallInterfacesModule, 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.firewall_interfaces.firewall_interfaces.Firewall_interfacesFacts.get_device_data" + ) + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestVyosFirewallInterfacesModule, 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_interfaces_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( + name="eth1", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth3", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + ], + state="merged", + ) + ) + commands = [ + "set interfaces ethernet eth1 firewall in name 'INBOUND'", + "set interfaces ethernet eth1 firewall out name 'OUTBOUND'", + "set interfaces ethernet eth1 firewall local name 'LOCAL'", + "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'", + "set interfaces ethernet eth3 firewall in name 'INBOUND'", + "set interfaces ethernet eth3 firewall out name 'OUTBOUND'", + "set interfaces ethernet eth3 firewall local name 'LOCAL'", + "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_02_merged_idem(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth2", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + ], + state="merged", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_firewall_rule_set_01_deleted_per_afi(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + access_rules=[dict(afi="ipv4"), dict(afi="ipv6")], + ) + ], + state="deleted", + ) + ) + commands = [ + "delete interfaces ethernet eth0 firewall in name", + "delete interfaces ethernet eth0 firewall local name", + "delete interfaces ethernet eth0 firewall out name", + "delete interfaces ethernet eth0 firewall local ipv6-name", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_03_deleted_per_interface(self): + set_module_args( + dict( + config=[dict(name="eth0"), dict(name="eth2")], state="deleted" + ) + ) + commands = [ + "delete interfaces ethernet eth0 firewall", + "delete interfaces ethernet eth2 firewall", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_03_deleted_all(self): + set_module_args(dict(config=[], state="deleted")) + commands = [ + "delete interfaces ethernet eth0 firewall", + "delete interfaces ethernet eth2 firewall", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_03_deleted(self): + set_module_args( + dict( + config=[dict(name="eth0"), dict(name="eth2")], state="deleted" + ) + ) + commands = [ + "delete interfaces ethernet eth0 firewall", + "delete interfaces ethernet eth2 firewall", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_04_deleted_interface_idem(self): + set_module_args( + dict( + config=[dict(name="eth1"), dict(name="eth3")], state="deleted" + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_firewall_rule_set_02_replaced_idem(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth2", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + ], + state="replaced", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_firewall_rule_set_01_replaced(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + access_rules=[ + dict( + afi="ipv4", + rules=[dict(name="INBOUND", direction="in"),], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth2", + access_rules=[ + dict( + afi="ipv4", + rules=[dict(name="LOCAL", direction="local")], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth3", + access_rules=[ + dict( + afi="ipv4", + rules=[dict(name="LOCAL", direction="local")], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + ], + state="replaced", + ) + ) + commands = [ + "delete interfaces ethernet eth0 firewall out name", + "delete interfaces ethernet eth0 firewall local name", + "delete interfaces ethernet eth2 firewall in name", + "delete interfaces ethernet eth2 firewall out name", + "set interfaces ethernet eth3 firewall local name 'LOCAL'", + "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_01_overridden(self): + set_module_args( + dict( + config=[ + dict( + name="eth1", + access_rules=[ + dict( + afi="ipv4", + rules=[dict(name="INBOUND", direction="in")], + ) + ], + ) + ], + state="overridden", + ) + ) + commands = [ + "delete interfaces ethernet eth0 firewall", + "delete interfaces ethernet eth2 firewall", + "set interfaces ethernet eth1 firewall in name 'INBOUND'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rule_set_02_overridden_idem(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + dict( + name="eth2", + access_rules=[ + dict( + afi="ipv4", + rules=[ + dict(name="INBOUND", direction="in"), + dict(name="OUTBOUND", direction="out"), + dict(name="LOCAL", direction="local"), + ], + ), + dict( + afi="ipv6", + rules=[ + dict(name="V6-LOCAL", direction="local") + ], + ), + ], + ), + ], + state="overridden", + ) + ) + self.execute_module(changed=False, commands=[])