diff options
Diffstat (limited to 'plugins')
18 files changed, 5376 insertions, 3 deletions
| diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py new file mode 100644 index 0000000..a018cc0 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -0,0 +1,263 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# +""" +The arg spec for the vyos_firewall_rules module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Firewall_rulesArgs(object):  # pylint: disable=R0903 +    """The arg spec for the vyos_firewall_rules module +    """ + +    def __init__(self, **kwargs): +        pass + +    argument_spec = { +        "config": { +            "elements": "dict", +            "options": { +                "afi": { +                    "choices": ["ipv4", "ipv6"], +                    "required": True, +                    "type": "str", +                }, +                "rule_sets": { +                    "elements": "dict", +                    "options": { +                        "default_action": { +                            "choices": ["drop", "reject", "accept"], +                            "type": "str", +                        }, +                        "description": {"type": "str"}, +                        "enable_default_log": {"type": "bool"}, +                        "name": {"type": "str"}, +                        "rules": { +                            "elements": "dict", +                            "options": { +                                "action": { +                                    "choices": [ +                                        "drop", +                                        "reject", +                                        "accept", +                                        "inspect", +                                    ], +                                    "type": "str", +                                }, +                                "description": {"type": "str"}, +                                "destination": { +                                    "options": { +                                        "address": {"type": "str"}, +                                        "group": { +                                            "options": { +                                                "address_group": { +                                                    "type": "str" +                                                }, +                                                "network_group": { +                                                    "type": "str" +                                                }, +                                                "port_group": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                        "port": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "disabled": {"type": "bool"}, +                                "fragment": { +                                    "choices": [ +                                        "match-frag", +                                        "match-non-frag", +                                    ], +                                    "type": "str", +                                }, +                                "icmp": { +                                    "options": { +                                        "code": {"type": "int"}, +                                        "type": {"type": "int"}, +                                        "type_name": { +                                            "choices": [ +                                                "any", +                                                "echo-reply", +                                                "destination-unreachable", +                                                "network-unreachable", +                                                "host-unreachable", +                                                "protocol-unreachable", +                                                "port-unreachable", +                                                "fragmentation-needed", +                                                "source-route-failed", +                                                "network-unknown", +                                                "host-unknown", +                                                "network-prohibited", +                                                "host-prohibited", +                                                "TOS-network-unreachable", +                                                "TOS-host-unreachable", +                                                "communication-prohibited", +                                                "host-precedence-violation", +                                                "precedence-cutoff", +                                                "source-quench", +                                                "redirect", +                                                "network-redirect", +                                                "host-redirect", +                                                "TOS-network-redirect", +                                                "TOS-host-redirect", +                                                "echo-request", +                                                "router-advertisement", +                                                "router-solicitation", +                                                "time-exceeded", +                                                "ttl-zero-during-transit", +                                                "ttl-zero-during-reassembly", +                                                "parameter-problem", +                                                "ip-header-bad", +                                                "required-option-missing", +                                                "timestamp-request", +                                                "timestamp-reply", +                                                "address-mask-request", +                                                "address-mask-reply", +                                                "ping", +                                                "pong", +                                                "ttl-exceeded", +                                            ], +                                            "type": "str", +                                        }, +                                    }, +                                    "type": "dict", +                                }, +                                "ipsec": { +                                    "choices": ["match-ipsec", "match-none"], +                                    "type": "str", +                                }, +                                "limit": { +                                    "options": { +                                        "burst": {"type": "int"}, +                                        "rate": { +                                            "options": { +                                                "number": {"type": "int"}, +                                                "unit": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                    }, +                                    "type": "dict", +                                }, +                                "number": {"required": True, "type": "int"}, +                                "p2p": { +                                    "elements": "dict", +                                    "options": { +                                        "application": { +                                            "choices": [ +                                                "all", +                                                "applejuice", +                                                "bittorrent", +                                                "directconnect", +                                                "edonkey", +                                                "gnutella", +                                                "kazaa", +                                            ], +                                            "type": "str", +                                        } +                                    }, +                                    "type": "list", +                                }, +                                "protocol": {"type": "str"}, +                                "recent": { +                                    "options": { +                                        "count": {"type": "int"}, +                                        "time": {"type": "int"}, +                                    }, +                                    "type": "dict", +                                }, +                                "source": { +                                    "options": { +                                        "address": {"type": "str"}, +                                        "group": { +                                            "options": { +                                                "address_group": { +                                                    "type": "str" +                                                }, +                                                "network_group": { +                                                    "type": "str" +                                                }, +                                                "port_group": {"type": "str"}, +                                            }, +                                            "type": "dict", +                                        }, +                                        "mac_address": {"type": "str"}, +                                        "port": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "state": { +                                    "options": { +                                        "established": {"type": "bool"}, +                                        "invalid": {"type": "bool"}, +                                        "new": {"type": "bool"}, +                                        "related": {"type": "bool"}, +                                    }, +                                    "type": "dict", +                                }, +                                "tcp": { +                                    "options": {"flags": {"type": "str"}}, +                                    "type": "dict", +                                }, +                                "time": { +                                    "options": { +                                        "monthdays": {"type": "str"}, +                                        "startdate": {"type": "str"}, +                                        "starttime": {"type": "str"}, +                                        "stopdate": {"type": "str"}, +                                        "stoptime": {"type": "str"}, +                                        "utc": {"type": "bool"}, +                                        "weekdays": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                            }, +                            "type": "list", +                        }, +                    }, +                    "type": "list", +                }, +            }, +            "type": "list", +        }, +        "running_config": {"type": "str"}, +        "state": { +            "choices": [ +                "merged", +                "replaced", +                "overridden", +                "deleted", +                "gathered", +                "rendered", +                "parsed", +            ], +            "default": "merged", +            "type": "str", +        }, +    }  # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py new file mode 100644 index 0000000..8ecd955 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py @@ -0,0 +1,99 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# +""" +The arg spec for the vyos_static_routes module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Static_routesArgs(object):  # pylint: disable=R0903 +    """The arg spec for the vyos_static_routes module +    """ + +    def __init__(self, **kwargs): +        pass + +    argument_spec = { +        "config": { +            "elements": "dict", +            "options": { +                "address_families": { +                    "elements": "dict", +                    "options": { +                        "afi": { +                            "choices": ["ipv4", "ipv6"], +                            "required": True, +                            "type": "str", +                        }, +                        "routes": { +                            "elements": "dict", +                            "options": { +                                "blackhole_config": { +                                    "options": { +                                        "distance": {"type": "int"}, +                                        "type": {"type": "str"}, +                                    }, +                                    "type": "dict", +                                }, +                                "dest": {"required": True, "type": "str"}, +                                "next_hops": { +                                    "elements": "dict", +                                    "options": { +                                        "admin_distance": {"type": "int"}, +                                        "enabled": {"type": "bool"}, +                                        "forward_router_address": { +                                            "required": True, +                                            "type": "str", +                                        }, +                                        "interface": {"type": "str"}, +                                    }, +                                    "type": "list", +                                }, +                            }, +                            "type": "list", +                        }, +                    }, +                    "type": "list", +                } +            }, +            "type": "list", +        }, +        "running_config": {"type": "str"}, +        "state": { +            "choices": [ +                "merged", +                "replaced", +                "overridden", +                "deleted", +                "gathered", +                "rendered", +                "parsed", +            ], +            "default": "merged", +            "type": "str", +        }, +    }  # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py new file mode 100644 index 0000000..e58593f --- /dev/null +++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py @@ -0,0 +1,1034 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_firewall_rules class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( +    ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( +    to_list, +    remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( +    Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    list_diff_want_only, +) + + +class Firewall_rules(ConfigBase): +    """ +    The vyos_firewall_rules class +    """ + +    gather_subset = [ +        "!all", +        "!min", +    ] + +    gather_network_resources = [ +        "firewall_rules", +    ] + +    def __init__(self, module): +        super(Firewall_rules, self).__init__(module) + +    def get_firewall_rules_facts(self, data=None): +        """ Get the 'facts' (the current configuration) + +        :rtype: A dictionary +        :returns: The current configuration as a dictionary +        """ +        facts, _warnings = Facts(self._module).get_facts( +            self.gather_subset, self.gather_network_resources, data=data +        ) +        firewall_rules_facts = facts["ansible_network_resources"].get( +            "firewall_rules" +        ) +        if not firewall_rules_facts: +            return [] +        return firewall_rules_facts + +    def execute_module(self): +        """ Execute the module + +        :rtype: A dictionary +        :returns: The result from module execution +        """ +        result = {"changed": False} +        warnings = list() +        commands = list() + +        if self.state in self.ACTION_STATES: +            existing_firewall_rules_facts = self.get_firewall_rules_facts() +        else: +            existing_firewall_rules_facts = [] + +        if self.state in self.ACTION_STATES or self.state == "rendered": +            commands.extend(self.set_config(existing_firewall_rules_facts)) + +        if commands and self.state in self.ACTION_STATES: +            if not self._module.check_mode: +                self._connection.edit_config(commands) +            result["changed"] = True + +        if self.state in self.ACTION_STATES: +            result["commands"] = commands + +        if self.state in self.ACTION_STATES or self.state == "gathered": +            changed_firewall_rules_facts = self.get_firewall_rules_facts() +        elif self.state == "rendered": +            result["rendered"] = commands +        elif self.state == "parsed": +            running_config = self._module.params["running_config"] +            if not running_config: +                self._module.fail_json( +                    msg="value of running_config parameter must not be empty for state parsed" +                ) +            result["parsed"] = self.get_firewall_rules_facts( +                data=running_config +            ) +        else: +            changed_firewall_rules_facts = [] + +        if self.state in self.ACTION_STATES: +            result["before"] = existing_firewall_rules_facts +            if result["changed"]: +                result["after"] = changed_firewall_rules_facts +        elif self.state == "gathered": +            result["gathered"] = changed_firewall_rules_facts + +        result["warnings"] = warnings +        return result + +    def set_config(self, existing_firewall_rules_facts): +        """ Collect the configuration from the args passed to the module, +            collect the current configuration (as a dict from facts) + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        want = self._module.params["config"] +        have = existing_firewall_rules_facts +        resp = self.set_state(want, have) +        return to_list(resp) + +    def set_state(self, w, h): +        """ Select the appropriate function based on the state provided + +        :param want: the desired configuration as a dictionary +        :param have: the current configuration as a dictionary +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if ( +            self.state in ("merged", "replaced", "overridden", "rendered") +            and not w +        ): +            self._module.fail_json( +                msg="value of config parameter must not be empty for state {0}".format( +                    self.state +                ) +            ) +        if self.state == "overridden": +            commands.extend(self._state_overridden(w, h)) +        elif self.state == "deleted": +            commands.extend(self._state_deleted(w, h)) +        elif w: +            if self.state == "merged" or self.state == "rendered": +                commands.extend(self._state_merged(w, h)) +            elif self.state == "replaced": +                commands.extend(self._state_replaced(w, h)) +        return commands + +    def _state_replaced(self, want, have): +        """ The command generator when state is replaced +         :rtype: A list +         :returns: the commands necessary to migrate the current configuration +                   to the desired configuration +         """ +        commands = [] +        if have: +            for h in have: +                r_sets = self._get_r_sets(h) +                for rs in r_sets: +                    w = self.search_r_sets_in_have(want, rs["name"], "r_list") +                    commands.extend( +                        self._add_r_sets(h["afi"], rs, w, opr=False) +                    ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_overridden(self, want, have): +        """ The command generator when state is overridden + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if have: +            for h in have: +                r_sets = self._get_r_sets(h) +                for rs in r_sets: +                    w = self.search_r_sets_in_have(want, rs["name"], "r_list") +                    if not w: +                        commands.append( +                            self._compute_command( +                                h["afi"], rs["name"], remove=True +                            ) +                        ) +                    else: +                        commands.extend( +                            self._add_r_sets(h["afi"], rs, w, opr=False) +                        ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_merged(self, want, have): +        """ The command generator when state is merged + +        :rtype: A list +        :returns: the commands necessary to merge the provided into +                  the current configuration +        """ +        commands = [] +        for w in want: +            r_sets = self._get_r_sets(w) +            for rs in r_sets: +                h = self.search_r_sets_in_have(have, rs["name"], "r_list") +                commands.extend(self._add_r_sets(w["afi"], rs, h)) +        return commands + +    def _state_deleted(self, want, have): +        """ The command generator when state is deleted + +        :rtype: A list +        :returns: the commands necessary to remove the current configuration +                  of the provided objects +        """ +        commands = [] +        if want: +            for w in want: +                r_sets = self._get_r_sets(w) +                if r_sets: +                    for rs in r_sets: +                        h = self.search_r_sets_in_have( +                            have, rs["name"], "r_list" +                        ) +                        if h: +                            w_rules = rs.get("rules") or [] +                            h_rules = h.get("rules") or [] +                            if w_rules and h_rules: +                                for rule in w_rules: +                                    if self.search_r_sets_in_have( +                                        h_rules, rule["number"], "rules" +                                    ): +                                        commands.append( +                                            self._add_r_base_attrib( +                                                w["afi"], +                                                rs["name"], +                                                "number", +                                                rule, +                                                opr=False, +                                            ) +                                        ) +                            else: +                                commands.append( +                                    self._compute_command( +                                        w["afi"], h["name"], remove=True +                                    ) +                                ) +                elif have: +                    for h in have: +                        if h["afi"] == w["afi"]: +                            commands.append( +                                self._compute_command(w["afi"], remove=True) +                            ) +        elif have: +            for h in have: +                r_sets = self._get_r_sets(h) +                if r_sets: +                    commands.append( +                        self._compute_command(afi=h["afi"], remove=True) +                    ) +        return commands + +    def _add_r_sets(self, afi, want, have, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for rule-sets attributes. +        :param afi: address type. +        :param want: desired config. +        :param have: target config. +        :param opr: True/False. +        :return: generated commands list. +        """ +        commands = [] +        l_set = ("description", "default_action", "enable_default_log") +        h_rs = {} +        h_rules = {} +        w_rs = deepcopy(remove_empties(want)) +        w_rules = w_rs.pop("rules", None) +        if have: +            h_rs = deepcopy(remove_empties(have)) +            h_rules = h_rs.pop("rules", None) +        if w_rs: +            for key, val in iteritems(w_rs): +                if ( +                    opr +                    and key in l_set +                    and not (h_rs and self._is_w_same(w_rs, h_rs, key)) +                ): +                    if key == "enable_default_log": +                        if val and ( +                            not h_rs or key not in h_rs or not h_rs[key] +                        ): +                            commands.append( +                                self._add_rs_base_attrib( +                                    afi, want["name"], key, w_rs +                                ) +                            ) +                    else: +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs +                            ) +                        ) +                elif not opr and key in l_set: +                    if ( +                        key == "enable_default_log" +                        and val +                        and h_rs +                        and (key not in h_rs or not h_rs[key]) +                    ): +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs, opr +                            ) +                        ) +                    elif not (h_rs and self._in_target(h_rs, key)): +                        commands.append( +                            self._add_rs_base_attrib( +                                afi, want["name"], key, w_rs, opr +                            ) +                        ) +            commands.extend( +                self._add_rules(afi, want["name"], w_rules, h_rules, opr) +            ) +        if h_rules: +            have["rules"] = h_rules +        if w_rules: +            want["rules"] = w_rules +        return commands + +    def _add_rules(self, afi, name, w_rules, h_rules, opr=True): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for rules attributes. +        :param want: desired config. +        :param have: target config. +        :return: generated commands list. +        """ +        commands = [] +        l_set = ( +            "ipsec", +            "action", +            "number", +            "protocol", +            "fragment", +            "disabled", +            "description", +        ) +        if w_rules: +            for w in w_rules: +                cmd = self._compute_command(afi, name, w["number"], opr=opr) +                h = self.search_r_sets_in_have( +                    h_rules, w["number"], type="rules" +                ) +                for key, val in iteritems(w): +                    if val: +                        if ( +                            opr +                            and key in l_set +                            and not (h and self._is_w_same(w, h, key)) +                        ): +                            if key == "disabled": +                                if not ( +                                    not val +                                    and (not h or key not in h or not h[key]) +                                ): +                                    commands.append( +                                        self._add_r_base_attrib( +                                            afi, name, key, w +                                        ) +                                    ) +                            else: +                                commands.append( +                                    self._add_r_base_attrib(afi, name, key, w) +                                ) +                        elif not opr: +                            if key == "number" and self._is_del(l_set, h): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                                continue +                            elif ( +                                key == "disabled" +                                and val +                                and h +                                and (key not in h or not h[key]) +                            ): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                            elif ( +                                key in l_set +                                and not (h and self._in_target(h, key)) +                                and not self._is_del(l_set, h) +                            ): +                                commands.append( +                                    self._add_r_base_attrib( +                                        afi, name, key, w, opr=opr +                                    ) +                                ) +                        elif key == "p2p": +                            commands.extend(self._add_p2p(key, w, h, cmd, opr)) +                        elif key == "tcp": +                            commands.extend(self._add_tcp(key, w, h, cmd, opr)) +                        elif key == "time": +                            commands.extend( +                                self._add_time(key, w, h, cmd, opr) +                            ) +                        elif key == "icmp": +                            commands.extend( +                                self._add_icmp(key, w, h, cmd, opr) +                            ) +                        elif key == "state": +                            commands.extend( +                                self._add_state(key, w, h, cmd, opr) +                            ) +                        elif key == "limit": +                            commands.extend( +                                self._add_limit(key, w, h, cmd, opr) +                            ) +                        elif key == "recent": +                            commands.extend( +                                self._add_recent(key, w, h, cmd, opr) +                            ) +                        elif key == "destination" or key == "source": +                            commands.extend( +                                self._add_src_or_dest(key, w, h, cmd, opr) +                            ) +        return commands + +    def _add_p2p(self, attr, w, h, cmd, opr): +        """ +        This function forms the set/delete commands based on the 'opr' type +        for p2p applications attributes. +        :param want: desired config. +        :param have: target config. +        :return: generated commands list. +        """ +        commands = [] +        have = [] +        if w: +            want = w.get(attr) or [] +        if h: +            have = h.get(attr) or [] +        if want: +            if opr: +                applications = list_diff_want_only(want, have) +                for app in applications: +                    commands.append( +                        cmd + (" " + attr + " " + app["application"]) +                    ) +            elif not opr and have: +                applications = list_diff_want_only(want, have) +                for app in applications: +                    commands.append( +                        cmd + (" " + attr + " " + app["application"]) +                    ) +        return commands + +    def _add_state(self, attr, w, h, cmd, opr): +        """ +        This function forms the command for 'state' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_state = {} +        commands = [] +        l_set = ("new", "invalid", "related", "established") +        if w[attr]: +            if h and attr in h.keys(): +                h_state = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not ( +                        h_state and self._is_w_same(w[attr], h_state, item) +                    ) +                ): +                    commands.append( +                        cmd +                        + ( +                            " " +                            + attr +                            + " " +                            + item +                            + " " +                            + self._bool_to_str(val) +                        ) +                    ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_state and self._in_target(h_state, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_recent(self, attr, w, h, cmd, opr): +        """ +        This function forms the command for 'recent' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_recent = {} +        l_set = ("count", "time") +        if w[attr]: +            if h and attr in h.keys(): +                h_recent = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not ( +                        h_recent and self._is_w_same(w[attr], h_recent, item) +                    ) +                ): +                    commands.append( +                        cmd + (" " + attr + " " + item + " " + str(val)) +                    ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_recent and self._in_target(h_recent, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_icmp(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'icmp' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_icmp = {} +        l_set = ("code", "type", "type_name") +        if w[attr]: +            if h and attr in h.keys(): +                h_icmp = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)) +                ): +                    if item == "type_name": +                        if "ipv6-name" in cmd: +                            commands.append( +                                cmd +                                + (" " + "icmpv6" + " " + "type" + " " + val) +                            ) +                        else: +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + item.replace("_", "-") +                                    + " " +                                    + val +                                ) +                            ) +                    else: +                        commands.append( +                            cmd + (" " + attr + " " + item + " " + str(val)) +                        ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_icmp and self._in_target(h_icmp, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_time(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'time' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_time = {} +        l_set = ( +            "utc", +            "stopdate", +            "stoptime", +            "weekdays", +            "monthdays", +            "startdate", +            "starttime", +        ) +        if w[attr]: +            if h and attr in h.keys(): +                h_time = h.get(attr) or {} +            for item, val in iteritems(w[attr]): +                if ( +                    opr +                    and item in l_set +                    and not (h_time and self._is_w_same(w[attr], h_time, item)) +                ): +                    if item == "utc": +                        if not ( +                            not val and (not h_time or item not in h_time) +                        ): +                            commands.append(cmd + (" " + attr + " " + item)) +                    else: +                        commands.append( +                            cmd + (" " + attr + " " + item + " " + val) +                        ) +                elif ( +                    not opr +                    and item in l_set +                    and not (h_time and self._is_w_same(w[attr], h_time, item)) +                ): +                    commands.append(cmd + (" " + attr + " " + item)) +        return commands + +    def _add_tcp(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'tcp' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_tcp = {} +        commands = [] +        if w[attr]: +            key = "flags" +            flags = w[attr].get(key) or {} +            if flags: +                if h and key in h[attr].keys(): +                    h_tcp = h[attr].get(key) or {} +                if flags: +                    if opr and not ( +                        h_tcp and self._is_w_same(w[attr], h[attr], key) +                    ): +                        commands.append( +                            cmd + (" " + attr + " " + key + " " + flags) +                        ) +                    if not opr and not ( +                        h_tcp and self._is_w_same(w[attr], h[attr], key) +                    ): +                        commands.append( +                            cmd + (" " + attr + " " + key + " " + flags) +                        ) +        return commands + +    def _add_limit(self, attr, w, h, cmd, opr): +        """ +        This function forms the commands for 'limit' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        h_limit = {} +        commands = [] +        if w[attr]: +            key = "burst" +            if ( +                opr +                and key in w[attr].keys() +                and not ( +                    h +                    and attr in h.keys() +                    and self._is_w_same(w[attr], h[attr], key) +                ) +            ): +                commands.append( +                    cmd +                    + (" " + attr + " " + key + " " + str(w[attr].get(key))) +                ) +            elif ( +                not opr +                and key in w[attr].keys() +                and not ( +                    h and attr in h.keys() and self._in_target(h[attr], key) +                ) +            ): +                commands.append( +                    cmd +                    + (" " + attr + " " + key + " " + str(w[attr].get(key))) +                ) +            key = "rate" +            rate = w[attr].get(key) or {} +            if rate: +                if h and key in h[attr].keys(): +                    h_limit = h[attr].get(key) or {} +                if "unit" in rate and "number" in rate: +                    if opr and not ( +                        h_limit +                        and self._is_w_same(rate, h_limit, "unit") +                        and self.is_w_same(rate, h_limit, "number") +                    ): +                        commands.append( +                            cmd +                            + ( +                                " " +                                + attr +                                + " " +                                + key +                                + " " +                                + str(rate["number"]) +                                + "/" +                                + rate["unit"] +                            ) +                        ) +                    if not opr and not ( +                        h_limit +                        and self._is_w_same(rate, h_limit, "unit") +                        and self._is_w_same(rate, h_limit, "number") +                    ): +                        commands.append(cmd + (" " + attr + " " + key)) +        return commands + +    def _add_src_or_dest(self, attr, w, h, cmd, opr=True): +        """ +        This function forms the commands for 'src/dest' attributes based on the 'opr'. +        :param attr: attribute name. +        :param w: base config. +        :param h: target config. +        :param cmd: commands to be prepend. +        :return: generated list of commands. +        """ +        commands = [] +        h_group = {} +        g_set = ("port_group", "address_group", "network_group") +        if w[attr]: +            keys = ("address", "mac_address", "port") +            for key in keys: +                if ( +                    opr +                    and key in w[attr].keys() +                    and not ( +                        h +                        and attr in h.keys() +                        and self._is_w_same(w[attr], h[attr], key) +                    ) +                ): +                    commands.append( +                        cmd +                        + ( +                            " " +                            + attr +                            + " " +                            + key.replace("_", "-") +                            + " " +                            + w[attr].get(key) +                        ) +                    ) +                elif ( +                    not opr +                    and key in w[attr].keys() +                    and not ( +                        h +                        and attr in h.keys() +                        and self._in_target(h[attr], key) +                    ) +                ): +                    commands.append(cmd + (" " + attr + " " + key)) + +            key = "group" +            group = w[attr].get(key) or {} +            if group: +                if h and key in h[attr].keys(): +                    h_group = h[attr].get(key) or {} +                for item, val in iteritems(group): +                    if val: +                        if ( +                            opr +                            and item in g_set +                            and not ( +                                h_group +                                and self._is_w_same(group, h_group, item) +                            ) +                        ): +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + key +                                    + " " +                                    + item.replace("_", "-") +                                    + " " +                                    + val +                                ) +                            ) +                        elif ( +                            not opr +                            and item in g_set +                            and not ( +                                h_group and self._in_target(h_group, item) +                            ) +                        ): +                            commands.append( +                                cmd +                                + ( +                                    " " +                                    + attr +                                    + " " +                                    + key +                                    + " " +                                    + item.replace("_", "-") +                                ) +                            ) +        return commands + +    def search_r_sets_in_have(self, have, w_name, type="rule_sets"): +        """ +        This function  returns the rule-set/rule if it is present in target config. +        :param have: target config. +        :param w_name: rule-set name. +        :param type: rule_sets/rule/r_list. +        :return: rule-set/rule. +        """ +        if have: +            key = "name" +            if type == "rules": +                key = "number" +                for r in have: +                    if r[key] == w_name: +                        return r +            elif type == "r_list": +                for h in have: +                    r_sets = self._get_r_sets(h) +                    for rs in r_sets: +                        if rs[key] == w_name: +                            return rs +            else: +                for rs in have: +                    if rs[key] == w_name: +                        return rs +        return None + +    def _get_r_sets(self, item, type="rule_sets"): +        """ +        This function returns the list of rule-sets/rules. +        :param item: config dictionary. +        :param type: rule_sets/rule/r_list. +        :return: list of rule-sets/rules. +        """ +        rs_list = [] +        r_sets = item[type] +        if r_sets: +            for rs in r_sets: +                rs_list.append(rs) +        return rs_list + +    def _compute_command( +        self, +        afi, +        name=None, +        number=None, +        attrib=None, +        value=None, +        remove=False, +        opr=True, +    ): +        """ +        This function construct the add/delete command based on passed attributes. +        :param afi:  address type. +        :param name:  rule-set name. +        :param number: rule-number. +        :param attrib: attribute name. +        :param value: value. +        :param remove: True if delete command needed to be construct. +        :param opr: opeeration flag. +        :return: generated command. +        """ +        if remove or not opr: +            cmd = "delete firewall " + self._get_fw_type(afi) +        else: +            cmd = "set firewall " + self._get_fw_type(afi) +        if name: +            cmd += " " + name +        if number: +            cmd += " rule " + str(number) +        if attrib: +            cmd += " " + attrib.replace("_", "-") +        if ( +            value +            and opr +            and attrib != "enable_default_log" +            and attrib != "disabled" +        ): +            cmd += " '" + str(value) + "'" +        return cmd + +    def _add_r_base_attrib(self, afi, name, attr, rule, opr=True): +        """ +        This function forms the command for 'rules' attributes which doesn't +        have further sub attributes. +        :param afi: address type. +        :param name: rule-set name +        :param attrib: attribute name +        :param rule: rule config dictionary. +        :param opr: True/False. +        :return: generated command. +        """ +        if attr == "number": +            command = self._compute_command( +                afi=afi, name=name, number=rule["number"], opr=opr +            ) +        else: +            command = self._compute_command( +                afi=afi, +                name=name, +                number=rule["number"], +                attrib=attr, +                value=rule[attr], +                opr=opr, +            ) +        return command + +    def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True): +        """ + +        This function forms the command for 'rule-sets' attributes which doesn't +        have further sub attributes. +        :param afi: address type. +        :param name: rule-set name +        :param attrib: attribute name +        :param rule: rule config dictionary. +        :param opr: True/False. +        :return: generated command. +        """ +        command = self._compute_command( +            afi=afi, name=name, attrib=attrib, value=rule[attrib], opr=opr +        ) +        return command + +    def _bool_to_str(self, val): +        """ +        This function converts the bool value into string. +        :param val: bool value. +        :return: enable/disable. +        """ +        return "enable" if val else "disable" + +    def _get_fw_type(self, afi): +        """ +        This function returns the firewall rule-set type based on IP address. +        :param afi: address type +        :return: rule-set type. +        """ +        return "ipv6-name" if afi == "ipv6" else "name" + +    def _is_del(self, l_set, h, key="number"): +        """ +        This function checks whether rule needs to be deleted based on +        the rule number. +        :param l_set: attribute set. +        :param h: target config. +        :param key: number. +        :return: True/False. +        """ +        return key in l_set and not (h and self._in_target(h, key)) + +    def _is_w_same(self, w, h, key): +        """ +        This function checks whether the key value is same in base and +        target config dictionary. +        :param w: base config. +        :param h: target config. +        :param key:attribute name. +        :return: True/False. +        """ +        return True if h and key in h and h[key] == w[key] else False + +    def _in_target(self, h, key): +        """ +        This function checks whether the target nexist and key present in target config. +        :param h: target config. +        :param key: attribute name. +        :return: True/False. +        """ +        return True if h and key in h else False + +    def _is_base_attrib(self, key): +        """ +        This function checks whether key is present in predefined +        based attribute set. +        :param key: +        :return: True/False. +        """ +        r_set = ( +            "p2p", +            "ipsec", +            "action", +            "fragment", +            "protocol", +            "disabled", +            "description", +            "mac_address", +            "default_action", +            "enable_default_log", +        ) +        return True if key in r_set else False diff --git a/plugins/module_utils/network/vyos/config/static_routes/__init__.py b/plugins/module_utils/network/vyos/config/static_routes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py new file mode 100644 index 0000000..e93d4ee --- /dev/null +++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py @@ -0,0 +1,627 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_static_routes class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( +    ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( +    to_list, +    dict_diff, +    remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( +    Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    get_route_type, +    get_lst_diff_for_dicts, +    get_lst_same_for_dicts, +    dict_delete, +) + + +class Static_routes(ConfigBase): +    """ +    The vyos_static_routes class +    """ + +    gather_subset = [ +        "!all", +        "!min", +    ] + +    gather_network_resources = [ +        "static_routes", +    ] + +    def __init__(self, module): +        super(Static_routes, self).__init__(module) + +    def get_static_routes_facts(self, data=None): +        """ Get the 'facts' (the current configuration) + +        :rtype: A dictionary +        :returns: The current configuration as a dictionary +        """ +        facts, _warnings = Facts(self._module).get_facts( +            self.gather_subset, self.gather_network_resources, data=data +        ) +        static_routes_facts = facts["ansible_network_resources"].get( +            "static_routes" +        ) +        if not static_routes_facts: +            return [] +        return static_routes_facts + +    def execute_module(self): +        """ Execute the module + +        :rtype: A dictionary +        :returns: The result from module execution +        """ +        result = {"changed": False} +        warnings = list() +        commands = list() + +        if self.state in self.ACTION_STATES: +            existing_static_routes_facts = self.get_static_routes_facts() +        else: +            existing_static_routes_facts = [] + +        if self.state in self.ACTION_STATES or self.state == "rendered": +            commands.extend(self.set_config(existing_static_routes_facts)) + +        if commands and self.state in self.ACTION_STATES: +            if not self._module.check_mode: +                self._connection.edit_config(commands) +            result["changed"] = True + +        if self.state in self.ACTION_STATES: +            result["commands"] = commands + +        if self.state in self.ACTION_STATES or self.state == "gathered": +            changed_static_routes_facts = self.get_static_routes_facts() +        elif self.state == "rendered": +            result["rendered"] = commands +        elif self.state == "parsed": +            running_config = self._module.params["running_config"] +            if not running_config: +                self._module.fail_json( +                    msg="value of running_config parameter must not be empty for state parsed" +                ) +            result["parsed"] = self.get_static_routes_facts( +                data=running_config +            ) +        else: +            changed_static_routes_facts = [] + +        if self.state in self.ACTION_STATES: +            result["before"] = existing_static_routes_facts +            if result["changed"]: +                result["after"] = changed_static_routes_facts +        elif self.state == "gathered": +            result["gathered"] = changed_static_routes_facts + +        result["warnings"] = warnings +        return result + +    def set_config(self, existing_static_routes_facts): +        """ Collect the configuration from the args passed to the module, +            collect the current configuration (as a dict from facts) + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        want = self._module.params["config"] +        have = existing_static_routes_facts +        resp = self.set_state(want, have) +        return to_list(resp) + +    def set_state(self, want, have): +        """ Select the appropriate function based on the state provided + +        :param want: the desired configuration as a dictionary +        :param have: the current configuration as a dictionary +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if ( +            self.state in ("merged", "replaced", "overridden", "rendered") +            and not want +        ): +            self._module.fail_json( +                msg="value of config parameter must not be empty for state {0}".format( +                    self.state +                ) +            ) +        if self.state == "overridden": +            commands.extend(self._state_overridden(want=want, have=have)) +        elif self.state == "deleted": +            commands.extend(self._state_deleted(want=want, have=have)) +        elif want: +            routes = self._get_routes(want) +            for r in routes: +                h_item = self.search_route_in_have(have, r["dest"]) +                if self.state == "merged" or self.state == "rendered": +                    commands.extend(self._state_merged(want=r, have=h_item)) +                elif self.state == "replaced": +                    commands.extend(self._state_replaced(want=r, have=h_item)) +        return commands + +    def search_route_in_have(self, have, want_dest): +        """ +        This function  returns the route if its found in +        have config. +        :param have: +        :param dest: +        :return: the matched route +        """ +        routes = self._get_routes(have) +        for r in routes: +            if r["dest"] == want_dest: +                return r +        return None + +    def _state_replaced(self, want, have): +        """ The command generator when state is replaced + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        if have: +            for key, value in iteritems(want): +                if value: +                    if key == "next_hops": +                        commands.extend(self._update_next_hop(want, have)) +                    elif key == "blackhole_config": +                        commands.extend( +                            self._update_blackhole(key, want, have) +                        ) +        commands.extend(self._state_merged(want, have)) +        return commands + +    def _state_overridden(self, want, have): +        """ The command generator when state is overridden + +        :rtype: A list +        :returns: the commands necessary to migrate the current configuration +                  to the desired configuration +        """ +        commands = [] +        routes = self._get_routes(have) +        for r in routes: +            route_in_want = self.search_route_in_have(want, r["dest"]) +            if not route_in_want: +                commands.append(self._compute_command(r["dest"], remove=True)) +        routes = self._get_routes(want) +        for r in routes: +            route_in_have = self.search_route_in_have(have, r["dest"]) +            commands.extend(self._state_replaced(r, route_in_have)) +        return commands + +    def _state_merged(self, want, have, opr=True): +        """ The command generator when state is merged + +        :rtype: A list +        :returns: the commands necessary to merge the provided into +                  the current configuration +        """ +        commands = [] +        if have: +            commands.extend(self._render_updates(want, have)) +        else: +            commands.extend(self._render_set_commands(want)) +        return commands + +    def _state_deleted(self, want, have): +        """ The command generator when state is deleted + +        :rtype: A list +        :returns: the commands necessary to remove the current configuration +                  of the provided objects +        """ +        commands = [] +        if want: +            routes = self._get_routes(want) +            if not routes: +                for w in want: +                    af = w["address_families"] +                    for item in af: +                        if self.afi_in_have(have, item): +                            commands.append( +                                self._compute_command( +                                    afi=item["afi"], remove=True +                                ) +                            ) +            for r in routes: +                h_route = self.search_route_in_have(have, r["dest"]) +                if h_route: +                    commands.extend( +                        self._render_updates(r, h_route, opr=False) +                    ) +        else: +            routes = self._get_routes(have) +            if self._is_ip_route_exist(routes): +                commands.append(self._compute_command(afi="ipv4", remove=True)) +            if self._is_ip_route_exist(routes, "route6"): +                commands.append(self._compute_command(afi="ipv6", remove=True)) +        return commands + +    def _render_set_commands(self, want): +        """ +        This function returns the list of commands to add attributes which are +        present in want +        :param want: +        :return: list of commands. +        """ +        commands = [] +        have = {} +        for key, value in iteritems(want): +            if value: +                if key == "dest": +                    commands.append(self._compute_command(dest=want["dest"])) +                elif key == "blackhole_config": +                    commands.extend(self._add_blackhole(key, want, have)) + +                elif key == "next_hops": +                    commands.extend(self._add_next_hop(want, have)) + +        return commands + +    def _add_blackhole(self, key, want, have): +        """ +        This function gets the diff for blackhole config specific attributes +        and form the commands for attributes which are present in want but not in have. +        :param key: +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        want_blackhole = want_copy.get(key) or {} +        have_blackhole = have_copy.get(key) or {} + +        updates = dict_delete(want_blackhole, have_blackhole) +        if updates: +            for attrib, value in iteritems(updates): +                if value: +                    if attrib == "distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="blackhole", +                                attrib=attrib, +                                remove=False, +                                value=str(value), +                            ) +                        ) +                    elif attrib == "type": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], key="blackhole" +                            ) +                        ) +        return commands + +    def _add_next_hop(self, want, have, opr=True): +        """ +        This function gets the diff for next hop specific attributes +        and form the commands to add attributes which are present in want but not in have. +        :param want: +        :param have: +        :return: list of commands. +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) +        if not opr: +            diff_next_hops = get_lst_same_for_dicts( +                want_copy, have_copy, "next_hops" +            ) +        else: +            diff_next_hops = get_lst_diff_for_dicts( +                want_copy, have_copy, "next_hops" +            ) +        if diff_next_hops: +            for hop in diff_next_hops: +                for element in hop: +                    if element == "forward_router_address": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                value=hop[element], +                                opr=opr, +                            ) +                        ) +                    elif element == "enabled" and not hop[element]: +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"], +                                value="disable", +                                opr=opr, +                            ) +                        ) +                    elif element == "admin_distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=str(hop[element]), +                                opr=opr, +                            ) +                        ) +                    elif element == "interface": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=hop[element], +                                opr=opr, +                            ) +                        ) +        return commands + +    def _update_blackhole(self, key, want, have): +        """ +        This function gets the difference for blackhole dict and +        form the commands to delete the attributes which are present in have but not in want. +        :param want: +        :param have: +        :return: list of commands +        :param key: +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        want_blackhole = want_copy.get(key) or {} +        have_blackhole = have_copy.get(key) or {} +        updates = dict_delete(have_blackhole, want_blackhole) +        if updates: +            for attrib, value in iteritems(updates): +                if value: +                    if attrib == "distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="blackhole", +                                attrib=attrib, +                                remove=True, +                                value=str(value), +                            ) +                        ) +                    elif ( +                        attrib == "type" +                        and "distance" not in want_blackhole.keys() +                    ): +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], key="blackhole", remove=True +                            ) +                        ) +        return commands + +    def _update_next_hop(self, want, have, opr=True): +        """ +        This function gets the difference for next_hops list and +        form the commands to delete the attributes which are present in have but not in want. +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] + +        want_copy = deepcopy(remove_empties(want)) +        have_copy = deepcopy(remove_empties(have)) + +        diff_next_hops = get_lst_diff_for_dicts( +            have_copy, want_copy, "next_hops" +        ) +        if diff_next_hops: +            for hop in diff_next_hops: +                for element in hop: +                    if element == "forward_router_address": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                value=hop[element], +                                remove=True, +                            ) +                        ) +                    elif element == "enabled": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"], +                                value="disable", +                                remove=True, +                            ) +                        ) +                    elif element == "admin_distance": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=str(hop[element]), +                                remove=True, +                            ) +                        ) +                    elif element == "interface": +                        commands.append( +                            self._compute_command( +                                dest=want["dest"], +                                key="next-hop", +                                attrib=hop["forward_router_address"] +                                + " " +                                + element, +                                value=hop[element], +                                remove=True, +                            ) +                        ) +        return commands + +    def _render_updates(self, want, have, opr=True): +        """ +        This function takes the diff between want and have and +        invokes the appropriate functions to create the commands +        to update the attributes. +        :param want: +        :param have: +        :return: list of commands +        """ +        commands = [] +        want_nh = want.get("next_hops") or [] +        # delete static route operation per destination +        if not opr and not want_nh: +            commands.append( +                self._compute_command(dest=want["dest"], remove=True) +            ) + +        else: +            temp_have_next_hops = have.pop("next_hops", None) +            temp_want_next_hops = want.pop("next_hops", None) +            updates = dict_diff(have, want) +            if temp_have_next_hops: +                have["next_hops"] = temp_have_next_hops +            if temp_want_next_hops: +                want["next_hops"] = temp_want_next_hops +            commands.extend(self._add_next_hop(want, have, opr=opr)) + +            if opr and updates: +                for key, value in iteritems(updates): +                    if value: +                        if key == "blackhole_config": +                            commands.extend( +                                self._add_blackhole(key, want, have) +                            ) +        return commands + +    def _compute_command( +        self, +        dest=None, +        key=None, +        attrib=None, +        value=None, +        remove=False, +        afi=None, +        opr=True, +    ): +        """ +        This functions construct the required command based on the passed arguments. +        :param dest: +        :param key: +        :param attrib: +        :param value: +        :param remove: +        :return:  constructed command +        """ +        if remove or not opr: +            cmd = "delete protocols static " + self.get_route_type(dest, afi) +        else: +            cmd = "set protocols static " + self.get_route_type(dest, afi) +        if dest: +            cmd += " " + dest +        if key: +            cmd += " " + key +        if attrib: +            cmd += " " + attrib +        if value: +            cmd += " '" + value + "'" +        return cmd + +    def afi_in_have(self, have, w_item): +        """ +        This functions checks for the afi +        list in have +        :param have: +        :param w_item: +        :return: +        """ +        if have: +            for h in have: +                af = h.get("address_families") or [] +            for item in af: +                if w_item["afi"] == item["afi"]: +                    return True +        return False + +    def get_route_type(self, dest=None, afi=None): +        """ +        This function returns the route type based on +        destination ip address or afi +        :param address: +        :return: +        """ +        if dest: +            return get_route_type(dest) +        elif afi == "ipv4": +            return "route" +        elif afi == "ipv6": +            return "route6" + +    def _is_ip_route_exist(self, routes, type="route"): +        """ +        This functions checks for the type of route. +        :param routes: +        :param type: +        :return: True/False +        """ +        for r in routes: +            if type == self.get_route_type(r["dest"]): +                return True +        return False + +    def _get_routes(self, lst): +        """ +        This function returns the list of routes +        :param lst: list of address families +        :return: list of routes +        """ +        r_list = [] +        for item in lst: +            af = item["address_families"] +            for element in af: +                routes = element.get("routes") or [] +                for r in routes: +                    r_list.append(r) +        return r_list diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index b5816c2..8f0a3bb 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -27,6 +27,12 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import (      Lldp_interfacesFacts,  ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( +    Firewall_rulesFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( +    Static_routesFacts, +)  from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (      Default,      Neighbors, @@ -41,6 +47,8 @@ FACT_RESOURCE_SUBSETS = dict(      lag_interfaces=Lag_interfacesFacts,      lldp_global=Lldp_globalFacts,      lldp_interfaces=Lldp_interfacesFacts, +    static_routes=Static_routesFacts, +    firewall_rules=Firewall_rulesFacts,  ) @@ -72,5 +80,4 @@ class Facts(FactsBase):              self.get_network_legacy_facts(                  FACT_LEGACY_SUBSETS, legacy_facts_type              ) -          return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py new file mode 100644 index 0000000..971ea6f --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -0,0 +1,380 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos firewall_rules fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( +    utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( +    Firewall_rulesArgs, +) + + +class Firewall_rulesFacts(object): +    """ The vyos firewall_rules fact class +    """ + +    def __init__(self, module, subspec="config", options="options"): +        self._module = module +        self.argument_spec = Firewall_rulesArgs.argument_spec +        spec = deepcopy(self.argument_spec) +        if subspec: +            if options: +                facts_argument_spec = spec[subspec][options] +            else: +                facts_argument_spec = spec[subspec] +        else: +            facts_argument_spec = spec + +        self.generated_spec = utils.generate_dict(facts_argument_spec) + +    def get_device_data(self, connection): +        return connection.get_config() + +    def populate_facts(self, connection, ansible_facts, data=None): +        """ Populate the facts for firewall_rules +        :param connection: the device connection +        :param ansible_facts: Facts dictionary +        :param data: previously collected conf +        :rtype: dictionary +        :returns: facts +        """ +        if not data: +            # typically data is populated from the current device configuration +            # data = connection.get('show running-config | section ^interface') +            # using mock data instead +            data = self.get_device_data(connection) +        # split the config into instances of the resource +        objs = [] +        v6_rules = findall( +            r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M +        ) +        v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) +        if v6_rules: +            config = self.get_rules(data, v6_rules, type="ipv6") +            if config: +                config = utils.remove_empties(config) +                objs.append(config) +        if v4_rules: +            config = self.get_rules(data, v4_rules, type="ipv4") +            if config: +                config = utils.remove_empties(config) +                objs.append(config) + +        ansible_facts["ansible_network_resources"].pop("firewall_rules", None) +        facts = {} +        if objs: +            facts["firewall_rules"] = [] +            params = utils.validate_config( +                self.argument_spec, {"config": objs} +            ) +            for cfg in params["config"]: +                facts["firewall_rules"].append(utils.remove_empties(cfg)) + +        ansible_facts["ansible_network_resources"].update(facts) +        return ansible_facts + +    def get_rules(self, data, rules, type): +        """ +        This function performs following: +        - Form regex to fetch 'rule-sets' specific config from data. +        - Form the rule-set list based on ip address. +        :param data: configuration. +        :param rules: list of rule-sets. +        :param type: ip address type. +        :return: generated rule-sets configuration. +        """ +        r_v4 = [] +        r_v6 = [] +        for r in set(rules): +            rule_regex = r" %s .+$" % r.strip("'") +            cfg = findall(rule_regex, data, M) +            fr = self.render_config(cfg, r.strip("'")) +            fr["name"] = r.strip("'") +            if type == "ipv6": +                r_v6.append(fr) +            else: +                r_v4.append(fr) +        if r_v4: +            config = {"afi": "ipv4", "rule_sets": r_v4} +        if r_v6: +            config = {"afi": "ipv6", "rule_sets": r_v6} +        return config + +    def render_config(self, conf, match): +        """ +        Render config as dictionary structure and delete keys +          from spec for null values + +        :param spec: The facts tree, generated from the argspec +        :param conf: The configuration +        :rtype: dictionary +        :returns: The generated config +        """ +        conf = "\n".join(filter(lambda x: x, conf)) +        a_lst = ["description", "default_action", "enable_default_log"] +        config = self.parse_attr(conf, a_lst, match) +        if not config: +            config = {} +        config["rules"] = self.parse_rules_lst(conf) +        return config + +    def parse_rules_lst(self, conf): +        """ +        This function forms the regex to fetch the 'rules' with in +        'rule-sets' +        :param conf: configuration data. +        :return: generated rule list configuration. +        """ +        r_lst = [] +        rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) +        if rules: +            rules_lst = [] +            for r in set(rules): +                r_regex = r" %s .+$" % r +                cfg = "\n".join(findall(r_regex, conf, M)) +                obj = self.parse_rules(cfg) +                obj["number"] = int(r) +                if obj: +                    rules_lst.append(obj) +            r_lst = sorted(rules_lst, key=lambda i: i["number"]) +        return r_lst + +    def parse_rules(self, conf): +        """ +        This function triggers the parsing of 'rule' attributes. +        a_lst is a list having rule attributes which doesn't +        have further sub attributes. +        :param conf: configuration +        :return: generated rule configuration dictionary. +        """ +        a_lst = [ +            "ipsec", +            "action", +            "protocol", +            "fragment", +            "disabled", +            "description", +        ] +        rule = self.parse_attr(conf, a_lst) +        r_sub = { +            "p2p": self.parse_p2p(conf), +            "tcp": self.parse_tcp(conf, "tcp"), +            "icmp": self.parse_icmp(conf, "icmp"), +            "time": self.parse_time(conf, "time"), +            "limit": self.parse_limit(conf, "limit"), +            "state": self.parse_state(conf, "state"), +            "recent": self.parse_recent(conf, "recent"), +            "source": self.parse_src_or_dest(conf, "source"), +            "destination": self.parse_src_or_dest(conf, "destination"), +        } +        rule.update(r_sub) +        return rule + +    def parse_p2p(self, conf): +        """ +        This function forms the regex to fetch the 'p2p' with in +        'rules' +        :param conf: configuration data. +        :return: generated rule list configuration. +        """ +        a_lst = [] +        applications = findall(r"p2p (?:\'*)(\d+)(?:\'*)", conf, M) +        if applications: +            app_lst = [] +            for r in set(applications): +                obj = {"application": r.strip("'")} +                app_lst.append(obj) +            a_lst = sorted(app_lst, key=lambda i: i["application"]) +        return a_lst + +    def parse_src_or_dest(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'source or +        destination' attributes. +        :param conf: configuration. +        :param attrib:'source/destination'. +        :return:generated source/destination configuration dictionary. +        """ +        a_lst = ["port", "address", "mac_address"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        cfg_dict["group"] = self.parse_group(conf, attrib + " group") +        return cfg_dict + +    def parse_recent(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'recent' attributes +        :param conf: configuration. +        :param attrib: 'recent'. +        :return: generated config dictionary. +        """ +        a_lst = ["time", "count"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_tcp(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'tcp' attributes. +        :param conf: configuration. +        :param attrib: 'tcp'. +        :return: generated config dictionary. +        """ +        cfg_dict = self.parse_attr(conf, ["flags"], match=attrib) +        return cfg_dict + +    def parse_time(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'time' attributes. +        :param conf: configuration. +        :param attrib: 'time'. +        :return: generated config dictionary. +        """ +        a_lst = [ +            "stopdate", +            "stoptime", +            "weekdays", +            "monthdays", +            "startdate", +            "starttime", +        ] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_state(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'state' attributes. +        :param conf: configuration +        :param attrib: 'state'. +        :return: generated config dictionary. +        """ +        a_lst = ["new", "invalid", "related", "established"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_group(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'group' attributes. +        :param conf: configuration. +        :param attrib: 'group'. +        :return: generated config dictionary. +        """ +        a_lst = ["port_group", "address_group", "network_group"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_icmp(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'icmp' attributes. +        :param conf: configuration to be parsed. +        :param attrib: 'icmp'. +        :return: generated config dictionary. +        """ +        a_lst = ["code", "type", "type_name"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_limit(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'limit' attributes. +        :param conf: configuration to be parsed. +        :param attrib: 'limit' +        :return: generated config dictionary. +        """ +        cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) +        cfg_dict["rate"] = self.parse_rate(conf, "rate") +        return cfg_dict + +    def parse_rate(self, conf, attrib=None): +        """ +        This function triggers the parsing of 'rate' attributes. +        :param conf: configuration. +        :param attrib: 'rate' +        :return: generated config dictionary. +        """ +        a_lst = ["unit", "number"] +        cfg_dict = self.parse_attr(conf, a_lst, match=attrib) +        return cfg_dict + +    def parse_attr(self, conf, attr_list, match=None): +        """ +        This function peforms the following: +        - Form the regex to fetch the required attribute config. +        - Type cast the output in desired format. +        :param conf: configuration. +        :param attr_list: list of attributes. +        :param match: parent node/attribute name. +        :return: generated config dictionary. +        """ +        config = {} +        for attrib in attr_list: +            regex = self.map_regex(attrib) +            if match: +                regex = match + " " + regex +            if conf: +                if self.is_bool(attrib): +                    out = conf.find(attrib.replace("_", "-")) + +                    dis = conf.find(attrib.replace("_", "-") + " 'disable'") +                    if out >= 1: +                        if dis >= 1: +                            config[attrib] = False +                        else: +                            config[attrib] = True +                else: +                    out = search(r"^.*" + regex + " (.+)", conf, M) +                    if out: +                        val = out.group(1).strip("'") +                        if self.is_num(attrib): +                            val = int(val) +                        config[attrib] = val +        return config + +    def map_regex(self, attrib): +        """ +        - This function construct the regex string. +        - replace the underscore with hyphen. +        :param attrib: attribute +        :return: regex string +        """ +        regex = attrib.replace("_", "-") +        if attrib == "disabled": +            regex = "disable" +        return regex + +    def is_bool(self, attrib): +        """ +        This function looks for the attribute in predefined bool type set. +        :param attrib: attribute. +        :return: True/False +        """ +        bool_set = ( +            "new", +            "invalid", +            "related", +            "disabled", +            "established", +            "enable_default_log", +        ) +        return True if attrib in bool_set else False + +    def is_num(self, attrib): +        """ +        This function looks for the attribute in predefined integer type set. +        :param attrib: attribute. +        :return: True/false. +        """ +        num_set = ("time", "code", "type", "count", "burst", "number") +        return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/static_routes/__init__.py b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/static_routes/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py new file mode 100644 index 0000000..0004947 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -0,0 +1,181 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos static_routes fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( +    utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( +    Static_routesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( +    get_route_type, +) + + +class Static_routesFacts(object): +    """ The vyos static_routes fact class +    """ + +    def __init__(self, module, subspec="config", options="options"): +        self._module = module +        self.argument_spec = Static_routesArgs.argument_spec +        spec = deepcopy(self.argument_spec) +        if subspec: +            if options: +                facts_argument_spec = spec[subspec][options] +            else: +                facts_argument_spec = spec[subspec] +        else: +            facts_argument_spec = spec + +        self.generated_spec = utils.generate_dict(facts_argument_spec) + +    def get_device_data(self, connection): +        return connection.get_config() + +    def populate_facts(self, connection, ansible_facts, data=None): +        """ Populate the facts for static_routes +        :param connection: the device connection +        :param ansible_facts: Facts dictionary +        :param data: previously collected conf +        :rtype: dictionary +        :returns: facts +        """ +        if not data: +            data = self.get_device_data(connection) +            # typically data is populated from the current device configuration +            # data = connection.get('show running-config | section ^interface') +            # using mock data instead +        objs = [] +        r_v4 = [] +        r_v6 = [] +        af = [] +        static_routes = findall( +            r"set protocols static route(6)? (\S+)", data, M +        ) +        if static_routes: +            for route in set(static_routes): +                route_regex = r" %s .+$" % route[1] +                cfg = findall(route_regex, data, M) +                sr = self.render_config(cfg) +                sr["dest"] = route[1].strip("'") +                afi = self.get_afi(sr["dest"]) +                if afi == "ipv4": +                    r_v4.append(sr) +                else: +                    r_v6.append(sr) +            if r_v4: +                afi_v4 = {"afi": "ipv4", "routes": r_v4} +                af.append(afi_v4) +            if r_v6: +                afi_v6 = {"afi": "ipv6", "routes": r_v6} +                af.append(afi_v6) +            config = {"address_families": af} +            if config: +                objs.append(config) + +        ansible_facts["ansible_network_resources"].pop("static_routes", None) +        facts = {} +        if objs: +            facts["static_routes"] = [] +            params = utils.validate_config( +                self.argument_spec, {"config": objs} +            ) +            for cfg in params["config"]: +                facts["static_routes"].append(utils.remove_empties(cfg)) + +        ansible_facts["ansible_network_resources"].update(facts) +        return ansible_facts + +    def render_config(self, conf): +        """ +        Render config as dictionary structure and delete keys +          from spec for null values + +        :param spec: The facts tree, generated from the argspec +        :param conf: The configuration +        :rtype: dictionary +        :returns: The generated config +        """ +        next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x), conf)) +        blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf)) +        routes_dict = { +            "blackhole_config": self.parse_blackhole(blackhole_conf), +            "next_hops": self.parse_next_hop(next_hops_conf), +        } +        return routes_dict + +    def parse_blackhole(self, conf): +        blackhole = None +        if conf: +            distance = search(r"^.*blackhole distance (.\S+)", conf, M) +            bh = conf.find("blackhole") +            if distance is not None: +                blackhole = {} +                value = distance.group(1).strip("'") +                blackhole["distance"] = int(value) +            elif bh: +                blackhole = {} +                blackhole["type"] = "blackhole" +        return blackhole + +    def get_afi(self, address): +        route_type = get_route_type(address) +        if route_type == "route": +            return "ipv4" +        elif route_type == "route6": +            return "ipv6" + +    def parse_next_hop(self, conf): +        nh_list = None +        if conf: +            nh_list = [] +            hop_list = findall(r"^.*next-hop (.+)", conf, M) +            if hop_list: +                for hop in hop_list: +                    distance = search(r"^.*distance (.\S+)", hop, M) +                    interface = search(r"^.*interface (.\S+)", hop, M) + +                    dis = hop.find("disable") +                    hop_info = hop.split(" ") +                    nh_info = { +                        "forward_router_address": hop_info[0].strip("'") +                    } +                    if interface: +                        nh_info["interface"] = interface.group(1).strip("'") +                    if distance: +                        value = distance.group(1).strip("'") +                        nh_info["admin_distance"] = int(value) +                    elif dis >= 1: +                        nh_info["enabled"] = False +                    for element in nh_list: +                        if ( +                            element["forward_router_address"] +                            == nh_info["forward_router_address"] +                        ): +                            if "interface" in nh_info.keys(): +                                element["interface"] = nh_info["interface"] +                            if "admin_distance" in nh_info.keys(): +                                element["admin_distance"] = nh_info[ +                                    "admin_distance" +                                ] +                            if "enabled" in nh_info.keys(): +                                element["enabled"] = nh_info["enabled"] +                            nh_info = None +                    if nh_info is not None: +                        nh_list.append(nh_info) +        return nh_list diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 6504bcd..402adfc 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -8,6 +8,9 @@ from __future__ import absolute_import, division, print_function  __metaclass__ = type  from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( +    ipaddress, +)  def search_obj_in_list(name, lst, key="name"): @@ -87,6 +90,27 @@ def get_lst_diff_for_dicts(want, have, lst):      return diff +def get_lst_same_for_dicts(want, have, lst): +    """ +    This function generates a list containing values +    that are common for list in want and list in have dict +    :param want: dict object to want +    :param have: dict object to have +    :param lst: list the comparison on +    :return: new list object with values which are common in want and have. +    """ +    diff = None +    if want and have: +        want_list = want.get(lst) or {} +        have_list = have.get(lst) or {} +        diff = [ +            i +            for i in want_list and have_list +            if i in have_list and i in want_list +        ] +    return diff + +  def list_diff_have_only(want_list, have_list):      """      This function generated the list containing values @@ -178,3 +202,30 @@ def is_dict_element_present(dict, key):          if item == key:              return True      return False + + +def get_ip_address_version(address): +    """ +    This function returns the version of IP address +    :param address: IP address +    :return: +    """ +    try: +        address = unicode(address) +    except NameError: +        address = str(address) +    version = ipaddress.ip_address(address.split("/")[0]).version +    return version + + +def get_route_type(address): +    """ +    This function returns the route type based on IP address +    :param address: +    :return: +    """ +    version = get_ip_address_version(address) +    if version == 6: +        return "route6" +    elif version == 4: +        return "route" diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index 9eaa278..19fb727 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -47,7 +47,7 @@ options:        Can specify a list of values to include a larger subset. Values can also be        used with an initial C(M(!)) to specify that a specific subset should not be        collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', -      'lldp_global', 'lldp_interfaces'. +      'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules'.      required: false  """ diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py new file mode 100644 index 0000000..a9e676b --- /dev/null +++ b/plugins/modules/vyos_firewall_rules.py @@ -0,0 +1,1565 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# + +""" +The module file for vyos_firewall_rules +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { +    "metadata_version": "1.1", +    "status": ["preview"], +    "supported_by": "network", +} + +DOCUMENTATION = """module: vyos_firewall_rules +short_description: Manage firewall rule-set attributes on VyOS devices +description: This module manages firewall rule-set attributes on VyOS devices +notes: +- Tested against VyOS 1.1.8 (helium). +- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). +author: +- Rohit Thakur (@rohitthakur2590) +options: +  config: +    description: A dictionary of Firewall rule-set options. +    type: list +    elements: dict +    suboptions: +      afi: +        description: +        - Specifies the type of rule-set. +        type: str +        choices: +        - ipv4 +        - ipv6 +        required: true +      rule_sets: +        description: +        - The Firewall rule-set list. +        type: list +        elements: dict +        suboptions: +          name: +            description: +            - Firewall rule set name. +            type: str +          default_action: +            description: +            - Default action for rule-set. +            - drop (Drop if no prior rules are hit (default)) +            - reject (Drop and notify source if no prior rules are hit) +            - accept (Accept if no prior rules are hit) +            type: str +            choices: +            - drop +            - reject +            - accept +          description: +            description: +            - Rule set description. +            type: str +          enable_default_log: +            description: +            - Option to log packets hitting default-action. +            type: bool +          rules: +            description: +            - A ditionary that specifies the rule-set configurations. +            type: list +            elements: dict +            suboptions: +              number: +                description: +                - Rule number. +                type: int +                required: true +              description: +                description: +                - Description of this rule. +                type: str +              action: +                description: +                - Specifying the action. +                type: str +                choices: +                - drop +                - reject +                - accept +                - inspect +              destination: +                description: +                - Specifying the destination parameters. +                type: dict +                suboptions: +                  address: +                    description: +                    - Destination ip address subnet or range. +                    - IPv4/6 address, subnet or range to match. +                    - Match everything except the specified address, subnet or range. +                    - Destination ip address subnet or range. +                    type: str +                  group: +                    description: +                    - Destination group. +                    type: dict +                    suboptions: +                      address_group: +                        description: +                        - Group of addresses. +                        type: str +                      network_group: +                        description: +                        - Group of networks. +                        type: str +                      port_group: +                        description: +                        - Group of ports. +                        type: str +                  port: +                    description: +                    - Multiple destination ports can be specified as a comma-separated +                      list. +                    - The whole list can also be "negated" using '!'. +                    - For example:'!22,telnet,http,123,1001-1005'. +                    type: str +              disabled: +                description: +                - Option to disable firewall rule. +                type: bool +              fragment: +                description: +                - IP fragment match. +                type: str +                choices: +                - match-frag +                - match-non-frag +              icmp: +                description: +                - ICMP type and code information. +                type: dict +                suboptions: +                  type_name: +                    description: +                    - ICMP type-name. +                    type: str +                    choices: +                    - any +                    - echo-reply +                    - destination-unreachable +                    - network-unreachable +                    - host-unreachable +                    - protocol-unreachable +                    - port-unreachable +                    - fragmentation-needed +                    - source-route-failed +                    - network-unknown +                    - host-unknown +                    - network-prohibited +                    - host-prohibited +                    - TOS-network-unreachable +                    - TOS-host-unreachable +                    - communication-prohibited +                    - host-precedence-violation +                    - precedence-cutoff +                    - source-quench +                    - redirect +                    - network-redirect +                    - host-redirect +                    - TOS-network-redirect +                    - TOS-host-redirect +                    - echo-request +                    - router-advertisement +                    - router-solicitation +                    - time-exceeded +                    - ttl-zero-during-transit +                    - ttl-zero-during-reassembly +                    - parameter-problem +                    - ip-header-bad +                    - required-option-missing +                    - timestamp-request +                    - timestamp-reply +                    - address-mask-request +                    - address-mask-reply +                    - ping +                    - pong +                    - ttl-exceeded +                  code: +                    description: +                    - ICMP code. +                    type: int +                  type: +                    description: +                    - ICMP type. +                    type: int +              ipsec: +                description: +                - Inboud ip sec packets. +                type: str +                choices: +                - match-ipsec +                - match-none +              limit: +                description: +                - Rate limit using a token bucket filter. +                type: dict +                suboptions: +                  burst: +                    description: +                    - Maximum number of packets to allow in excess of rate. +                    type: int +                  rate: +                    description: +                    - format for rate (integer/time unit). +                    - any one of second, minute, hour or day may be used to specify +                      time unit. +                    - eg. 1/second implies rule to be matched at an average of once +                      per second. +                    type: dict +                    suboptions: +                      number: +                        description: +                        - This is the integer value. +                        type: int +                      unit: +                        description: +                        - This is the time unit. +                        type: str +              p2p: +                description: +                - P2P application packets. +                type: list +                elements: dict +                suboptions: +                  application: +                    description: +                    - Name of the application. +                    type: str +                    choices: +                    - all +                    - applejuice +                    - bittorrent +                    - directconnect +                    - edonkey +                    - gnutella +                    - kazaa +              protocol: +                description: +                - Protocol to match (protocol name in /etc/protocols or protocol number +                  or all). +                - <text> IP protocol name from /etc/protocols (e.g. "tcp" or "udp"). +                - <0-255> IP protocol number. +                - tcp_udp Both TCP and UDP. +                - all All IP protocols. +                - (!)All IP protocols except for the specified name or number. +                type: str +              recent: +                description: +                - Parameters for matching recently seen sources. +                type: dict +                suboptions: +                  count: +                    description: +                    - Source addresses seen more than N times. +                    type: int +                  time: +                    description: +                    - Source addresses seen in the last N seconds. +                    type: int +              source: +                description: +                - Source parameters. +                type: dict +                suboptions: +                  address: +                    description: +                    - Source ip address subnet or range. +                    - IPv4/6 address, subnet or range to match. +                    - Match everything except the specified address, subnet or range. +                    - Source ip address subnet or range. +                    type: str +                  group: +                    description: +                    - Source group. +                    type: dict +                    suboptions: +                      address_group: +                        description: +                        - Group of addresses. +                        type: str +                      network_group: +                        description: +                        - Group of networks. +                        type: str +                      port_group: +                        description: +                        - Group of ports. +                        type: str +                  port: +                    description: +                    - Multiple source ports can be specified as a comma-separated +                      list. +                    - The whole list can also be "negated" using '!'. +                    - For example:'!22,telnet,http,123,1001-1005'. +                    type: str +                  mac_address: +                    description: +                    - <MAC address> MAC address to match. +                    - <!MAC address> Match everything except the specified MAC address. +                    type: str +              state: +                description: +                - Session state. +                type: dict +                suboptions: +                  established: +                    description: +                    - Established state. +                    type: bool +                  invalid: +                    description: +                    - Invalid state. +                    type: bool +                  new: +                    description: +                    - New state. +                    type: bool +                  related: +                    description: +                    - Related state. +                    type: bool +              tcp: +                description: +                - TCP flags to match. +                type: dict +                suboptions: +                  flags: +                    description: +                    - TCP flags to be matched. +                    type: str +              time: +                description: +                - Time to match rule. +                type: dict +                suboptions: +                  utc: +                    description: +                    - Interpret times for startdate, stopdate, starttime and stoptime +                      to be UTC. +                    type: bool +                  monthdays: +                    description: +                    - Monthdays to match rule on. +                    type: str +                  startdate: +                    description: +                    - Date to start matching rule. +                    type: str +                  starttime: +                    description: +                    - Time of day to start matching rule. +                    type: str +                  stopdate: +                    description: +                    - Date to stop matching rule. +                    type: str +                  stoptime: +                    description: +                    - Time of day to stop matching rule. +                    type: str +                  weekdays: +                    description: +                    - Weekdays to match rule on. +                    type: str +  running_config: +    description: +    - The module, by default, will connect to the remote device and retrieve the current +      running-config to use as a base for comparing against the contents of source. +      There are times when it is not desirable to have the task get the current running-config +      for every task in a playbook.  The I(running_config) argument allows the implementer +      to pass in the configuration to use as the base config for comparison. This +      value of this option should be the output received from device by executing +      command C(show configuration commands | grep 'firewall' +    type: str +  state: +    description: +    - The state the configuration should be left in +    type: str +    choices: +    - merged +    - replaced +    - overridden +    - deleted +    - gathered +    - rendered +    - parsed +    default: merged +""" +EXAMPLES = """ +# Using deleted to delete firewall rules based on rule-set name +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 501 action 'accept' +# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' +# set firewall name Downlink rule 501 ipsec 'match-ipsec' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' +# +- name: Delete attributes of given firewall rules. +  vyos_firewall_rules: +    config: +      - afi: ipv4 +        rule_sets: +          - name: 'Downlink' +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +#    "commands": [ +#        "delete firewall name Downlink" +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall group address-group 'inbound' + + +# Using deleted to delete all the the firewall rules when provided config is empty +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 501 action 'accept' +# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' +# set firewall name Downlink rule 501 ipsec 'match-ipsec' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' +# +- name: Delete attributes of given firewall rules. +  vyos_firewall_rules: +    config: +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +#    "commands": [ +#        "delete firewall name" +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall group address-group 'inbound' + + +# Using deleted to delete the the firewall rules based on afi +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 501 action 'accept' +# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' +# set firewall name Downlink rule 501 ipsec 'match-ipsec' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' +# +- name: Delete attributes of given firewall rules. +  vyos_firewall_rules: +    config: +      - afi: ipv4 +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +#    "commands": [ +#        "delete firewall name", +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep firewall +# set firewall group address-group 'inbound' + + + +# Using deleted to delete the the firewall rules based on rule number/id +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 501 action 'accept' +# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' +# set firewall name Downlink rule 501 ipsec 'match-ipsec' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' +# +- name: Delete attributes of given firewall rules. +  vyos_firewall_rules: +    config: +      - afi: ipv4 +        rule_sets: +          - name: 'Downlink' +            rules: +              - number: 501 +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +#    "commands": [ +#        "delete firewall ipv6-name Downlink rule 501" +#    ] +# +#    "after": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +# After state +# ------------ +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' + + +# Using merged +# +# Before state: +# ------------- +# +# vyos@vyos# run show  configuration commands | grep firewall +# set firewall group address-group 'inbound' +# +- name: Merge the provided configuration with the exisiting running configuration +  vyos_firewall_rules: +    config: +      - afi: 'ipv6' +        rule_sets: +          - name: 'UPLINK' +            description: 'This is ipv6 specific rule-set' +            default_action: 'accept' +            rules: +              - number: 1 +                action: 'accept' +                description: 'Fwipv6-Rule 1 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 2 +                action: 'accept' +                description: 'Fwipv6-Rule 2 is configured by Ansible' +                ipsec: 'match-ipsec' + +      - afi: 'ipv4' +        rule_sets: +          - name: 'INBOUND' +            description: 'IPv4 INBOUND rule set' +            default_action: 'accept' +            rules: +              - number: 101 +                action: 'accept' +                description: 'Rule 101 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 102 +                action: 'reject' +                description: 'Rule 102 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 103 +                action: 'accept' +                description: 'Rule 103 is configured by Ansible' +                destination: +                  group: +                    address_group: 'inbound' +                source: +                  address: '192.0.2.0' +                state: +                  established: true +                  new: false +                  invalid: false +                  related: true +    state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [] +# +#    "commands": [ +#       "set firewall ipv6-name UPLINK default-action 'accept'", +#       "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'", +#       "set firewall ipv6-name UPLINK rule 1 action 'accept'", +#       "set firewall ipv6-name UPLINK rule 1", +#       "set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'", +#       "set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'", +#       "set firewall ipv6-name UPLINK rule 2 action 'accept'", +#       "set firewall ipv6-name UPLINK rule 2", +#       "set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'", +#       "set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'", +#       "set firewall name INBOUND default-action 'accept'", +#       "set firewall name INBOUND description 'IPv4 INBOUND rule set'", +#       "set firewall name INBOUND rule 101 action 'accept'", +#       "set firewall name INBOUND rule 101", +#       "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", +#       "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", +#       "set firewall name INBOUND rule 102 action 'reject'", +#       "set firewall name INBOUND rule 102", +#       "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", +#       "set firewall name INBOUND rule 102 ipsec 'match-ipsec'", +#       "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", +#       "set firewall name INBOUND rule 103 destination group address-group inbound", +#       "set firewall name INBOUND rule 103", +#       "set firewall name INBOUND rule 103 source address 192.0.2.0", +#       "set firewall name INBOUND rule 103 state established enable", +#       "set firewall name INBOUND rule 103 state related enable", +#       "set firewall name INBOUND rule 103 state invalid disable", +#       "set firewall name INBOUND rule 103 state new disable", +#       "set firewall name INBOUND rule 103 action 'accept'" +#    ] +# +# "after": [ +#        { +#            "afi": "ipv6", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "This is ipv6 specific rule-set", +#                    "name": "UPLINK", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 1 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 1 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 2 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 2 +#                        } +#                    ] +#                } +#            ] +#        }, +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "INBOUND", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 101 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 101 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 102 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 102 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Rule 103 is configured by Ansible", +#                            "destination": { +#                                "group": { +#                                    "address_group": "inbound" +#                                } +#                            }, +#                            "number": 103, +#                            "source": { +#                                "address": "192.0.2.0" +#                            }, +#                            "state": { +#                                "established": true, +#                                "invalid": false, +#                                "new": false, +#                                "related": true +#                            } +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall ipv6-name UPLINK rule 1 action 'accept' +# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' +# set firewall ipv6-name UPLINK rule 2 action 'accept' +# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 102 action 'reject' +# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' +# set firewall name INBOUND rule 102 ipsec 'match-ipsec' +# set firewall name INBOUND rule 103 action 'accept' +# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' +# set firewall name INBOUND rule 103 destination group address-group 'inbound' +# set firewall name INBOUND rule 103 source address '192.0.2.0' +# set firewall name INBOUND rule 103 state established 'enable' +# set firewall name INBOUND rule 103 state invalid 'disable' +# set firewall name INBOUND rule 103 state new 'disable' +# set firewall name INBOUND rule 103 state related 'enable' + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall ipv6-name UPLINK rule 1 action 'accept' +# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' +# set firewall ipv6-name UPLINK rule 2 action 'accept' +# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 102 action 'reject' +# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' +# set firewall name INBOUND rule 102 ipsec 'match-ipsec' +# set firewall name INBOUND rule 103 action 'accept' +# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' +# set firewall name INBOUND rule 103 destination group address-group 'inbound' +# set firewall name INBOUND rule 103 source address '192.0.2.0' +# set firewall name INBOUND rule 103 state established 'enable' +# set firewall name INBOUND rule 103 state invalid 'disable' +# set firewall name INBOUND rule 103 state new 'disable' +# set firewall name INBOUND rule 103 state related 'enable' +# +- name: Replace device configurations of listed firewall rules with provided configurations +  vyos_firewall_rules: +    config: +      - afi: 'ipv6' +        rule_sets: +          - name: 'UPLINK' +            description: 'This is ipv6 specific rule-set' +            default_action: 'accept' +      - afi: 'ipv4' +        rule_sets: +          - name: 'INBOUND' +            description: 'IPv4 INBOUND rule set' +            default_action: 'accept' +            rules: +              - number: 101 +                action: 'accept' +                description: 'Rule 101 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 104 +                action: 'reject' +                description: 'Rule 104 is configured by Ansible' +                ipsec: 'match-none' +    state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +#    "before": [ +#        { +#            "afi": "ipv6", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "This is ipv6 specific rule-set", +#                    "name": "UPLINK", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 1 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 1 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 2 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 2 +#                        } +#                    ] +#                } +#            ] +#        }, +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "INBOUND", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 101 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 101 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 102 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 102 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Rule 103 is configured by Ansible", +#                            "destination": { +#                                "group": { +#                                    "address_group": "inbound" +#                                } +#                            }, +#                            "number": 103, +#                            "source": { +#                                "address": "192.0.2.0" +#                            }, +#                            "state": { +#                                "established": true, +#                                "invalid": false, +#                                "new": false, +#                                "related": true +#                            } +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# "commands": [ +#        "delete firewall ipv6-name UPLINK rule 1", +#        "delete firewall ipv6-name UPLINK rule 2", +#        "delete firewall name INBOUND rule 102", +#        "delete firewall name INBOUND rule 103", +#        "set firewall name INBOUND rule 104 action 'reject'", +#        "set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible'", +#        "set firewall name INBOUND rule 104", +#        "set firewall name INBOUND rule 104 ipsec 'match-none'" +#    ] +# +#    "after": [ +#        { +#            "afi": "ipv6", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "This is ipv6 specific rule-set", +#                    "name": "UPLINK" +#                } +#            ] +#        }, +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "INBOUND", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 101 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 101 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 104 is configured by Ansible", +#                            "ipsec": "match-none", +#                            "number": 104 +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 104 action 'reject' +# set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible' +# set firewall name INBOUND rule 104 ipsec 'match-none' + + +# Using overridden +# +# Before state +# -------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 104 action 'reject' +# set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible' +# set firewall name INBOUND rule 104 ipsec 'match-none' +# +- name: Overrides all device configuration with provided configuration +  vyos_firewall_rules: +    config: +      - afi: 'ipv4' +        rule_sets: +          - name: 'Downlink' +            description: 'IPv4 INBOUND rule set' +            default_action: 'accept' +            rules: +              - number: 501 +                action: 'accept' +                description: 'Rule 501 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 502 +                action: 'reject' +                description: 'Rule 502 is configured by Ansible' +                ipsec: 'match-ipsec' +    state: overridden +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +#        { +#            "afi": "ipv6", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "This is ipv6 specific rule-set", +#                    "name": "UPLINK" +#                } +#            ] +#        }, +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "INBOUND", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 101 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 101 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 104 is configured by Ansible", +#                            "ipsec": "match-none", +#                            "number": 104 +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +#    "commands": [ +#        "delete firewall ipv6-name UPLINK", +#        "delete firewall name INBOUND", +#        "set firewall name Downlink default-action 'accept'", +#        "set firewall name Downlink description 'IPv4 INBOUND rule set'", +#        "set firewall name Downlink rule 501 action 'accept'", +#        "set firewall name Downlink rule 501", +#        "set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'", +#        "set firewall name Downlink rule 501 ipsec 'match-ipsec'", +#        "set firewall name Downlink rule 502 action 'reject'", +#        "set firewall name Downlink rule 502", +#        "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'", +#        "set firewall name Downlink rule 502 ipsec 'match-ipsec'" +# +# +#    "after": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#               } +#            ] +#        } +#    ] +# +# +# After state +# ------------ +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall name Downlink default-action 'accept' +# set firewall name Downlink description 'IPv4 INBOUND rule set' +# set firewall name Downlink rule 501 action 'accept' +# set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' +# set firewall name Downlink rule 501 ipsec 'match-ipsec' +# set firewall name Downlink rule 502 action 'reject' +# set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' +# set firewall name Downlink rule 502 ipsec 'match-ipsec' + + +# Using gathered +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall ipv6-name UPLINK rule 1 action 'accept' +# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' +# set firewall ipv6-name UPLINK rule 2 action 'accept' +# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 102 action 'reject' +# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' +# set firewall name INBOUND rule 102 ipsec 'match-ipsec' +# set firewall name INBOUND rule 103 action 'accept' +# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' +# set firewall name INBOUND rule 103 destination group address-group 'inbound' +# set firewall name INBOUND rule 103 source address '192.0.2.0' +# set firewall name INBOUND rule 103 state established 'enable' +# set firewall name INBOUND rule 103 state invalid 'disable' +# set firewall name INBOUND rule 103 state new 'disable' +# set firewall name INBOUND rule 103 state related 'enable' +# +- name: Gather listed firewall rules with provided configurations +  vyos_firewall_rules: +    config: +    state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +#    "gathered": [ +#        { +#            "afi": "ipv6", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "This is ipv6 specific rule-set", +#                    "name": "UPLINK", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 1 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 1 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Fwipv6-Rule 2 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 2 +#                        } +#                    ] +#                } +#            ] +#        }, +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "INBOUND", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 101 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 101 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 102 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 102 +#                        }, +#                        { +#                            "action": "accept", +#                            "description": "Rule 103 is configured by Ansible", +#                            "destination": { +#                                "group": { +#                                    "address_group": "inbound" +#                                } +#                            }, +#                            "number": 103, +#                            "source": { +#                                "address": "192.0.2.0" +#                            }, +#                            "state": { +#                                "established": true, +#                                "invalid": false, +#                                "new": false, +#                                "related": true +#                            } +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set firewall group address-group 'inbound' +# set firewall ipv6-name UPLINK default-action 'accept' +# set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' +# set firewall ipv6-name UPLINK rule 1 action 'accept' +# set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' +# set firewall ipv6-name UPLINK rule 2 action 'accept' +# set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' +# set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' +# set firewall name INBOUND default-action 'accept' +# set firewall name INBOUND description 'IPv4 INBOUND rule set' +# set firewall name INBOUND rule 101 action 'accept' +# set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' +# set firewall name INBOUND rule 101 ipsec 'match-ipsec' +# set firewall name INBOUND rule 102 action 'reject' +# set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' +# set firewall name INBOUND rule 102 ipsec 'match-ipsec' +# set firewall name INBOUND rule 103 action 'accept' +# set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' +# set firewall name INBOUND rule 103 destination group address-group 'inbound' +# set firewall name INBOUND rule 103 source address '192.0.2.0' +# set firewall name INBOUND rule 103 state established 'enable' +# set firewall name INBOUND rule 103 state invalid 'disable' +# set firewall name INBOUND rule 103 state new 'disable' +# set firewall name INBOUND rule 103 state related 'enable' + + +# Using rendered +# +# +- name: Render the commands for provided  configuration +  vyos_firewall_rules: +    config: +      - afi: 'ipv6' +        rule_sets: +          - name: 'UPLINK' +            description: 'This is ipv6 specific rule-set' +            default_action: 'accept' +      - afi: 'ipv4' +        rule_sets: +          - name: 'INBOUND' +            description: 'IPv4 INBOUND rule set' +            default_action: 'accept' +            rules: +              - number: 101 +                action: 'accept' +                description: 'Rule 101 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 102 +                action: 'reject' +                description: 'Rule 102 is configured by Ansible' +                ipsec: 'match-ipsec' +              - number: 103 +                action: 'accept' +                description: 'Rule 103 is configured by Ansible' +                destination: +                  group: +                    address_group: 'inbound' +                source: +                  address: '192.0.2.0' +                state: +                  established: true +                  new: false +                  invalid: false +                  related: true +    state: rendered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +#        "set firewall ipv6-name UPLINK default-action 'accept'", +#        "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'", +#        "set firewall name INBOUND default-action 'accept'", +#        "set firewall name INBOUND description 'IPv4 INBOUND rule set'", +#        "set firewall name INBOUND rule 101 action 'accept'", +#        "set firewall name INBOUND rule 101", +#        "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", +#        "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", +#        "set firewall name INBOUND rule 102 action 'reject'", +#        "set firewall name INBOUND rule 102", +#        "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", +#        "set firewall name INBOUND rule 102 ipsec 'match-ipsec'", +#        "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", +#        "set firewall name INBOUND rule 103 destination group address-group inbound", +#        "set firewall name INBOUND rule 103", +#        "set firewall name INBOUND rule 103 source address 192.0.2.0", +#        "set firewall name INBOUND rule 103 state established enable", +#        "set firewall name INBOUND rule 103 state related enable", +#        "set firewall name INBOUND rule 103 state invalid disable", +#        "set firewall name INBOUND rule 103 state new disable", +#        "set firewall name INBOUND rule 103 action 'accept'" +#    ] + + +# Using parsed +# +# +- name: Render the commands for provided  configuration +  vyos_firewall_rules: +    running_config: +      "set firewall group address-group 'inbound' + set firewall name Downlink default-action 'accept' + set firewall name Downlink description 'IPv4 INBOUND rule set' + set firewall name Downlink rule 501 action 'accept' + set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' + set firewall name Downlink rule 501 ipsec 'match-ipsec' + set firewall name Downlink rule 502 action 'reject' + set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' + set firewall name Downlink rule 502 ipsec 'match-ipsec'" +    state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +#        { +#            "afi": "ipv4", +#            "rule_sets": [ +#                { +#                    "default_action": "accept", +#                    "description": "IPv4 INBOUND rule set", +#                    "name": "Downlink", +#                    "rules": [ +#                        { +#                            "action": "accept", +#                            "description": "Rule 501 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 501 +#                        }, +#                        { +#                            "action": "reject", +#                            "description": "Rule 502 is configured by Ansible", +#                            "ipsec": "match-ipsec", +#                            "number": 502 +#                        } +#                    ] +#                } +#            ] +#        } +#    ] + + +""" +RETURN = """ +before: +  description: The configuration prior to the model invocation. +  returned: always +  type: list +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +after: +  description: The resulting configuration model invocation. +  returned: when changed +  type: list +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +commands: +  description: The set of commands pushed to the remote device. +  returned: always +  type: list +  sample: +    - "set firewall name Downlink default-action 'accept'" +    - "set firewall name Downlink description 'IPv4 INBOUND rule set'" +    - "set firewall name Downlink rule 501 action 'accept'" +    - "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'" +    - "set firewall name Downlink rule 502 ipsec 'match-ipsec'" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( +    Firewall_rulesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import ( +    Firewall_rules, +) + + +def main(): +    """ +    Main entry point for module execution +    :returns: the result form module invocation +    """ +    required_if = [ +        ("state", "merged", ("config",)), +        ("state", "replaced", ("config",)), +        ("state", "overridden", ("config",)), +        ("state", "parsed", ("running_config",)), +    ] +    mutually_exclusive = [("config", "running_config")] + +    module = AnsibleModule( +        argument_spec=Firewall_rulesArgs.argument_spec, +        required_if=required_if, +        supports_check_mode=True, +        mutually_exclusive=mutually_exclusive, +    ) +    result = Firewall_rules(module).execute_module() +    module.exit_json(**result) + + +if __name__ == "__main__": +    main() diff --git a/plugins/modules/vyos_static_route.py b/plugins/modules/vyos_static_route.py index e0c40e7..af9a1e3 100644 --- a/plugins/modules/vyos_static_route.py +++ b/plugins/modules/vyos_static_route.py @@ -21,7 +21,7 @@  ANSIBLE_METADATA = {      "metadata_version": "1.1", -    "status": ["preview"], +    "status": ["deprecated"],      "supported_by": "network",  } @@ -32,6 +32,10 @@ short_description: Manage static IP routes on Vyatta VyOS network devices  description:  - This module provides declarative management of static IP routes on Vyatta VyOS network    devices. +deprecated: +  removed_in: '2.13' +  alternative: vyos_static_routes +  why: Updated modules released with more functionality.  notes:  - Tested against VyOS 1.1.8 (helium).  - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). @@ -40,17 +44,22 @@ options:      description:      - Network prefix of the static route. C(mask) param should be ignored if C(prefix)        is provided with C(mask) value C(prefix/mask). +    type: str    mask:      description:      - Network prefix mask of the static route. +    type: str    next_hop:      description:      - Next hop IP of the static route. +    type: str    admin_distance:      description:      - Admin distance of the static route. +    type: int    aggregate:      description: List of static route definitions +    type: list    state:      description:      - State of the static route configuration. @@ -58,6 +67,7 @@ options:      choices:      - present      - absent +    type: str  extends_documentation_fragment:  - vyos.vyos.vyos  """ diff --git a/plugins/modules/vyos_static_routes.py b/plugins/modules/vyos_static_routes.py new file mode 100644 index 0000000..6e50203 --- /dev/null +++ b/plugins/modules/vyos_static_routes.py @@ -0,0 +1,1156 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +#                WARNING                    # +############################################# +# +# This file is auto generated by the resource +#   module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +#   by the resource module builder. +# +# Changes should be made in the model used to +#   generate this file or in the resource module +#   builder template. +# +############################################# + +""" +The module file for vyos_static_routes +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { +    "metadata_version": "1.1", +    "status": ["preview"], +    "supported_by": "network", +} + +DOCUMENTATION = """module: vyos_static_routes +short_description: Manages attributes of static routes on VyOS network devices. +description: This module manages attributes of static routes on VyOS network devices. +notes: +- Tested against VyOS 1.1.8 (helium). +- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). +author: +- Rohit Thakur (@rohitthakur2590) +options: +  config: +    description: A provided static route configuration. +    type: list +    elements: dict +    suboptions: +      address_families: +        description: A dictionary specifying the address family to which the static +          route(s) belong. +        type: list +        elements: dict +        suboptions: +          afi: +            description: +            - Specifies the type of route. +            type: str +            choices: +            - ipv4 +            - ipv6 +            required: true +          routes: +            description: A ditionary that specify the static route configurations. +            type: list +            elements: dict +            suboptions: +              dest: +                description: +                - An IPv4/v6 address in CIDR notation that specifies the destination +                  network for the static route. +                type: str +                required: true +              blackhole_config: +                description: +                - Configured to silently discard packets. +                type: dict +                suboptions: +                  type: +                    description: +                    - This is to configure only blackhole. +                    type: str +                  distance: +                    description: +                    - Distance for the route. +                    type: int +              next_hops: +                description: +                - Next hops to the specified destination. +                type: list +                elements: dict +                suboptions: +                  forward_router_address: +                    description: +                    - The IP address of the next hop that can be used to reach the +                      destination network. +                    type: str +                    required: true +                  enabled: +                    description: +                    - Disable IPv4/v6 next-hop static route. +                    type: bool +                  admin_distance: +                    description: +                    - Distance value for the route. +                    type: int +                  interface: +                    description: +                    - Name of the outgoing interface. +                    type: str +  running_config: +    description: +    - The module, by default, will connect to the remote device and retrieve the current +      running-config to use as a base for comparing against the contents of source. +      There are times when it is not desirable to have the task get the current running-config +      for every task in a playbook.  The I(running_config) argument allows the implementer +      to pass in the configuration to use as the base config for comparison. This +      value of this option should be the output received from device by executing +      command C(show configuration commands | grep 'static route') +    type: str +  state: +    description: +    - The state of the configuration after module completion. +    type: str +    choices: +    - merged +    - replaced +    - overridden +    - deleted +    - gathered +    - rendered +    - parsed +    default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration  commands | grep static +# +- name: Merge the provided configuration with the exisiting running configuration +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +         routes: +           - dest: 192.0.2.32/28 +             blackhole_config: +               type: 'blackhole' +             next_hops: +               - forward_router_address: 192.0.2.6 +               - forward_router_address: 192.0.2.7 +     - address_families: +       - afi: 'ipv6' +         routes: +           - dest: 2001:db8:1000::/36 +             blackhole_config: +               distance: 2 +             next_hops: +               - forward_router_address: 2001:db8:2000:2::1 +               - forward_router_address: 2001:db8:2000:2::2 +    state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [] +# +#    "commands": [ +#        "set protocols static route 192.0.2.32/28", +#        "set protocols static route 192.0.2.32/28 blackhole", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'", +#        "set protocols static route6 2001:db8:1000::/36", +#        "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'", +#        "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'", +#        "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'" +#    ] +# +# "after": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route 192.0.2.33/28 'blackhole' +# set protocols static route 192.0.2.33/28 next-hop '192.0.2.3' +# set protocols static route 192.0.2.33/28 next-hop '192.0.2.4' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Replace device configurations of listed static routes with provided configurations +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +         routes: +           - dest: 192.0.2.32/28 +             blackhole_config: +               distance: 2 +             next_hops: +               - forward_router_address: 192.0.2.7 +                 enabled: false +               - forward_router_address: 192.0.2.9 +    state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +#    "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        }, +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.33/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.3" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.4" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# "commands": [ +#        "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.6'", +#        "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.7'", +#        "set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable'", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'", +#        "set protocols static route 192.0.2.32/28 blackhole distance '2'" +#    ] +# +#    "after": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "enabled": false, +#                                    "forward_router_address": "192.0.2.7" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.9" +#                                } +#                            ] +#                        }, +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.33/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.3" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.4" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 blackhole distance '2' +# set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.9' +# set protocols static route 192.0.2.33/28 'blackhole' +# set protocols static route 192.0.2.33/28 next-hop '192.0.2.3' +# set protocols static route 192.0.2.33/28 next-hop '192.0.2.4' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' + + +# Using overridden +# +# Before state +# -------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 blackhole distance '2' +# set protocols static route 192.0.2.32/28 next-hop 192.0.2.7 'disable' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.9' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Overrides all device configuration with provided configuration +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +         routes: +           - dest: 198.0.2.48/28 +             next_hops: +               - forward_router_address: 192.0.2.18 +    state: overridden +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "enabled": false, +#                                    "forward_router_address": "192.0.2.7" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.9" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +#    "commands": [ +#        "delete protocols static route 192.0.2.32/28", +#        "delete protocols static route6 2001:db8:1000::/36", +#        "set protocols static route 198.0.2.48/28", +#        "set protocols static route 198.0.2.48/28 next-hop '192.0.2.18'" +# +# +#    "after": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "dest": "198.0.2.48/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.18" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# +# After state +# ------------ +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 198.0.2.48/28 next-hop '192.0.2.18' + + +# Using deleted to delete static route based on destination +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Delete static route per destination. +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +         routes: +           - dest: '192.0.2.32/28' +       - afi: 'ipv6' +         routes: +           - dest: '2001:db8:1000::/36' +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +#    "commands": [ +#       "delete protocols static route 192.0.2.32/28", +#       "delete protocols static route6 2001:db8:1000::/36" +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep static +# set protocols 'static' + + +# Using deleted to delete static route based on afi +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Delete static route based on afi. +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +       - afi: 'ipv6' +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +#    "commands": [ +#       "delete protocols static route", +#       "delete protocols static route6" +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep static +# set protocols 'static' + + +# Using deleted to delete all the static routes when passes config is empty +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Delete all the static routes. +  vyos_static_routes: +    config: +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +#    "commands": [ +#       "delete protocols static route", +#       "delete protocols static route6" +#    ] +# +# "after": [] +# After state +# ------------ +# vyos@vyos# run show configuration commands | grep static +# set protocols 'static' + + +# Using deleted to delete static route based on next-hop +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Delete static routes per next-hops +  vyos_static_routes: +    config: +     - address_families: +       - afi: 'ipv4' +         routes: +           - dest: '192.0.2.32/28' +             next-hops: +               - forward_router_address: '192.0.2.6' +       - afi: 'ipv6' +         routes: +           - dest: '2001:db8:1000::/36' +             next-hops: +               - forward_router_address: '2001:db8:2000:2::1' +    state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +#    "before": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +#    "commands": [ +#       "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.6'", +#       "delete protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'" +#    ] +# +#    "after": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# After state +# ------------ +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' + + +# Using rendered +# +# +- name: Render the commands for provided  configuration +  vyos_static_routes: +    config: +      - address_families: +          - afi: 'ipv4' +            routes: +              - dest: 192.0.2.32/28 +                blackhole_config: +                  type: 'blackhole' +                next_hops: +                  - forward_router_address: 192.0.2.6 +                  - forward_router_address: 192.0.2.7 +      - address_families: +          - afi: 'ipv6' +            routes: +              - dest: 2001:db8:1000::/36 +                blackhole_config: +                  distance: 2 +                next_hops: +                  - forward_router_address: 2001:db8:2000:2::1 +                  - forward_router_address: 2001:db8:2000:2::2 +    state: rendered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +#        "set protocols static route 192.0.2.32/28", +#        "set protocols static route 192.0.2.32/28 blackhole", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'", +#        "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'", +#        "set protocols static route6 2001:db8:1000::/36", +#        "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'", +#        "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'", +#        "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'" +#    ] + + +# Using parsed +# +# +- name: Render the commands for provided  configuration +  vyos_static_routes: +    running_config: +      "set protocols static route 192.0.2.32/28 'blackhole' + set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' + set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' + set protocols static route6 2001:db8:1000::/36 blackhole distance '2' + set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' + set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'" +    state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] + + +# Using gathered +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +# +- name: Gather listed static routes with provided configurations +  vyos_static_routes: +    config: +    state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +#    "gathered": [ +#        { +#            "address_families": [ +#                { +#                    "afi": "ipv4", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "type": "blackhole" +#                            }, +#                            "dest": "192.0.2.32/28", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "192.0.2.6" +#                                }, +#                                { +#                                    "forward_router_address": "192.0.2.7" +#                                } +#                            ] +#                        } +#                    ] +#                }, +#                { +#                    "afi": "ipv6", +#                    "routes": [ +#                        { +#                            "blackhole_config": { +#                                "distance": 2 +#                            }, +#                            "dest": "2001:db8:1000::/36", +#                            "next_hops": [ +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::1" +#                                }, +#                                { +#                                    "forward_router_address": "2001:db8:2000:2::2" +#                                } +#                            ] +#                        } +#                    ] +#                } +#            ] +#        } +#    ] +# +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep static +# set protocols static route 192.0.2.32/28 'blackhole' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.6' +# set protocols static route 192.0.2.32/28 next-hop '192.0.2.7' +# set protocols static route6 2001:db8:1000::/36 blackhole distance '2' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +# set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' + + +""" +RETURN = """ +before: +  description: The configuration prior to the model invocation. +  returned: always +  type: list +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +after: +  description: The resulting configuration model invocation. +  returned: when changed +  type: list +  sample: > +    The configuration returned will always be in the same format +     of the parameters above. +commands: +  description: The set of commands pushed to the remote device. +  returned: always +  type: list +  sample: +    - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.6'" +    - "set protocols static route 192.0.2.32/28 'blackhole'" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( +    Static_routesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.static_routes.static_routes import ( +    Static_routes, +) + + +def main(): +    """ +    Main entry point for module execution + +    :returns: the result form module invocation +    """ +    required_if = [ +        ("state", "merged", ("config",)), +        ("state", "replaced", ("config",)), +        ("state", "overridden", ("config",)), +        ("state", "parsed", ("running_config",)), +    ] +    mutually_exclusive = [("config", "running_config")] + +    module = AnsibleModule( +        argument_spec=Static_routesArgs.argument_spec, +        required_if=required_if, +        supports_check_mode=True, +        mutually_exclusive=mutually_exclusive, +    ) +    result = Static_routes(module).execute_module() +    module.exit_json(**result) + + +if __name__ == "__main__": +    main() | 
