diff options
Diffstat (limited to 'plugins/module_utils/network')
14 files changed, 2643 insertions, 1 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py new file mode 100644 index 00000000..a018cc0b --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -0,0 +1,263 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# +""" +The arg spec for the vyos_firewall_rules module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Firewall_rulesArgs(object):  # pylint: disable=R0903 +    """The arg spec for the vyos_firewall_rules module +    """ + +    def __init__(self, **kwargs): +        pass + +    argument_spec = { +        "config": { +            "elements": "dict", +            "options": { +                "afi": { +                    "choices": ["ipv4", "ipv6"], +                    "required": True, +                    "type": "str", +                }, +                "rule_sets": { +                    "elements": "dict", +                    "options": { +                        "default_action": { +                            "choices": ["drop", "reject", "accept"], +                            "type": "str", +                        }, +                        "description": {"type": "str"}, +                        "enable_default_log": {"type": "bool"}, +                        "name": {"type": "str"}, +                        "rules": { +                            "elements": "dict", +                            "options": { +                                "action": { +                                    "choices": [ +                                        "drop", +                                        "reject", +                                        "accept", +                                        "inspect", +                                    ], +                                    "type": "str", +                                }, +                                "description": {"type": "str"}, +                                "destination": { +                                    "options": { +                                        "address": {"type": "str"}, +                                        "group": { +                                            "options": { +                                                "address_group": { +                                                    "type": "str" +                                                }, +                                                "network_group": { +                                                    "type": "str" +                                                }, +                                                "port_group": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                        "port": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "disabled": {"type": "bool"}, +                                "fragment": { +                                    "choices": [ +                                        "match-frag", +                                        "match-non-frag", +                                    ], +                                    "type": "str", +                                }, +                                "icmp": { +                                    "options": { +                                        "code": {"type": "int"}, +                                        "type": {"type": "int"}, +                                        "type_name": { +                                            "choices": [ +                                                "any", +                                                "echo-reply", +                                                "destination-unreachable", +                                                "network-unreachable", +                                                "host-unreachable", +                                                "protocol-unreachable", +                                                "port-unreachable", +                                                "fragmentation-needed", +                                                "source-route-failed", +                                                "network-unknown", +                                                "host-unknown", +                                                "network-prohibited", +                                                "host-prohibited", +                                                "TOS-network-unreachable", +                                                "TOS-host-unreachable", +                                                "communication-prohibited", +                                                "host-precedence-violation", +                                                "precedence-cutoff", +                                                "source-quench", +                                                "redirect", +                                                "network-redirect", +                                                "host-redirect", +                                                "TOS-network-redirect", +                                                "TOS-host-redirect", +                                                "echo-request", +                                                "router-advertisement", +                                                "router-solicitation", +                                                "time-exceeded", +                                                "ttl-zero-during-transit", +                                                "ttl-zero-during-reassembly", +                                                "parameter-problem", +                                                "ip-header-bad", +                                                "required-option-missing", +                                                "timestamp-request", +                                                "timestamp-reply", +                                                "address-mask-request", +                                                "address-mask-reply", +                                                "ping", +                                                "pong", +                                                "ttl-exceeded", +                                            ], +                                            "type": "str", +                                        }, +                                    }, +                                    "type": "dict", +                                }, +                                "ipsec": { +                                    "choices": ["match-ipsec", "match-none"], +                                    "type": "str", +                                }, +                                "limit": { +                                    "options": { +                                        "burst": {"type": "int"}, +                                        "rate": { +                                            "options": { +                                                "number": {"type": "int"}, +                                                "unit": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                    }, +                                    "type": "dict", +                                }, +                                "number": {"required": True, "type": "int"}, +                                "p2p": { +                                    "elements": "dict", +                                    "options": { +                                        "application": { +                                            "choices": [ +                                                "all", +                                                "applejuice", +                                                "bittorrent", +                                                "directconnect", +                                                "edonkey", +                                                "gnutella", +                                                "kazaa", +                                            ], +                                            "type": "str", +                                        } +                                    }, +                                    "type": "list", +                                }, +                                "protocol": {"type": "str"}, +                                "recent": { +                                    "options": { +                                        "count": {"type": "int"}, +                                        "time": {"type": "int"}, +                                    }, +                                    "type": "dict", +                                }, +                                "source": { +                                    "options": { +                                        "address": {"type": "str"}, +                                        "group": { +                                            "options": { +                                                "address_group": { +                                                    "type": "str" +                                                }, +                                                "network_group": { +                                                    "type": "str" +                                                }, +                                                "port_group": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                        "mac_address": {"type": "str"}, +                                        "port": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "state": { +                                    "options": { +                                        "established": {"type": "bool"}, +                                        "invalid": {"type": "bool"}, +                                        "new": {"type": "bool"}, +                                        "related": {"type": "bool"}, +                                    }, +                                    "type": "dict", +                                }, +                                "tcp": { +                                    "options": {"flags": {"type": "str"}}, +                                    "type": "dict", +                                }, +                                "time": { +                                    "options": { +                                        "monthdays": {"type": "str"}, +                                        "startdate": {"type": "str"}, +                                        "starttime": {"type": "str"}, +                                        "stopdate": {"type": "str"}, +                                        "stoptime": {"type": "str"}, +                                        "utc": {"type": "bool"}, +                                        "weekdays": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                            }, +                            "type": "list", +                        }, +                    }, +                    "type": "list", +                }, +            }, +            "type": "list", +        }, +        "running_config": {"type": "str"}, +        "state": { +            "choices": [ +                "merged", +                "replaced", +                "overridden", +                "deleted", +                "gathered", +                "rendered", +                "parsed", +            ], +            "default": "merged", +            "type": "str", +        }, +    }  # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py new file mode 100644 index 00000000..8ecd955a --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py @@ -0,0 +1,99 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# +""" +The arg spec for the vyos_static_routes module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Static_routesArgs(object):  # pylint: disable=R0903 +    """The arg spec for the vyos_static_routes module +    """ + +    def __init__(self, **kwargs): +        pass + +    argument_spec = { +        "config": { +            "elements": "dict", +            "options": { +                "address_families": { +                    "elements": "dict", +                    "options": { +                        "afi": { +                            "choices": ["ipv4", "ipv6"], +                            "required": True, +                            "type": "str", +                        }, +                        "routes": { +                            "elements": "dict", +                            "options": { +                                "blackhole_config": { +                                    "options": { +                                        "distance": {"type": "int"}, +                                        "type": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "dest": {"required": True, "type": "str"}, +                                "next_hops": { +                                    "elements": "dict", +                                    "options": { +                                        "admin_distance": {"type": "int"}, +                                        "enabled": {"type": "bool"}, +                                        "forward_router_address": { +                                            "required": True, +                                            "type": "str", +                                        }, +                                        "interface": {"type": "str"}, +                                    }, +                                    "type": "list", +                                }, +                            }, +                            "type": "list", +                        }, +                    }, +                    "type": "list", +                } +            }, +            "type": "list", +        }, +        "running_config": {"type": "str"}, +        "state": { +            "choices": [ +                "merged", +                "replaced", +                "overridden", +                "deleted", +                "gathered", +                "rendered", +                "parsed", +            ], +            "default": "merged", +            "type": "str", +        }, +    }  # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py new file mode 100644 index 00000000..e58593f4 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py @@ -0,0 +1,1034 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_firewall_rules class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( +    ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( +    to_list, +    remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( +    Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    list_diff_want_only, +) + + +class Firewall_rules(ConfigBase): +    """ +    The vyos_firewall_rules class +    """ + +    gather_subset = [ +        "!all", +        "!min", +    ] + +    gather_network_resources = [ +        "firewall_rules", +    ] + +    def __init__(self, module): +        super(Firewall_rules, self).__init__(module) + +    def get_firewall_rules_facts(self, data=None): +        """ Get the 'facts' (the current configuration) + +        :rtype: A dictionary +        :returns: The current configuration as a dictionary +        """ +        facts, _warnings = Facts(self._module).get_facts( +            self.gather_subset, self.gather_network_resources, data=data +        ) +        firewall_rules_facts = facts["ansible_network_resources"].get( +            "firewall_rules" +        ) +        if not firewall_rules_facts: +            return [] +        return firewall_rules_facts + +    def execute_module(self): +        """ Execute the module + +        :rtype: A dictionary +        :returns: The result from module execution +        """ +        result = {"changed": False} +        warnings = list() +        commands = list() + +        if self.state in self.ACTION_STATES: +            existing_firewall_rules_facts = self.get_firewall_rules_facts() +        else: +            existing_firewall_rules_facts = [] + +        if self.state in self.ACTION_STATES or self.state == "rendered": +            commands.extend(self.set_config(existing_firewall_rules_facts)) + +        if commands and self.state in self.ACTION_STATES: +            if not self._module.check_mode: +                self._connection.edit_config(commands) +            result["changed"] = True + +        if self.state in self.ACTION_STATES: +            result["commands"] = commands + +        if self.state in self.ACTION_STATES or self.state == "gathered": +            changed_firewall_rules_facts = self.get_firewall_rules_facts() +        elif self.state == "rendered": +            result["rendered"] = commands +        elif self.state == "parsed": +            running_config = self._module.params["running_config"] +            if not running_config: +                self._module.fail_json( +                    msg="value of running_config parameter must not be empty for state parsed" +                ) +            result["parsed"] = self.get_firewall_rules_facts( +                data=running_config +            ) +        else: +            changed_firewall_rules_facts = [] + +        if self.state in self.ACTION_STATES: +            result["before"] = existing_firewall_rules_facts +            if result["changed"]: +                result["after"] = changed_firewall_rules_facts +        elif self.state == "gathered": +            result["gathered"] = changed_firewall_rules_facts + +        result["warnings"] = warnings +        return result + +    def set_config(self, existing_firewall_rules_facts): +        """ Collect the configuration from the args passed to the module, +            collect the current configuration (as a dict from facts) + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        want = self._module.params["config"] +        have = existing_firewall_rules_facts +        resp = self.set_state(want, have) +        return to_list(resp) + +    def set_state(self, w, h): +        """ Select the appropriate function based on the state provided + +        :param want: the desired configuration as a dictionary +        :param have: the current configuration as a dictionary +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if ( +            self.state in ("merged", "replaced", "overridden", "rendered") +            and not w +        ): +            self._module.fail_json( +                msg="value of config parameter must not be empty for state {0}".format( +                    self.state +                ) +            ) +        if self.state == "overridden": +            commands.extend(self._state_overridden(w, h)) +        elif self.state == "deleted": +            commands.extend(self._state_deleted(w, h)) +        elif w: +            if self.state == "merged" or self.state == "rendered": +                commands.extend(self._state_merged(w, h)) +            elif self.state == "replaced": +                commands.extend(self._state_replaced(w, h)) +        return commands + +    def _state_replaced(self, want, have): +        """ The command generator when state is replaced +         :rtype: A list +         :returns: the commands necessary to migrate the current configuration +                   to the desired configuration +         """ +        commands = [] +        if have: +            for h in have: +                r_sets = self._get_r_sets(h) +                for rs in r_sets: +                    w = self.search_r_sets_in_have(want, rs["name"], "r_list") +                    commands.extend( +                        self._add_r_sets(h["afi"], rs, w, opr=False) +                    ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_overridden(self, want, have): +        """ The command generator when state is overridden + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if have: +            for h in have: +                r_sets = self._get_r_sets(h) +                for rs in r_sets: +                    w = self.search_r_sets_in_have(want, rs["name"], "r_list") +                    if not w: +                        commands.append( +                            self._compute_command( +                                h["afi"], rs["name"], remove=True +                            ) +                        ) +                    else: +                        commands.extend( +                            self._add_r_sets(h["afi"], rs, w, opr=False) +                        ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_merged(self, want, have): +        """ The command generator when state is merged + +        :rtype: A list +        :returns: the commands necessary to merge the provided into +                  the current configuration +        """ +        commands = [] +        for w in want: +            r_sets = self._get_r_sets(w) +            for rs in r_sets: +                h = self.search_r_sets_in_have(have, rs["name"], "r_list") +                commands.extend(self._add_r_sets(w["afi"], rs, h)) +        return commands + +    def _state_deleted(self, want, have): +        """ The command generator when state is deleted + +        :rtype: A list +        :returns: the commands necessary to remove the current configuration +                  of the provided objects +        """ +        commands = [] +        if want: +            for w in want: +                r_sets = self._get_r_sets(w) +                if r_sets: +                    for rs in r_sets: +                        h = self.search_r_sets_in_have( +                            have, rs["name"], "r_list" +                        ) +                        if h: +                            w_rules = rs.get("rules") or [] +                            h_rules = h.get("rules") or [] +                            if w_rules and h_rules: +                                for rule in w_rules: +                                    if self.search_r_sets_in_have( +                                        h_rules, rule["number"], "rules" +                                    ): +                                        commands.append( +                                            self._add_r_base_attrib( +                                                w["afi"], +                                                rs["name"], +                                                "number", +                                                rule, +                                                opr=False, +                                            ) +                                        ) +                            else: +                                commands.append( +                                    self._compute_command( +                                        w["afi"], h["name"], remove=True +                                    ) +                                ) +                elif have: +                    for h in have: +                        if h["afi"] == w["afi"]: +                            commands.append( +                                self._compute_command(w["afi"], remove=True) +                            ) +        elif have: +            for h in have: +                r_sets = self._get_r_sets(h) +                if r_sets: +                    commands.append( +                        self._compute_command(afi=h["afi"], remove=True) +                    ) +        return commands + +    def _add_r_sets(self, afi, want, have, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for rule-sets attributes. +        :param afi: address type. +        :param want: desired config. +        :param have: target config. +        :param opr: True/False. +        :return: generated commands list. +        """ +        commands = [] +        l_set = ("description", "default_action", "enable_default_log") +        h_rs = {} +        h_rules = {} +        w_rs = deepcopy(remove_empties(want)) +        w_rules = w_rs.pop("rules", None) +        if have: +            h_rs = deepcopy(remove_empties(have)) +            h_rules = h_rs.pop("rules", None) +        if w_rs: +            for key, val in iteritems(w_rs): +                if ( +                    opr +                    and key in l_set +                    and not (h_rs and self._is_w_same(w_rs, h_rs, key)) +                ): +                    if key == "enable_default_log": +                        if val and ( +                            not h_rs or key not in h_rs or not h_rs[key] +                        ): +                            commands.append( +                                self._add_rs_base_attrib( +                                    afi, want["name"], key, w_rs +                                ) +                            ) +                    else: +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs +                            ) +                        ) +                elif not opr and key in l_set: +                    if ( +                        key == "enable_default_log" +                        and val +                        and h_rs +                        and (key not in h_rs or not h_rs[key]) +                    ): +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs, opr +                            ) +                        ) +                    elif not (h_rs and self._in_target(h_rs, key)): +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs, opr +                            ) +                        ) +            commands.extend( +                self._add_rules(afi, want["name"], w_rules, h_rules, opr) +            ) +        if h_rules: +            have["rules"] = h_rules +        if w_rules: +            want["rules"] = w_rules +        return commands + +    def _add_rules(self, afi, name, w_rules, h_rules, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for rules attributes. +        :param want: desired config. +        :param have: target config. +        :return: generated commands list. +        """ +        commands = [] +        l_set = ( +            "ipsec", +            "action", +            "number", +            "protocol", +            "fragment", +            "disabled", +            "description", +        ) +        if w_rules: +            for w in w_rules: +                cmd = self._compute_command(afi, name, w["number"], opr=opr) +                h = self.search_r_sets_in_have( +                    h_rules, w["number"], type="rules" +                ) +                for key, val in iteritems(w): +                    if val: +                        if ( +                            opr +                            and key in l_set +                            and not (h and self._is_w_same(w, h, key)) +                        ): +                            if key == "disabled": +                                if not ( +                                    not val +                                    and (not h or key not in h or not h[key]) +                                ): +                                    commands.append( +                                        self._add_r_base_attrib( +                                            afi, name, key, w +                                        ) +                                    ) +                            else: +                                commands.append( +                                    self._add_r_base_attrib(afi, name, key, w) +                                ) +                        elif not opr: +                            if key == "number" and self._is_del(l_set, h): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                                continue +                            elif ( +                                key == "disabled" +                                and val +                                and h +                                and (key not in h or not h[key]) +                            ): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                            elif ( +                                key in l_set +                                and not (h and self._in_target(h, key)) +                                and not self._is_del(l_set, h) +                            ): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                        elif key == "p2p": +                            commands.extend(self._add_p2p(key, w, h, cmd, opr)) +                        elif key == "tcp": +                            commands.extend(self._add_tcp(key, w, h, cmd, opr)) +                        elif key == "time": +                            commands.extend( +                                self._add_time(key, w, h, cmd, opr) +                            ) +                        elif key == "icmp": +                            commands.extend( +                                self._add_icmp(key, w, h, cmd, opr) +                            ) +                        elif key == "state": +                            commands.extend( +                                self._add_state(key, w, h, cmd, opr) +                            ) +                        elif key == "limit": +                            commands.extend( +                                self._add_limit(key, w, h, cmd, opr) +                            ) +                        elif key == "recent": +                            commands.extend( +                                self._add_recent(key, w, h, cmd, opr) +                            ) +                        elif key == "destination" or key == "source": +                            commands.extend( +                                self._add_src_or_dest(key, w, h, cmd, opr) +                            ) +        return commands + +    def _add_p2p(self, attr, w, h, cmd, opr): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for p2p applications attributes. +        :param want: desired config. +        :param have: target config. +        :return: generated commands list. +        """ +        commands = [] +        have = [] +        if w: +            want = w.get(attr) or [] +        if h: +            have = h.get(attr) or [] +        if want: +            if opr: +                applications = list_diff_want_only(want, have) +                for app in applications: +                    commands.append( +                        cmd + (" " + attr + " " + app["application"]) +                    ) +            elif not opr and have: +                applications = list_diff_want_only(want, have) +                for app in applications: +                    commands.append( +                        cmd + (" " + attr + " " + app["application"]) +                    ) +        return commands + +    def _add_state(self, attr, w, h, cmd, opr): +        """ +        This function forms the command for 'state' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_state = {} +        commands = [] +        l_set = ("new", "invalid", "related", "established") +        if w[attr]: +            if h and attr in h.keys(): +                h_state = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not ( +                        h_state and self._is_w_same(w[attr], h_state, item) +                    ) +                ): +                    commands.append( +                        cmd +                        + ( +                            " " +                            + attr +                            + " " +                            + item +                            + " " +                            + self._bool_to_str(val) +                        ) +                    ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_state and self._in_target(h_state, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_recent(self, attr, w, h, cmd, opr): +        """ +        This function forms the command for 'recent' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_recent = {} +        l_set = ("count", "time") +        if w[attr]: +            if h and attr in h.keys(): +                h_recent = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not ( +                        h_recent and self._is_w_same(w[attr], h_recent, item) +                    ) +                ): +                    commands.append( +                        cmd + (" " + attr + " " + item + " " + str(val)) +                    ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_recent and self._in_target(h_recent, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_icmp(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'icmp' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_icmp = {} +        l_set = ("code", "type", "type_name") +        if w[attr]: +            if h and attr in h.keys(): +                h_icmp = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)) +                ): +                    if item == "type_name": +                        if "ipv6-name" in cmd: +                            commands.append( +                                cmd +                                + (" " + "icmpv6" + " " + "type" + " " + val) +                            ) +                        else: +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + item.replace("_", "-") +                                    + " " +                                    + val +                                ) +                            ) +                    else: +                        commands.append( +                            cmd + (" " + attr + " " + item + " " + str(val)) +                        ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_icmp and self._in_target(h_icmp, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_time(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'time' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_time = {} +        l_set = ( +            "utc", +            "stopdate", +            "stoptime", +            "weekdays", +            "monthdays", +            "startdate", +            "starttime", +        ) +        if w[attr]: +            if h and attr in h.keys(): +                h_time = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not (h_time and self._is_w_same(w[attr], h_time, item)) +                ): +                    if item == "utc": +                        if not ( +                            not val and (not h_time or item not in h_time) +                        ): +                            commands.append(cmd + (" " + attr + " " + item)) +                    else: +                        commands.append( +                            cmd + (" " + attr + " " + item + " " + val) +                        ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_time and self._is_w_same(w[attr], h_time, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_tcp(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'tcp' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_tcp = {} +        commands = [] +        if w[attr]: +            key = "flags" +            flags = w[attr].get(key) or {} +            if flags: +                if h and key in h[attr].keys(): +                    h_tcp = h[attr].get(key) or {} +                if flags: +                    if opr and not ( +                        h_tcp and self._is_w_same(w[attr], h[attr], key) +                    ): +                        commands.append( +                            cmd + (" " + attr + " " + key + " " + flags) +                        ) +                    if not opr and not ( +                        h_tcp and self._is_w_same(w[attr], h[attr], key) +                    ): +                        commands.append( +                            cmd + (" " + attr + " " + key + " " + flags) +                        ) +        return commands + +    def _add_limit(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'limit' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_limit = {} +        commands = [] +        if w[attr]: +            key = "burst" +            if ( +                opr +                and key in w[attr].keys() +                and not ( +                    h +                    and attr in h.keys() +                    and self._is_w_same(w[attr], h[attr], key) +                ) +            ): +                commands.append( +                    cmd +                    + (" " + attr + " " + key + " " + str(w[attr].get(key))) +                ) +            elif ( +                not opr +                and key in w[attr].keys() +                and not ( +                    h and attr in h.keys() and self._in_target(h[attr], key) +                ) +            ): +                commands.append( +                    cmd +                    + (" " + attr + " " + key + " " + str(w[attr].get(key))) +                ) +            key = "rate" +            rate = w[attr].get(key) or {} +            if rate: +                if h and key in h[attr].keys(): +                    h_limit = h[attr].get(key) or {} +                if "unit" in rate and "number" in rate: +                    if opr and not ( +                        h_limit +                        and self._is_w_same(rate, h_limit, "unit") +                        and self.is_w_same(rate, h_limit, "number") +                    ): +                        commands.append( +                            cmd +                            + ( +                                " " +                                + attr +                                + " " +                                + key +                                + " " +                                + str(rate["number"]) +                                + "/" +                                + rate["unit"] +                            ) +                        ) +                    if not opr and not ( +                        h_limit +                        and self._is_w_same(rate, h_limit, "unit") +                        and self._is_w_same(rate, h_limit, "number") +                    ): +                        commands.append(cmd + (" " + attr + " " + key)) +        return commands + +    def _add_src_or_dest(self, attr, w, h, cmd, opr=True): +        """ +        This function forms the commands for 'src/dest' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_group = {} +        g_set = ("port_group", "address_group", "network_group") +        if w[attr]: +            keys = ("address", "mac_address", "port") +            for key in keys: +                if ( +                    opr +                    and key in w[attr].keys() +                    and not ( +                        h +                        and attr in h.keys() +                        and self._is_w_same(w[attr], h[attr], key) +                    ) +                ): +                    commands.append( +                        cmd +                        + ( +                            " " +                            + attr +                            + " " +                            + key.replace("_", "-") +                            + " " +                            + w[attr].get(key) +                        ) +                    ) +                elif ( +                    not opr +                    and key in w[attr].keys() +                    and not ( +                        h +                        and attr in h.keys() +                        and self._in_target(h[attr], key) +                    ) +                ): +                    commands.append(cmd + (" " + attr + " " + key)) + +            key = "group" +            group = w[attr].get(key) or {} +            if group: +                if h and key in h[attr].keys(): +                    h_group = h[attr].get(key) or {} +                for item, val in iteritems(group): +                    if val: +                        if ( +                            opr +                            and item in g_set +                            and not ( +                                h_group +                                and self._is_w_same(group, h_group, item) +                            ) +                        ): +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + key +                                    + " " +                                    + item.replace("_", "-") +                                    + " " +                                    + val +                                ) +                            ) +                        elif ( +                            not opr +                            and item in g_set +                            and not ( +                                h_group and self._in_target(h_group, item) +                            ) +                        ): +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + key +                                    + " " +                                    + item.replace("_", "-") +                                ) +                            ) +        return commands + +    def search_r_sets_in_have(self, have, w_name, type="rule_sets"): +        """ +        This function  returns the rule-set/rule if it is present in target config. +        :param have: target config. +        :param w_name: rule-set name. +        :param type: rule_sets/rule/r_list. +        :return: rule-set/rule. +        """ +        if have: +            key = "name" +            if type == "rules": +                key = "number" +                for r in have: +                    if r[key] == w_name: +                        return r +            elif type == "r_list": +                for h in have: +                    r_sets = self._get_r_sets(h) +                    for rs in r_sets: +                        if rs[key] == w_name: +                            return rs +            else: +                for rs in have: +                    if rs[key] == w_name: +                        return rs +        return None + +    def _get_r_sets(self, item, type="rule_sets"): +        """ +        This function returns the list of rule-sets/rules. +        :param item: config dictionary. +        :param type: rule_sets/rule/r_list. +        :return: list of rule-sets/rules. +        """ +        rs_list = [] +        r_sets = item[type] +        if r_sets: +            for rs in r_sets: +                rs_list.append(rs) +        return rs_list + +    def _compute_command( +        self, +        afi, +        name=None, +        number=None, +        attrib=None, +        value=None, +        remove=False, +        opr=True, +    ): +        """ +        This function construct the add/delete command based on passed attributes. +        :param afi:  address type. +        :param name:  rule-set name. +        :param number: rule-number. +        :param attrib: attribute name. +        :param value: value. +        :param remove: True if delete command needed to be construct. +        :param opr: opeeration flag. +        :return: generated command. +        """ +        if remove or not opr: +            cmd = "delete firewall " + self._get_fw_type(afi) +        else: +            cmd = "set firewall " + self._get_fw_type(afi) +        if name: +            cmd += " " + name +        if number: +            cmd += " rule " + str(number) +        if attrib: +            cmd += " " + attrib.replace("_", "-") +        if ( +            value +            and opr +            and attrib != "enable_default_log" +            and attrib != "disabled" +        ): +            cmd += " '" + str(value) + "'" +        return cmd + +    def _add_r_base_attrib(self, afi, name, attr, rule, opr=True): +        """ +        This function forms the command for 'rules' attributes which doesn't +        have further sub attributes. +        :param afi: address type. +        :param name: rule-set name +        :param attrib: attribute name +        :param rule: rule config dictionary. +        :param opr: True/False. +        :return: generated command. +        """ +        if attr == "number": +            command = self._compute_command( +                afi=afi, name=name, number=rule["number"], opr=opr +            ) +        else: +            command = self._compute_command( +                afi=afi, +                name=name, +                number=rule["number"], +                attrib=attr, +                value=rule[attr], +                opr=opr, +            ) +        return command + +    def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True): +        """ + +        This function forms the command for 'rule-sets' attributes which doesn't +        have further sub attributes. +        :param afi: address type. +        :param name: rule-set name +        :param attrib: attribute name +        :param rule: rule config dictionary. +        :param opr: True/False. +        :return: generated command. +        """ +        command = self._compute_command( +            afi=afi, name=name, attrib=attrib, value=rule[attrib], opr=opr +        ) +        return command + +    def _bool_to_str(self, val): +        """ +        This function converts the bool value into string. +        :param val: bool value. +        :return: enable/disable. +        """ +        return "enable" if val else "disable" + +    def _get_fw_type(self, afi): +        """ +        This function returns the firewall rule-set type based on IP address. +        :param afi: address type +        :return: rule-set type. +        """ +        return "ipv6-name" if afi == "ipv6" else "name" + +    def _is_del(self, l_set, h, key="number"): +        """ +        This function checks whether rule needs to be deleted based on +        the rule number. +        :param l_set: attribute set. +        :param h: target config. +        :param key: number. +        :return: True/False. +        """ +        return key in l_set and not (h and self._in_target(h, key)) + +    def _is_w_same(self, w, h, key): +        """ +        This function checks whether the key value is same in base and +        target config dictionary. +        :param w: base config. +        :param h: target config. +        :param key:attribute name. +        :return: True/False. +        """ +        return True if h and key in h and h[key] == w[key] else False + +    def _in_target(self, h, key): +        """ +        This function checks whether the target nexist and key present in target config. +        :param h: target config. +        :param key: attribute name. +        :return: True/False. +        """ +        return True if h and key in h else False + +    def _is_base_attrib(self, key): +        """ +        This function checks whether key is present in predefined +        based attribute set. +        :param key: +        :return: True/False. +        """ +        r_set = ( +            "p2p", +            "ipsec", +            "action", +            "fragment", +            "protocol", +            "disabled", +            "description", +            "mac_address", +            "default_action", +            "enable_default_log", +        ) +        return True if key in r_set else False diff --git a/plugins/module_utils/network/vyos/config/static_routes/__init__.py b/plugins/module_utils/network/vyos/config/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/config/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py new file mode 100644 index 00000000..e93d4ee3 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py @@ -0,0 +1,627 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_static_routes class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( +    ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( +    to_list, +    dict_diff, +    remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( +    Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    get_route_type, +    get_lst_diff_for_dicts, +    get_lst_same_for_dicts, +    dict_delete, +) + + +class Static_routes(ConfigBase): +    """ +    The vyos_static_routes class +    """ + +    gather_subset = [ +        "!all", +        "!min", +    ] + +    gather_network_resources = [ +        "static_routes", +    ] + +    def __init__(self, module): +        super(Static_routes, self).__init__(module) + +    def get_static_routes_facts(self, data=None): +        """ Get the 'facts' (the current configuration) + +        :rtype: A dictionary +        :returns: The current configuration as a dictionary +        """ +        facts, _warnings = Facts(self._module).get_facts( +            self.gather_subset, self.gather_network_resources, data=data +        ) +        static_routes_facts = facts["ansible_network_resources"].get( +            "static_routes" +        ) +        if not static_routes_facts: +            return [] +        return static_routes_facts + +    def execute_module(self): +        """ Execute the module + +        :rtype: A dictionary +        :returns: The result from module execution +        """ +        result = {"changed": False} +        warnings = list() +        commands = list() + +        if self.state in self.ACTION_STATES: +            existing_static_routes_facts = self.get_static_routes_facts() +        else: +            existing_static_routes_facts = [] + +        if self.state in self.ACTION_STATES or self.state == "rendered": +            commands.extend(self.set_config(existing_static_routes_facts)) + +        if commands and self.state in self.ACTION_STATES: +            if not self._module.check_mode: +                self._connection.edit_config(commands) +            result["changed"] = True + +        if self.state in self.ACTION_STATES: +            result["commands"] = commands + +        if self.state in self.ACTION_STATES or self.state == "gathered": +            changed_static_routes_facts = self.get_static_routes_facts() +        elif self.state == "rendered": +            result["rendered"] = commands +        elif self.state == "parsed": +            running_config = self._module.params["running_config"] +            if not running_config: +                self._module.fail_json( +                    msg="value of running_config parameter must not be empty for state parsed" +                ) +            result["parsed"] = self.get_static_routes_facts( +                data=running_config +            ) +        else: +            changed_static_routes_facts = [] + +        if self.state in self.ACTION_STATES: +            result["before"] = existing_static_routes_facts +            if result["changed"]: +                result["after"] = changed_static_routes_facts +        elif self.state == "gathered": +            result["gathered"] = changed_static_routes_facts + +        result["warnings"] = warnings +        return result + +    def set_config(self, existing_static_routes_facts): +        """ Collect the configuration from the args passed to the module, +            collect the current configuration (as a dict from facts) + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        want = self._module.params["config"] +        have = existing_static_routes_facts +        resp = self.set_state(want, have) +        return to_list(resp) + +    def set_state(self, want, have): +        """ Select the appropriate function based on the state provided + +        :param want: the desired configuration as a dictionary +        :param have: the current configuration as a dictionary +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if ( +            self.state in ("merged", "replaced", "overridden", "rendered") +            and not want +        ): +            self._module.fail_json( +                msg="value of config parameter must not be empty for state {0}".format( +                    self.state +                ) +            ) +        if self.state == "overridden": +            commands.extend(self._state_overridden(want=want, have=have)) +        elif self.state == "deleted": +            commands.extend(self._state_deleted(want=want, have=have)) +        elif want: +            routes = self._get_routes(want) +            for r in routes: +                h_item = self.search_route_in_have(have, r["dest"]) +                if self.state == "merged" or self.state == "rendered": +                    commands.extend(self._state_merged(want=r, have=h_item)) +                elif self.state == "replaced": +                    commands.extend(self._state_replaced(want=r, have=h_item)) +        return commands + +    def search_route_in_have(self, have, want_dest): +        """ +        This function  returns the route if its found in +        have config. +        :param have: +        :param dest: +        :return: the matched route +        """ +        routes = self._get_routes(have) +        for r in routes: +            if r["dest"] == want_dest: +                return r +        return None + +    def _state_replaced(self, want, have): +        """ The command generator when state is replaced + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if have: +            for key, value in iteritems(want): +                if value: +                    if key == "next_hops": +                        commands.extend(self._update_next_hop(want, have)) +                    elif key == "blackhole_config": +                        commands.extend( +                            self._update_blackhole(key, want, have) +                        ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_overridden(self, want, have): +        """ The command generator when state is overridden + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        routes = self._get_routes(have) +        for r in routes: +            route_in_want = self.search_route_in_have(want, r["dest"]) +            if not route_in_want: +                commands.append(self._compute_command(r["dest"], remove=True)) +        routes = self._get_routes(want) +        for r in routes: +            route_in_have = self.search_route_in_have(have, r["dest"]) +            commands.extend(self._state_replaced(r, route_in_have)) +        return commands + +    def _state_merged(self, want, have, opr=True): +        """ The command generator when state is merged + +        :rtype: A list +        :returns: the commands necessary to merge the provided into +                  the current configuration +        """ +        commands = [] +        if have: +            commands.extend(self._render_updates(want, have)) +        else: +            commands.extend(self._render_set_commands(want)) +        return commands + +    def _state_deleted(self, want, have): +        """ The command generator when state is deleted + +        :rtype: A list +        :returns: the commands necessary to remove the current configuration +                  of the provided objects +        """ +        commands = [] +        if want: +            routes = self._get_routes(want) +            if not routes: +                for w in want: +                    af = w["address_families"] +                    for item in af: +                        if self.afi_in_have(have, item): +                            commands.append( +                                self._compute_command( +                                    afi=item["afi"], remove=True +                                ) +                            ) +            for r in routes: +                h_route = self.search_route_in_have(have, r["dest"]) +                if h_route: +                    commands.extend( +                        self._render_updates(r, h_route, opr=False) +                    ) +        else: +            routes = self._get_routes(have) +            if self._is_ip_route_exist(routes): +                commands.append(self._compute_command(afi="ipv4", remove=True)) +            if self._is_ip_route_exist(routes, "route6"): +                commands.append(self._compute_command(afi="ipv6", remove=True)) +        return commands + +    def _render_set_commands(self, want): +        """ +        This function returns the list of commands to add attributes which are +        present in want +        :param want: +        :return: list of commands. +        """ +        commands = [] +        have = {} +        for key, value in iteritems(want): +            if value: +                if key == "dest": +                    commands.append(self._compute_command(dest=want["dest"])) +                elif key == "blackhole_config": +                    commands.extend(self._add_blackhole(key, want, have)) + +                elif key == "next_hops": +                    commands.extend(self._add_next_hop(want, have)) + +        return commands + +    def _add_blackhole(self, key, want, have): +        """ +        This function gets the diff for blackhole config specific attributes +        and form the commands for attributes which are present in want but not in have. +        :param key: +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        want_blackhole = want_copy.get(key) or {} +        have_blackhole = have_copy.get(key) or {} + +        updates = dict_delete(want_blackhole, have_blackhole) +        if updates: +            for attrib, value in iteritems(updates): +                if value: +                    if attrib == "distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="blackhole", +                                attrib=attrib, +                                remove=False, +                                value=str(value), +                            ) +                        ) +                    elif attrib == "type": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], key="blackhole" +                            ) +                        ) +        return commands + +    def _add_next_hop(self, want, have, opr=True): +        """ +        This function gets the diff for next hop specific attributes +        and form the commands to add attributes which are present in want but not in have. +        :param want: +        :param have: +        :return: list of commands. +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) +        if not opr: +            diff_next_hops = get_lst_same_for_dicts( +                want_copy, have_copy, "next_hops" +            ) +        else: +            diff_next_hops = get_lst_diff_for_dicts( +                want_copy, have_copy, "next_hops" +            ) +        if diff_next_hops: +            for hop in diff_next_hops: +                for element in hop: +                    if element == "forward_router_address": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                value=hop[element], +                                opr=opr, +                            ) +                        ) +                    elif element == "enabled" and not hop[element]: +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"], +                                value="disable", +                                opr=opr, +                            ) +                        ) +                    elif element == "admin_distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=str(hop[element]), +                                opr=opr, +                            ) +                        ) +                    elif element == "interface": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=hop[element], +                                opr=opr, +                            ) +                        ) +        return commands + +    def _update_blackhole(self, key, want, have): +        """ +        This function gets the difference for blackhole dict and +        form the commands to delete the attributes which are present in have but not in want. +        :param want: +        :param have: +        :return: list of commands +        :param key: +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        want_blackhole = want_copy.get(key) or {} +        have_blackhole = have_copy.get(key) or {} +        updates = dict_delete(have_blackhole, want_blackhole) +        if updates: +            for attrib, value in iteritems(updates): +                if value: +                    if attrib == "distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="blackhole", +                                attrib=attrib, +                                remove=True, +                                value=str(value), +                            ) +                        ) +                    elif ( +                        attrib == "type" +                        and "distance" not in want_blackhole.keys() +                    ): +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], key="blackhole", remove=True +                            ) +                        ) +        return commands + +    def _update_next_hop(self, want, have, opr=True): +        """ +        This function gets the difference for next_hops list and +        form the commands to delete the attributes which are present in have but not in want. +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] + +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        diff_next_hops = get_lst_diff_for_dicts( +            have_copy, want_copy, "next_hops" +        ) +        if diff_next_hops: +            for hop in diff_next_hops: +                for element in hop: +                    if element == "forward_router_address": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                value=hop[element], +                                remove=True, +                            ) +                        ) +                    elif element == "enabled": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"], +                                value="disable", +                                remove=True, +                            ) +                        ) +                    elif element == "admin_distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=str(hop[element]), +                                remove=True, +                            ) +                        ) +                    elif element == "interface": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=hop[element], +                                remove=True, +                            ) +                        ) +        return commands + +    def _render_updates(self, want, have, opr=True): +        """ +        This function takes the diff between want and have and +        invokes the appropriate functions to create the commands +        to update the attributes. +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_nh = want.get("next_hops") or [] +        # delete static route operation per destination +        if not opr and not want_nh: +            commands.append( +                self._compute_command(dest=want["dest"], remove=True) +            ) + +        else: +            temp_have_next_hops = have.pop("next_hops", None) +            temp_want_next_hops = want.pop("next_hops", None) +            updates = dict_diff(have, want) +            if temp_have_next_hops: +                have["next_hops"] = temp_have_next_hops +            if temp_want_next_hops: +                want["next_hops"] = temp_want_next_hops +            commands.extend(self._add_next_hop(want, have, opr=opr)) + +            if opr and updates: +                for key, value in iteritems(updates): +                    if value: +                        if key == "blackhole_config": +                            commands.extend( +                                self._add_blackhole(key, want, have) +                            ) +        return commands + +    def _compute_command( +        self, +        dest=None, +        key=None, +        attrib=None, +        value=None, +        remove=False, +        afi=None, +        opr=True, +    ): +        """ +        This functions construct the required command based on the passed arguments. +        :param dest: +        :param key: +        :param attrib: +        :param value: +        :param remove: +        :return:  constructed command +        """ +        if remove or not opr: +            cmd = "delete protocols static " + self.get_route_type(dest, afi) +        else: +            cmd = "set protocols static " + self.get_route_type(dest, afi) +        if dest: +            cmd += " " + dest +        if key: +            cmd += " " + key +        if attrib: +            cmd += " " + attrib +        if value: +            cmd += " '" + value + "'" +        return cmd + +    def afi_in_have(self, have, w_item): +        """ +        This functions checks for the afi +        list in have +        :param have: +        :param w_item: +        :return: +        """ +        if have: +            for h in have: +                af = h.get("address_families") or [] +            for item in af: +                if w_item["afi"] == item["afi"]: +                    return True +        return False + +    def get_route_type(self, dest=None, afi=None): +        """ +        This function returns the route type based on +        destination ip address or afi +        :param address: +        :return: +        """ +        if dest: +            return get_route_type(dest) +        elif afi == "ipv4": +            return "route" +        elif afi == "ipv6": +            return "route6" + +    def _is_ip_route_exist(self, routes, type="route"): +        """ +        This functions checks for the type of route. +        :param routes: +        :param type: +        :return: True/False +        """ +        for r in routes: +            if type == self.get_route_type(r["dest"]): +                return True +        return False + +    def _get_routes(self, lst): +        """ +        This function returns the list of routes +        :param lst: list of address families +        :return: list of routes +        """ +        r_list = [] +        for item in lst: +            af = item["address_families"] +            for element in af: +                routes = element.get("routes") or [] +                for r in routes: +                    r_list.append(r) +        return r_list diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index b5816c20..8f0a3bb6 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -27,6 +27,12 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import (      Lldp_interfacesFacts,  ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( +    Firewall_rulesFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( +    Static_routesFacts, +)  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (      Default,      Neighbors, @@ -41,6 +47,8 @@ FACT_RESOURCE_SUBSETS = dict(      lag_interfaces=Lag_interfacesFacts,      lldp_global=Lldp_globalFacts,      lldp_interfaces=Lldp_interfacesFacts, +    static_routes=Static_routesFacts, +    firewall_rules=Firewall_rulesFacts,  ) @@ -72,5 +80,4 @@ class Facts(FactsBase):              self.get_network_legacy_facts(                  FACT_LEGACY_SUBSETS, legacy_facts_type              ) -          return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py new file mode 100644 index 00000000..971ea6fe --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -0,0 +1,380 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos firewall_rules fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( +    utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( +    Firewall_rulesArgs, +) + + +class Firewall_rulesFacts(object): +    """ The vyos firewall_rules fact class +    """ + +    def __init__(self, module, subspec="config", options="options"): +        self._module = module +        self.argument_spec = Firewall_rulesArgs.argument_spec +        spec = deepcopy(self.argument_spec) +        if subspec: +            if options: +                facts_argument_spec = spec[subspec][options] +            else: +                facts_argument_spec = spec[subspec] +        else: +            facts_argument_spec = spec + +        self.generated_spec = utils.generate_dict(facts_argument_spec) + +    def get_device_data(self, connection): +        return connection.get_config() + +    def populate_facts(self, connection, ansible_facts, data=None): +        """ Populate the facts for firewall_rules +        :param connection: the device connection +        :param ansible_facts: Facts dictionary +        :param data: previously collected conf +        :rtype: dictionary +        :returns: facts +        """ +        if not data: +            # typically data is populated from the current device configuration +            # data = connection.get('show running-config | section ^interface') +            # using mock data instead +            data = self.get_device_data(connection) +        # split the config into instances of the resource +        objs = [] +        v6_rules = findall( +            r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M +        ) +        v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) +        if v6_rules: +            config = self.get_rules(data, v6_rules, type="ipv6") +            if config: +                config = utils.remove_empties(config) +                objs.append(config) +        if v4_rules: +            config = self.get_rules(data, v4_rules, type="ipv4") +            if config: +                config = utils.remove_empties(config) +                objs.append(config) + +        ansible_facts["ansible_network_resources"].pop("firewall_rules", None) +        facts = {} +        if objs: +            facts["firewall_rules"] = [] +            params = utils.validate_config( +                self.argument_spec, {"config": objs} +            ) +            for cfg in params["config"]: +                facts["firewall_rules"].append(utils.remove_empties(cfg)) + +        ansible_facts["ansible_network_resources"].update(facts) +        return ansible_facts + +    def get_rules(self, data, rules, type): +        """ +        This function performs following: +        - Form regex to fetch 'rule-sets' specific config from data. +        - Form the rule-set list based on ip address. +        :param data: configuration. +        :param rules: list of rule-sets. +        :param type: ip address type. +        :return: generated rule-sets configuration. +        """ +        r_v4 = [] +        r_v6 = [] +        for r in set(rules): +            rule_regex = r" %s .+$" % r.strip("'") +            cfg = findall(rule_regex, data, M) +            fr = self.render_config(cfg, r.strip("'")) +            fr["name"] = r.strip("'") +            if type == "ipv6": +                r_v6.append(fr) +            else: +                r_v4.append(fr) +        if r_v4: +            config = {"afi": "ipv4", "rule_sets": r_v4} +        if r_v6: +            config = {"afi": "ipv6", "rule_sets": r_v6} +        return config + +    def render_config(self, conf, match): +        """ +        Render config as dictionary structure and delete keys +          from spec for null values + +        :param spec: The facts tree, generated from the argspec +        :param conf: The configuration +        :rtype: dictionary +        :returns: The generated config +        """ +        conf = "\n".join(filter(lambda x: x, conf)) +        a_lst = ["description", "default_action", "enable_default_log"] +        config = self.parse_attr(conf, a_lst, match) +        if not config: +            config = {} +        config["rules"] = self.parse_rules_lst(conf) +        return config + +    def parse_rules_lst(self, conf): +        """ +        This function forms the regex to fetch the 'rules' with in +        'rule-sets' +        :param conf: configuration data. +        :return: generated rule list configuration. +        """ +        r_lst = [] +        rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) +        if rules: +            rules_lst = [] +            for r in set(rules): +                r_regex = r" %s .+$" % r +                cfg = "\n".join(findall(r_regex, conf, M)) +                obj = self.parse_rules(cfg) +                obj["number"] = int(r) +                if obj: +                    rules_lst.append(obj) +            r_lst = sorted(rules_lst, key=lambda i: i["number"]) +        return r_lst + +    def parse_rules(self, conf): +        """ +        This function triggers the parsing of 'rule' attributes. +        a_lst is a list having rule attributes which doesn't +        have further sub attributes. +        :param conf: configuration +        :return: generated rule configuration dictionary. +        """ +        a_lst = [ +            "ipsec", +            "action", +            "protocol", +            "fragment", +            "disabled", +            "description", +        ] +        rule = self.parse_attr(conf, a_lst) +        r_sub = { +            "p2p": self.parse_p2p(conf), +            "tcp": self.parse_tcp(conf, "tcp"), +            "icmp": self.parse_icmp(conf, "icmp"), +            "time": self.parse_time(conf, "time"), +            "limit": self.parse_limit(conf, "limit"), +            "state": self.parse_state(conf, "state"), +            "recent": self.parse_recent(conf, "recent"), +            "source": self.parse_src_or_dest(conf, "source"), +            "destination": self.parse_src_or_dest(conf, "destination"), +        } +        rule.update(r_sub) +        return rule + +    def parse_p2p(self, conf): +        """ +        This function forms the regex to fetch the 'p2p' with in +        'rules' +        :param conf: configuration data. +        :return: generated rule list configuration. +        """ +        a_lst = [] +        applications = findall(r"p2p (?:\'*)(\d+)(?:\'*)", conf, M) +        if applications: +            app_lst = [] +            for r in set(applications): +                obj = {"application": r.strip("'")} +                app_lst.append(obj) +            a_lst = sorted(app_lst, key=lambda i: i["application"]) +        return a_lst + +    def parse_src_or_dest(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'source or +        destination' attributes. +        :param conf: configuration. +        :param attrib:'source/destination'. +        :return:generated source/destination configuration dictionary. +        """ +        a_lst = ["port", "address", "mac_address"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        cfg_dict["group"] = self.parse_group(conf, attrib + " group") +        return cfg_dict + +    def parse_recent(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'recent' attributes +        :param conf: configuration. +        :param attrib: 'recent'. +        :return: generated config dictionary. +        """ +        a_lst = ["time", "count"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_tcp(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'tcp' attributes. +        :param conf: configuration. +        :param attrib: 'tcp'. +        :return: generated config dictionary. +        """ +        cfg_dict = self.parse_attr(conf, ["flags"], match=attrib) +        return cfg_dict + +    def parse_time(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'time' attributes. +        :param conf: configuration. +        :param attrib: 'time'. +        :return: generated config dictionary. +        """ +        a_lst = [ +            "stopdate", +            "stoptime", +            "weekdays", +            "monthdays", +            "startdate", +            "starttime", +        ] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_state(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'state' attributes. +        :param conf: configuration +        :param attrib: 'state'. +        :return: generated config dictionary. +        """ +        a_lst = ["new", "invalid", "related", "established"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_group(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'group' attributes. +        :param conf: configuration. +        :param attrib: 'group'. +        :return: generated config dictionary. +        """ +        a_lst = ["port_group", "address_group", "network_group"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_icmp(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'icmp' attributes. +        :param conf: configuration to be parsed. +        :param attrib: 'icmp'. +        :return: generated config dictionary. +        """ +        a_lst = ["code", "type", "type_name"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_limit(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'limit' attributes. +        :param conf: configuration to be parsed. +        :param attrib: 'limit' +        :return: generated config dictionary. +        """ +        cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) +        cfg_dict["rate"] = self.parse_rate(conf, "rate") +        return cfg_dict + +    def parse_rate(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'rate' attributes. +        :param conf: configuration. +        :param attrib: 'rate' +        :return: generated config dictionary. +        """ +        a_lst = ["unit", "number"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_attr(self, conf, attr_list, match=None): +        """ +        This function peforms the following: +        - Form the regex to fetch the required attribute config. +        - Type cast the output in desired format. +        :param conf: configuration. +        :param attr_list: list of attributes. +        :param match: parent node/attribute name. +        :return: generated config dictionary. +        """ +        config = {} +        for attrib in attr_list: +            regex = self.map_regex(attrib) +            if match: +                regex = match + " " + regex +            if conf: +                if self.is_bool(attrib): +                    out = conf.find(attrib.replace("_", "-")) + +                    dis = conf.find(attrib.replace("_", "-") + " 'disable'") +                    if out >= 1: +                        if dis >= 1: +                            config[attrib] = False +                        else: +                            config[attrib] = True +                else: +                    out = search(r"^.*" + regex + " (.+)", conf, M) +                    if out: +                        val = out.group(1).strip("'") +                        if self.is_num(attrib): +                            val = int(val) +                        config[attrib] = val +        return config + +    def map_regex(self, attrib): +        """ +        - This function construct the regex string. +        - replace the underscore with hyphen. +        :param attrib: attribute +        :return: regex string +        """ +        regex = attrib.replace("_", "-") +        if attrib == "disabled": +            regex = "disable" +        return regex + +    def is_bool(self, attrib): +        """ +        This function looks for the attribute in predefined bool type set. +        :param attrib: attribute. +        :return: True/False +        """ +        bool_set = ( +            "new", +            "invalid", +            "related", +            "disabled", +            "established", +            "enable_default_log", +        ) +        return True if attrib in bool_set else False + +    def is_num(self, attrib): +        """ +        This function looks for the attribute in predefined integer type set. +        :param attrib: attribute. +        :return: True/false. +        """ +        num_set = ("time", "code", "type", "count", "burst", "number") +        return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/static_routes/__init__.py b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py new file mode 100644 index 00000000..00049475 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -0,0 +1,181 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos static_routes fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( +    utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( +    Static_routesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    get_route_type, +) + + +class Static_routesFacts(object): +    """ The vyos static_routes fact class +    """ + +    def __init__(self, module, subspec="config", options="options"): +        self._module = module +        self.argument_spec = Static_routesArgs.argument_spec +        spec = deepcopy(self.argument_spec) +        if subspec: +            if options: +                facts_argument_spec = spec[subspec][options] +            else: +                facts_argument_spec = spec[subspec] +        else: +            facts_argument_spec = spec + +        self.generated_spec = utils.generate_dict(facts_argument_spec) + +    def get_device_data(self, connection): +        return connection.get_config() + +    def populate_facts(self, connection, ansible_facts, data=None): +        """ Populate the facts for static_routes +        :param connection: the device connection +        :param ansible_facts: Facts dictionary +        :param data: previously collected conf +        :rtype: dictionary +        :returns: facts +        """ +        if not data: +            data = self.get_device_data(connection) +            # typically data is populated from the current device configuration +            # data = connection.get('show running-config | section ^interface') +            # using mock data instead +        objs = [] +        r_v4 = [] +        r_v6 = [] +        af = [] +        static_routes = findall( +            r"set protocols static route(6)? (\S+)", data, M +        ) +        if static_routes: +            for route in set(static_routes): +                route_regex = r" %s .+$" % route[1] +                cfg = findall(route_regex, data, M) +                sr = self.render_config(cfg) +                sr["dest"] = route[1].strip("'") +                afi = self.get_afi(sr["dest"]) +                if afi == "ipv4": +                    r_v4.append(sr) +                else: +                    r_v6.append(sr) +            if r_v4: +                afi_v4 = {"afi": "ipv4", "routes": r_v4} +                af.append(afi_v4) +            if r_v6: +                afi_v6 = {"afi": "ipv6", "routes": r_v6} +                af.append(afi_v6) +            config = {"address_families": af} +            if config: +                objs.append(config) + +        ansible_facts["ansible_network_resources"].pop("static_routes", None) +        facts = {} +        if objs: +            facts["static_routes"] = [] +            params = utils.validate_config( +                self.argument_spec, {"config": objs} +            ) +            for cfg in params["config"]: +                facts["static_routes"].append(utils.remove_empties(cfg)) + +        ansible_facts["ansible_network_resources"].update(facts) +        return ansible_facts + +    def render_config(self, conf): +        """ +        Render config as dictionary structure and delete keys +          from spec for null values + +        :param spec: The facts tree, generated from the argspec +        :param conf: The configuration +        :rtype: dictionary +        :returns: The generated config +        """ +        next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x), conf)) +        blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf)) +        routes_dict = { +            "blackhole_config": self.parse_blackhole(blackhole_conf), +            "next_hops": self.parse_next_hop(next_hops_conf), +        } +        return routes_dict + +    def parse_blackhole(self, conf): +        blackhole = None +        if conf: +            distance = search(r"^.*blackhole distance (.\S+)", conf, M) +            bh = conf.find("blackhole") +            if distance is not None: +                blackhole = {} +                value = distance.group(1).strip("'") +                blackhole["distance"] = int(value) +            elif bh: +                blackhole = {} +                blackhole["type"] = "blackhole" +        return blackhole + +    def get_afi(self, address): +        route_type = get_route_type(address) +        if route_type == "route": +            return "ipv4" +        elif route_type == "route6": +            return "ipv6" + +    def parse_next_hop(self, conf): +        nh_list = None +        if conf: +            nh_list = [] +            hop_list = findall(r"^.*next-hop (.+)", conf, M) +            if hop_list: +                for hop in hop_list: +                    distance = search(r"^.*distance (.\S+)", hop, M) +                    interface = search(r"^.*interface (.\S+)", hop, M) + +                    dis = hop.find("disable") +                    hop_info = hop.split(" ") +                    nh_info = { +                        "forward_router_address": hop_info[0].strip("'") +                    } +                    if interface: +                        nh_info["interface"] = interface.group(1).strip("'") +                    if distance: +                        value = distance.group(1).strip("'") +                        nh_info["admin_distance"] = int(value) +                    elif dis >= 1: +                        nh_info["enabled"] = False +                    for element in nh_list: +                        if ( +                            element["forward_router_address"] +                            == nh_info["forward_router_address"] +                        ): +                            if "interface" in nh_info.keys(): +                                element["interface"] = nh_info["interface"] +                            if "admin_distance" in nh_info.keys(): +                                element["admin_distance"] = nh_info[ +                                    "admin_distance" +                                ] +                            if "enabled" in nh_info.keys(): +                                element["enabled"] = nh_info["enabled"] +                            nh_info = None +                    if nh_info is not None: +                        nh_list.append(nh_info) +        return nh_list diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 6504bcd9..402adfc9 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -8,6 +8,9 @@ from __future__ import absolute_import, division, print_function  __metaclass__ = type  from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( +    ipaddress, +)  def search_obj_in_list(name, lst, key="name"): @@ -87,6 +90,27 @@ def get_lst_diff_for_dicts(want, have, lst):      return diff +def get_lst_same_for_dicts(want, have, lst): +    """ +    This function generates a list containing values +    that are common for list in want and list in have dict +    :param want: dict object to want +    :param have: dict object to have +    :param lst: list the comparison on +    :return: new list object with values which are common in want and have. +    """ +    diff = None +    if want and have: +        want_list = want.get(lst) or {} +        have_list = have.get(lst) or {} +        diff = [ +            i +            for i in want_list and have_list +            if i in have_list and i in want_list +        ] +    return diff + +  def list_diff_have_only(want_list, have_list):      """      This function generated the list containing values @@ -178,3 +202,30 @@ def is_dict_element_present(dict, key):          if item == key:              return True      return False + + +def get_ip_address_version(address): +    """ +    This function returns the version of IP address +    :param address: IP address +    :return: +    """ +    try: +        address = unicode(address) +    except NameError: +        address = str(address) +    version = ipaddress.ip_address(address.split("/")[0]).version +    return version + + +def get_route_type(address): +    """ +    This function returns the route type based on IP address +    :param address: +    :return: +    """ +    version = get_ip_address_version(address) +    if version == 6: +        return "route6" +    elif version == 4: +        return "route"  | 
