diff options
author | CaptTrews <capttrews@gmail.com> | 2020-02-25 00:29:36 +0000 |
---|---|---|
committer | CaptTrews <capttrews@gmail.com> | 2020-02-25 00:29:36 +0000 |
commit | 1e35316ae5a079cc5466d97e873f48ae1ec574e2 (patch) | |
tree | be3cd3beea41c943836fcf8f6c87c6ea837eb8d5 /plugins/module_utils/network | |
parent | cfd3472a4d2b8906a944451341f37af1fde2dd54 (diff) | |
download | vyos.vyos-1e35316ae5a079cc5466d97e873f48ae1ec574e2.tar.gz vyos.vyos-1e35316ae5a079cc5466d97e873f48ae1ec574e2.zip |
Updated from network content collector
Signed-off-by: CaptTrews <capttrews@gmail.com>
Diffstat (limited to 'plugins/module_utils/network')
7 files changed, 1354 insertions, 0 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_global/__init__.py b/plugins/module_utils/network/vyos/argspec/firewall_global/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_global/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py new file mode 100644 index 0000000..4c26773 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py @@ -0,0 +1,152 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# +""" +The arg spec for the vyos_firewall_global module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Firewall_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_firewall_global module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "config_trap": {"type": "bool"}, + "group": { + "options": { + "address_group": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "members": { + "elements": "dict", + "options": {"address": {"type": "str"}}, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "network_group": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "members": { + "elements": "dict", + "options": {"address": {"type": "str"}}, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "port_group": { + "elements": "dict", + "options": { + "description": {"type": "str"}, + "members": { + "elements": "dict", + "options": {"port": {"type": "str"}}, + "type": "list", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "log_martians": {"type": "bool"}, + "ping": { + "options": { + "all": {"type": "bool"}, + "broadcast": {"type": "bool"}, + }, + "type": "dict", + }, + "route_redirects": { + "elements": "dict", + "options": { + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + "icmp_redirects": { + "options": { + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + "type": "dict", + }, + "ip_src_route": {"type": "bool"}, + }, + "type": "list", + }, + "state_policy": { + "elements": "dict", + "options": { + "action": { + "choices": ["accept", "drop", "reject"], + "type": "str", + }, + "connection_type": { + "choices": ["established", "invalid", "related"], + "type": "str", + }, + "log": {"type": "bool"}, + }, + "type": "list", + }, + "syn_cookies": {"type": "bool"}, + "twa_hazards_protection": {"type": "bool"}, + "validation": { + "choices": ["strict", "loose", "disable"], + "type": "str", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/firewall_global/__init__.py b/plugins/module_utils/network/vyos/config/firewall_global/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_global/__init__.py diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py new file mode 100644 index 0000000..afc9853 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py @@ -0,0 +1,810 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_firewall_global class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + list_diff_want_only, +) + + +class Firewall_global(ConfigBase): + """ + The vyos_firewall_global class + """ + + gather_subset = [ + "!all", + "!min", + ] + + gather_network_resources = [ + "firewall_global", + ] + + def __init__(self, module): + super(Firewall_global, self).__init__(module) + + def get_firewall_global_facts(self, data=None): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources, data=data + ) + firewall_global_facts = facts["ansible_network_resources"].get( + "firewall_global" + ) + if not firewall_global_facts: + return [] + return firewall_global_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_firewall_global_facts = self.get_firewall_global_facts() + else: + existing_firewall_global_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_firewall_global_facts)) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_firewall_global_facts = self.get_firewall_global_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_firewall_global_facts( + data=running_config + ) + else: + changed_firewall_global_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_firewall_global_facts + if result["changed"]: + result["after"] = changed_firewall_global_facts + elif self.state == "gathered": + result["gathered"] = changed_firewall_global_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_firewall_global_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_firewall_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, w, h): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if self.state in ("merged", "replaced", "rendered") and not w: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + self.state + ) + ) + if self.state == "deleted": + commands.extend(self._state_deleted(want=None, have=h)) + elif w: + if self.state == "merged" or self.state == "rendered": + commands.extend(self._state_merged(w, h)) + elif self.state == "replaced": + commands.extend(self._state_replaced(w, h)) + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + commands.extend(self._state_deleted(have, want)) + commands.extend(self._state_merged(want, have)) + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + commands.extend(self._add_global_attr(want, have)) + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + b_set = ( + "config_trap", + "validation", + "log_martians", + "syn_cookies", + "twa_hazards_protection", + ) + if want: + for key, val in iteritems(want): + if val and key in b_set and not have: + commands.append(self._form_attr_cmd(attr=key, opr=False)) + elif ( + val + and key in b_set + and have + and key in have + and have[key] != val + ): + commands.append(self._form_attr_cmd(attr=key, opr=False)) + else: + commands.extend(self._render_attr_config(want, have, key)) + elif not want and have: + commands.append(self._compute_command(opr=False)) + elif have: + for key, val in iteritems(have): + if val and key in b_set: + commands.append(self._form_attr_cmd(attr=key, opr=False)) + else: + commands.extend(self._render_attr_config(want, have, key)) + return commands + + def _render_attr_config(self, w, h, key, opr=False): + """ + This function invoke the function to extend commands + based on the key. + :param w: the desired configuration. + :param h: the current configuration. + :param key: attribute name + :param opr: operation + :return: list of commands + """ + commands = [] + if key == "ping": + commands.extend(self._render_ping(key, w, h, opr=opr)) + elif key == "group": + commands.extend(self._render_group(key, w, h, opr=opr)) + elif key == "state_policy": + commands.extend(self._render_state_policy(key, w, h, opr=opr)) + elif key == "route_redirects": + commands.extend(self._render_route_redirects(key, w, h, opr=opr)) + return commands + + def _add_global_attr(self, w, h, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for firewall_global attributes. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + w_fg = deepcopy(remove_empties(w)) + l_set = ( + "config_trap", + "validation", + "log_martians", + "syn_cookies", + "twa_hazards_protection", + ) + if w_fg: + for key, val in iteritems(w_fg): + if ( + opr + and key in l_set + and not (h and self._is_w_same(w_fg, h, key)) + ): + commands.append( + self._form_attr_cmd( + attr=key, val=self._bool_to_str(val), opr=opr + ) + ) + elif not opr: + if key and self._is_del(l_set, h): + commands.append( + self._form_attr_cmd( + attr=key, key=self._bool_to_str(val), opr=opr + ) + ) + continue + elif ( + key in l_set + and not (h and self._in_target(h, key)) + and not self._is_del(l_set, h) + ): + commands.append( + self._form_attr_cmd( + attr=key, val=self._bool_to_str(val), opr=opr + ) + ) + else: + commands.extend( + self._render_attr_config(w_fg, h, key, opr) + ) + return commands + + def _render_ping(self, attr, w, h, opr): + """ + This function forms the commands for 'ping' attributes based on the 'opr'. + :param attr: attribute name. + :param w: the desired configuration. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h_ping = {} + l_set = ("all", "broadcast") + if h: + h_ping = h.get(attr) or {} + if self._is_root_del(w[attr], h_ping, attr): + for item, value in iteritems(h[attr]): + if not opr and item in l_set: + commands.append(self._form_attr_cmd(attr=item, opr=opr)) + elif w[attr]: + if h and attr in h.keys(): + h_ping = h.get(attr) or {} + for item, value in iteritems(w[attr]): + if ( + opr + and item in l_set + and not (h_ping and self._is_w_same(w[attr], h_ping, item)) + ): + commands.append( + self._form_attr_cmd( + attr=item, val=self._bool_to_str(value), opr=opr + ) + ) + elif ( + not opr + and item in l_set + and not (h_ping and self._is_w_same(w[attr], h_ping, item)) + ): + commands.append(self._form_attr_cmd(attr=item, opr=opr)) + return commands + + def _render_group(self, attr, w, h, opr): + """ + This function forms the commands for 'group' attribute based on the 'opr'. + :param attr: attribute name. + :param w: base config. + :param h: target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h_grp = {} + if not opr and self._is_root_del(h, w, attr): + commands.append(self._form_attr_cmd(attr=attr, opr=opr)) + else: + if h: + h_grp = h.get("group") or {} + if w: + commands.extend( + self._render_grp_mem("port-group", w["group"], h_grp, opr) + ) + commands.extend( + self._render_grp_mem( + "address_group", w["group"], h_grp, opr + ) + ) + commands.extend( + self._render_grp_mem( + "network_group", w["group"], h_grp, opr + ) + ) + return commands + + def _render_grp_mem(self, attr, w, h, opr): + """ + This function forms the commands for group list/members attributes based on the 'opr'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h_grp = [] + w_grp = [] + l_set = ("name", "description") + if w: + w_grp = w.get(attr) or [] + if h: + h_grp = h.get(attr) or [] + + if w_grp: + for want in w_grp: + cmd = self._compute_command(key="group", attr=attr, opr=opr) + h = self.search_attrib_in_have(h_grp, want, "name") + for key, val in iteritems(want): + if val: + if ( + opr + and key in l_set + and not (h and self._is_w_same(want, h, key)) + ): + if key == "name": + commands.append(cmd + " " + str(val)) + else: + commands.append( + cmd + + " " + + want["name"] + + " " + + key + + " '" + + str(want[key]) + + "'" + ) + elif not opr and key in l_set: + if key == "name" and self._is_grp_del( + h, want, key + ): + commands.append(cmd + " " + want["name"]) + continue + elif not ( + h and self._in_target(h, key) + ) and not self._is_grp_del(h, want, key): + commands.append( + cmd + " " + want["name"] + " " + key + ) + elif key == "members": + commands.extend( + self._render_ports_addrs( + key, want, h, opr, cmd, want["name"], attr + ) + ) + return commands + + def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type): + """ + This function forms the commands for port/address/network group members + based on the 'opr'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param name: name of group. + :param type: group type. + :return: generated list of commands. + """ + commands = [] + have = [] + if w: + want = w.get(attr) or [] + if h: + have = h.get(attr) or [] + + if want: + if opr: + members = list_diff_want_only(want, have) + for member in members: + commands.append( + cmd + + " " + + name + + " " + + self._grp_type(type) + + " " + + member[self._get_mem_type(type)] + ) + elif not opr and have: + members = list_diff_want_only(want, have) + for member in members: + commands.append( + cmd + + " " + + name + + " " + + self._grp_type(type) + + " " + + member[self._get_mem_type(type)] + ) + return commands + + def _get_mem_type(self, group): + """ + This function returns the member type + based on the type of group. + """ + return "port" if group == "port_group" else "address" + + def _render_state_policy(self, attr, w, h, opr): + """ + This function forms the commands for 'state-policy' attributes + based on the 'opr'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + have = [] + l_set = ("log", "action", "connection_type") + if not opr and self._is_root_del(h, w, attr): + commands.append(self._form_attr_cmd(attr=attr, opr=opr)) + else: + w_sp = deepcopy(remove_empties(w)) + want = w_sp.get(attr) or [] + if h: + have = h.get(attr) or [] + if want: + for w in want: + h = self.search_attrib_in_have(have, w, "connection_type") + for key, val in iteritems(w): + if val and key != "connection_type": + if ( + opr + and key in l_set + and not (h and self._is_w_same(w, h, key)) + ): + commands.append( + self._form_attr_cmd( + key=attr + " " + w["connection_type"], + attr=key, + val=self._bool_to_str(val), + opr=opr, + ) + ) + elif not opr and key in l_set: + if not ( + h and self._in_target(h, key) + ) and not self._is_del(l_set, h): + if key == "action": + commands.append( + self._form_attr_cmd( + attr=attr + + " " + + w["connection_type"], + opr=opr, + ) + ) + else: + commands.append( + self._form_attr_cmd( + attr=attr + + " " + + w["connection_type"], + val=self._bool_to_str(val), + opr=opr, + ) + ) + return commands + + def _render_route_redirects(self, attr, w, h, opr): + """ + This function forms the commands for 'route_redirects' attributes based on the 'opr'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + have = [] + l_set = ("afi", "ip_src_route") + + if w: + want = w.get(attr) or [] + if h: + have = h.get(attr) or [] + + if want: + for w in want: + h = self.search_attrib_in_have(have, w, "afi") + for key, val in iteritems(w): + if val and key != "afi": + if ( + opr + and key in l_set + and not (h and self._is_w_same(w, h, key)) + ): + commands.append( + self._form_attr_cmd( + attr=key, + val=self._bool_to_str(val), + opr=opr, + ) + ) + elif not opr and key in l_set: + if self._is_del(l_set, h): + commands.append( + self._form_attr_cmd( + attr=key, + val=self._bool_to_str(val), + opr=opr, + ) + ) + continue + elif not ( + h and self._in_target(h, key) + ) and not self._is_del(l_set, h): + commands.append( + self._form_attr_cmd( + attr=key, + val=self._bool_to_str(val), + opr=opr, + ) + ) + elif key == "icmp_redirects": + commands.extend( + self._render_icmp_redirects(key, w, h, opr) + ) + return commands + + def _render_icmp_redirects(self, attr, w, h, opr): + """ + This function forms the commands for 'icmp_redirects' attributes + based on the 'opr'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h_red = {} + l_set = ("send", "receive") + if w[attr]: + if h and attr in h.keys(): + h_red = h.get(attr) or {} + for item, value in iteritems(w[attr]): + if ( + opr + and item in l_set + and not (h_red and self._is_w_same(w[attr], h_red, item)) + ): + commands.append( + self._form_attr_cmd( + attr=item, val=self._bool_to_str(value), opr=opr + ) + ) + elif ( + not opr + and item in l_set + and not (h_red and self._is_w_same(w[attr], h_red, item)) + ): + commands.append(self._form_attr_cmd(attr=item, opr=opr)) + return commands + + def search_attrib_in_have(self, have, want, attr): + """ + This function returns the attribute if it is present in target config. + :param have: the target config. + :param want: the desired config. + :param attr: attribute name . + :return: attribute/None + """ + if have: + for h in have: + if h[attr] == want[attr]: + return h + return None + + def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): + """ + This function forms the command for leaf attribute. + :param key: parent key. + :param attr: attribute name + :param value: value + :param opr: True/False. + :return: generated command. + """ + command = self._compute_command( + key=key, attr=self._map_attrib(attr), val=val, opr=opr + ) + return command + + def _compute_command( + self, key=None, attr=None, val=None, remove=False, opr=True + ): + """ + This function construct the add/delete command based on passed attributes. + :param key: parent key. + :param attr: attribute name + :param value: value + :param remove: True/False. + :param opr: True/False. + :return: generated command. + """ + if remove or not opr: + cmd = "delete firewall " + else: + cmd = "set firewall " + if key: + cmd += key.replace("_", "-") + " " + if attr: + cmd += attr.replace("_", "-") + if val and opr: + cmd += " '" + str(val) + "'" + return cmd + + def _bool_to_str(self, val): + """ + This function converts the bool value into string. + :param val: bool value. + :return: enable/disable. + """ + return ( + "enable" + if str(val) == "True" + else "disable" + if str(val) == "False" + else val + ) + + def _grp_type(self, val): + """ + This function returns the group member type based on value argument. + :param val: value. + :return: member type. + """ + return ( + "address" + if val == "address_group" + else "network" + if val == "network_group" + else "port" + ) + + def _is_w_same(self, w, h, key): + """ + This function checks whether the key value is same in desired and + target config dictionary. + :param w: base config. + :param h: target config. + :param key:attribute name. + :return: True/False. + """ + return True if h and key in h and h[key] == w[key] else False + + def _in_target(self, h, key): + """ + This function checks whether the target exist and key present in target config. + :param h: target config. + :param key: attribute name. + :return: True/False. + """ + return True if h and key in h else False + + def _is_grp_del(self, w, h, key): + """ + This function checks whether group needed to be deleted based on + desired and target configs. + :param w: the desired config. + :param h: the target config. + :param key: group name. + :return: True/False. + """ + return ( + True + if h and key in h and (not w or key not in w or not w[key]) + else False + ) + + def _is_root_del(self, w, h, key): + """ + This function checks whether a root attribute which can have + further child attributes needed to be deleted. + :param w: the desired config. + :param h: the target config. + :param key: attribute name. + :return: True/False. + """ + return ( + True + if h and key in h and (not w or key not in w or not w[key]) + else False + ) + + def _is_del(self, b_set, h, key="number"): + """ + This function checks whether attribute needs to be deleted + when operation is false and attribute present in present target config. + :param b_set: attribute set. + :param h: target config. + :param key: number. + :return: True/False. + """ + return key in b_set and not (h and self._in_target(h, key)) + + def _map_attrib(self, attrib, type=None): + """ + - This function construct the regex string. + - replace the underscore with hyphen. + :param attrib: attribute + :return: regex string + """ + regex = attrib.replace("_", "-") + if attrib == "send": + if type == "ipv6": + regex = "ipv6-send-redirects" + else: + regex = "send-redirects" + elif attrib == "ip_src_route": + if type == "ipv6": + regex = "ipv6-src-route" + elif attrib == "receive": + if type == "ipv6": + regex = "ipv6-receive-redirects" + else: + regex = "receive-redirects" + elif attrib == "disabled": + regex = "disable" + elif attrib == "all": + regex = "all-ping" + elif attrib == "broadcast": + regex = "broadcast-ping" + elif attrib == "validation": + regex = "source-validation" + return regex diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 8f0a3bb..6e6a82b 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -33,6 +33,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew 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.legacy.base import ( Default, Neighbors, @@ -49,6 +52,7 @@ FACT_RESOURCE_SUBSETS = dict( lldp_interfaces=Lldp_interfacesFacts, static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts, + firewall_global=Firewall_globalFacts, ) diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/__init__.py b/plugins/module_utils/network/vyos/facts/firewall_global/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_global/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py new file mode 100644 index 0000000..0823259 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py @@ -0,0 +1,388 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos firewall_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from re import findall, search, M +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import ( + Firewall_globalArgs, +) + + +class Firewall_globalFacts(object): + """ The vyos firewall_global fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Firewall_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection): + return connection.get_config() + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for firewall_global + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section ^interface') + # using mock data instead + data = self.get_device_data(connection) + objs = {} + firewalls = findall(r"^set firewall .*$", data, M) + if firewalls: + objs = self.render_config(firewalls) + facts = {} + params = utils.validate_config(self.argument_spec, {"config": objs}) + facts["firewall_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + conf = "\n".join( + filter( + lambda x: ("firewall ipv6-name" and "firewall name" not in x), + conf, + ) + ) + + a_lst = [ + "config_trap", + "validation", + "log_martians", + "syn_cookies", + "twa_hazards_protection", + ] + firewall = self.parse_attr(conf, a_lst) + f_sub = { + "ping": self.parse_ping(conf), + "group": self.parse_group(conf), + "route_redirects": self.route_redirects(conf), + "state_policy": self.parse_state_policy(conf), + } + firewall.update(f_sub) + return firewall + + def route_redirects(self, conf): + """ + This function forms the regex to fetch the afi and invoke + functions to fetch route redirects and source routes + :param conf: configuration data. + :return: generated rule list configuration. + """ + rr_lst = [] + + v6_attr = findall( + r"^set firewall (?:ipv6-src-route|ipv6-receive-redirects) (\S+)", + conf, + M, + ) + if v6_attr: + obj = self.parse_rr_attrib(conf, "ipv6") + if obj: + rr_lst.append(obj) + + v4_attr = findall( + r"^set firewall (?:ip-src-route|receive-redirects|send-redirects) (\S+)", + conf, + M, + ) + if v4_attr: + obj = self.parse_rr_attrib(conf, "ipv4") + if obj: + rr_lst.append(obj) + return rr_lst + + def parse_rr_attrib(self, conf, attrib=None): + """ + This function fetches the 'ip_src_route' + invoke function to parse icmp redirects. + :param conf: configuration to be parsed. + :param attrib: 'ipv4/ipv6'. + :return: generated config dictionary. + """ + + cfg_dict = self.parse_attr(conf, ["ip_src_route"], type=attrib) + cfg_dict["icmp_redirects"] = self.parse_icmp_redirects(conf, attrib) + cfg_dict["afi"] = attrib + return cfg_dict + + def parse_icmp_redirects(self, conf, attrib=None): + """ + This function triggers the parsing of 'icmp_redirects' attributes. + :param conf: configuration to be parsed. + :param attrib: 'ipv4/ipv6'. + :return: generated config dictionary. + """ + a_lst = ["send", "receive"] + cfg_dict = self.parse_attr(conf, a_lst, type=attrib) + return cfg_dict + + def parse_ping(self, conf): + """ + This function triggers the parsing of 'ping' attributes. + :param conf: configuration to be parsed. + :return: generated config dictionary. + """ + a_lst = ["all", "broadcast"] + cfg_dict = self.parse_attr(conf, a_lst) + return cfg_dict + + def parse_state_policy(self, conf): + """ + This function fetched the connecton type and invoke + function to parse other state-policy attributes. + :param conf: configuration data. + :return: generated rule list configuration. + """ + sp_lst = [] + attrib = "state-policy" + policies = findall(r"^set firewall " + attrib + " (\\S+)", conf, M) + + if policies: + rules_lst = [] + for sp in set(policies): + sp_regex = r" %s .+$" % sp + cfg = "\n".join(findall(sp_regex, conf, M)) + obj = self.parse_policies(cfg, sp) + obj["connection_type"] = sp + if obj: + rules_lst.append(obj) + sp_lst = sorted(rules_lst, key=lambda i: i["connection_type"]) + return sp_lst + + def parse_policies(self, conf, attrib=None): + """ + This function triggers the parsing of policy attributes + action and log. + :param conf: configuration + :param attrib: connection type. + :return: generated rule configuration dictionary. + """ + a_lst = ["action", "log"] + cfg_dict = self.parse_attr(conf, a_lst, match=attrib) + return cfg_dict + + def parse_group(self, conf): + """ + This function triggers the parsing of 'group' attributes. + :param conf: configuration. + :return: generated config dictionary. + """ + cfg_dict = {} + cfg_dict["port_group"] = self.parse_group_lst(conf, "port-group") + cfg_dict["address_group"] = self.parse_group_lst(conf, "address-group") + cfg_dict["network_group"] = self.parse_group_lst(conf, "network-group") + return cfg_dict + + def parse_group_lst(self, conf, type): + """ + This function fetches the name of group and invoke function to + parse group attributes'. + :param conf: configuration data. + :param type: type of group. + :return: generated group list configuration. + """ + g_lst = [] + + groups = findall(r"^set firewall group " + type + " (\\S+)", conf, M) + if groups: + rules_lst = [] + for gr in set(groups): + gr_regex = r" %s .+$" % gr + cfg = "\n".join(findall(gr_regex, conf, M)) + obj = self.parse_groups(cfg, type, gr) + obj["name"] = gr.strip("'") + if obj: + rules_lst.append(obj) + g_lst = sorted(rules_lst, key=lambda i: i["name"]) + return g_lst + + def parse_groups(self, conf, type, name): + """ + This function fetches the description and invoke + the parsing of group members. + :param conf: configuration. + :param type: type of group. + :param name: name of group. + :return: generated configuration dictionary. + """ + a_lst = ["name", "description"] + group = self.parse_attr(conf, a_lst) + key = self.get_key(type) + r_sub = {key[0]: self.parse_address_port_lst(conf, name, key[1])} + group.update(r_sub) + return group + + def parse_address_port_lst(self, conf, name, key): + """ + This function forms the regex to fetch the + group members attributes. + :param conf: configuration data. + :param name: name of group. + :param key: key value. + :return: generated member list configuration. + """ + l_lst = [] + attribs = findall(r"^.*" + name + " " + key + " (\\S+)", conf, M) + if attribs: + for attr in attribs: + if key == "port": + l_lst.append({"port": attr.strip("'")}) + else: + l_lst.append({"address": attr.strip("'")}) + return l_lst + + def parse_attr(self, conf, attr_list, match=None, type=None): + """ + This function peforms the following: + - Form the regex to fetch the required attribute config. + - Type cast the output in desired format. + :param conf: configuration. + :param attr_list: list of attributes. + :param match: parent node/attribute name. + :return: generated config dictionary. + """ + config = {} + for attrib in attr_list: + regex = self.map_regex(attrib, type) + if match: + regex = match + " " + regex + if conf: + if self.is_bool(attrib): + attr = self.map_regex(attrib, type) + out = conf.find(attr.replace("_", "-")) + dis = conf.find(attr.replace("_", "-") + " 'disable'") + if out >= 1: + if dis >= 1: + config[attrib] = False + else: + config[attrib] = True + else: + out = search(r"^.*" + regex + " (.+)", conf, M) + if out: + val = out.group(1).strip("'") + if self.is_num(attrib): + val = int(val) + config[attrib] = val + return config + + def get_key(self, type): + """ + This function map the group type to + member type + :param type: + :return: + """ + key = () + if type == "port-group": + key = ("members", "port") + elif type == "address-group": + key = ("members", "address") + elif type == "network-group": + key = ("members", "network") + return key + + def map_regex(self, attrib, type=None): + """ + - This function construct the regex string. + - replace the underscore with hyphen. + :param attrib: attribute + :return: regex string + """ + regex = attrib.replace("_", "-") + if attrib == "all": + regex = "all-ping" + elif attrib == "disabled": + regex = "disable" + elif attrib == "broadcast": + regex = "broadcast-ping" + elif attrib == "send": + if type == "ipv6": + regex = "ipv6-send-redirects" + else: + regex = "send-redirects" + elif attrib == "ip_src_route": + if type == "ipv6": + regex = "ipv6-src-route" + elif attrib == "receive": + if type == "ipv6": + regex = "ipv6-receive-redirects" + else: + regex = "receive-redirects" + return regex + + def is_num(self, attrib): + """ + This function looks for the attribute in predefined integer type set. + :param attrib: attribute. + :return: True/false. + """ + num_set = ("time", "code", "type", "count", "burst", "number") + return True if attrib in num_set else False + + def get_src_route(self, attrib): + """ + This function looks for the attribute in predefined integer type set. + :param attrib: attribute. + :return: True/false. + """ + return "ipv6_src_route" if attrib == "ipv6" else "ip_src_route" + + def is_bool(self, attrib): + """ + This function looks for the attribute in predefined bool type set. + :param attrib: attribute. + :return: True/False + """ + bool_set = ( + "all", + "log", + "send", + "receive", + "broadcast", + "config_trap", + "log_martians", + "syn_cookies", + "ip_src_route", + "twa_hazards_protection", + ) + return True if attrib in bool_set else False |