From 9eb7709c61e4c2e1409ebf1f2854c9249cb1fd35 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 18 Feb 2020 07:32:26 -0500 Subject: [PATCH] Vyos static route module added (#62193) * Vyos static route module added Signed-off-by: rohitthakur2590 * sanity fixes Signed-off-by: rohitthakur2590 * empty config traceback fix Signed-off-by: rohitthakur2590 * sanity check fix * model specific changes and SI test cases updated Signed-off-by: rohitthakur2590 * new state changes and SI test cases updated Signed-off-by: rohitthakur2590 * sanity fixes Signed-off-by: rohitthakur2590 * UT cases added Signed-off-by: rohitthakur2590 * replaced operation fix Signed-off-by: rohitthakur2590 * review comments incorporated Signed-off-by: rohitthakur2590 * shippable fix Signed-off-by: rohitthakur2590 * sanity fix Signed-off-by: rohitthakur2590 * delete opr updated Signed-off-by: rohitthakur2590 * comments incorporated Signed-off-by: rohitthakur2590 --- .../rst/porting_guides/porting_guide_2.10.rst | 1 + .../vyos/argspec/static_routes/__init__.py | 0 .../argspec/static_routes/static_routes.py | 107 ++ .../vyos/config/static_routes/__init__.py | 0 .../config/static_routes/static_routes.py | 523 ++++++++ .../module_utils/network/vyos/facts/facts.py | 3 +- .../vyos/facts/static_routes/__init__.py | 0 .../vyos/facts/static_routes/static_routes.py | 161 +++ .../module_utils/network/vyos/utils/utils.py | 45 + ..._static_route.py => _vyos_static_route.py} | 12 +- .../modules/network/vyos/vyos_facts.py | 2 +- .../network/vyos/vyos_static_routes.py | 1147 +++++++++++++++++ .../vyos_static_routes/defaults/main.yaml | 3 + .../targets/vyos_static_routes/meta/main.yaml | 0 .../targets/vyos_static_routes/tasks/cli.yaml | 19 + .../vyos_static_routes/tasks/main.yaml | 2 + .../tests/cli/_parsed_config.cfg | 6 + .../tests/cli/_populate.yaml | 14 + .../tests/cli/_remove_config.yaml | 8 + .../vyos_static_routes/tests/cli/deleted.yaml | 51 + .../tests/cli/deleted_afi.yaml | 47 + .../tests/cli/deleted_all.yaml | 44 + .../tests/cli/deleted_nh.yaml | 55 + .../tests/cli/empty_config.yaml | 58 + .../tests/cli/gathered.yaml | 31 + .../vyos_static_routes/tests/cli/merged.yaml | 61 + .../tests/cli/overridden.yaml | 52 + .../vyos_static_routes/tests/cli/parsed.yaml | 39 + .../tests/cli/rendered.yaml | 49 + .../tests/cli/replaced.yaml | 56 + .../vyos_static_routes/tests/cli/rtt.yaml | 71 + .../targets/vyos_static_routes/vars/main.yaml | 163 +++ test/sanity/ignore.txt | 20 +- .../fixtures/vyos_static_routes_config.cfg | 2 + .../network/vyos/test_vyos_static_route.py | 8 +- .../network/vyos/test_vyos_static_routes.py | 202 +++ 36 files changed, 3045 insertions(+), 17 deletions(-) create mode 100644 lib/ansible/module_utils/network/vyos/argspec/static_routes/__init__.py create mode 100644 lib/ansible/module_utils/network/vyos/argspec/static_routes/static_routes.py create mode 100644 lib/ansible/module_utils/network/vyos/config/static_routes/__init__.py create mode 100644 lib/ansible/module_utils/network/vyos/config/static_routes/static_routes.py create mode 100644 lib/ansible/module_utils/network/vyos/facts/static_routes/__init__.py create mode 100644 lib/ansible/module_utils/network/vyos/facts/static_routes/static_routes.py rename lib/ansible/modules/network/vyos/{vyos_static_route.py => _vyos_static_route.py} (97%) create mode 100644 lib/ansible/modules/network/vyos/vyos_static_routes.py create mode 100644 test/integration/targets/vyos_static_routes/defaults/main.yaml create mode 100644 test/integration/targets/vyos_static_routes/meta/main.yaml create mode 100644 test/integration/targets/vyos_static_routes/tasks/cli.yaml create mode 100644 test/integration/targets/vyos_static_routes/tasks/main.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/_populate.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/deleted.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/gathered.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/merged.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/overridden.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/parsed.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/rendered.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/replaced.yaml create mode 100644 test/integration/targets/vyos_static_routes/tests/cli/rtt.yaml create mode 100644 test/integration/targets/vyos_static_routes/vars/main.yaml create mode 100644 test/units/modules/network/vyos/fixtures/vyos_static_routes_config.cfg create mode 100644 test/units/modules/network/vyos/test_vyos_static_routes.py diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.10.rst b/docs/docsite/rst/porting_guides/porting_guide_2.10.rst index 09cc7d9c77f..80f630dc0b0 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_2.10.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_2.10.rst @@ -54,6 +54,7 @@ Deprecation notices The following modules will be removed in Ansible 2.14. Please update your playbooks accordingly. * ldap_attr use :ref:`ldap_attrs ` instead. +* vyos_static_route use :ref:`vyos_static_routes ` instead. The following functionality will be removed in Ansible 2.14. Please update update your playbooks accordingly. diff --git a/lib/ansible/module_utils/network/vyos/argspec/static_routes/__init__.py b/lib/ansible/module_utils/network/vyos/argspec/static_routes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/vyos/argspec/static_routes/static_routes.py b/lib/ansible/module_utils/network/vyos/argspec/static_routes/static_routes.py new file mode 100644 index 00000000000..1042d6d31a4 --- /dev/null +++ b/lib/ansible/module_utils/network/vyos/argspec/static_routes/static_routes.py @@ -0,0 +1,107 @@ +# +# -*- 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/lib/ansible/module_utils/network/vyos/config/static_routes/__init__.py b/lib/ansible/module_utils/network/vyos/config/static_routes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/vyos/config/static_routes/static_routes.py b/lib/ansible/module_utils/network/vyos/config/static_routes/static_routes.py new file mode 100644 index 00000000000..786d26057d2 --- /dev/null +++ b/lib/ansible/module_utils/network/vyos/config/static_routes/static_routes.py @@ -0,0 +1,523 @@ +# +# -*- 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.module_utils.network.common.cfg.base import ConfigBase +from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties +from ansible.module_utils.network.vyos.facts.facts import Facts +from ansible.module_utils.six import iteritems +from ansible.module_utils.network. vyos.utils.utils import get_route_type, \ + diff_list_of_dicts, 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/lib/ansible/module_utils/network/vyos/facts/facts.py b/lib/ansible/module_utils/network/vyos/facts/facts.py index 7c12166ee09..8b191f0df49 100644 --- a/lib/ansible/module_utils/network/vyos/facts/facts.py +++ b/lib/ansible/module_utils/network/vyos/facts/facts.py @@ -15,6 +15,7 @@ from ansible.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces impor from ansible.module_utils.network.vyos.facts.lldp_global.lldp_global import Lldp_globalFacts from ansible.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts from ansible.module_utils.network.vyos.facts.firewall_rules.firewall_rules import Firewall_rulesFacts +from ansible.module_utils.network.vyos.facts.static_routes.static_routes import Static_routesFacts from ansible.module_utils.network.vyos.facts.legacy.base import Default, Neighbors, Config @@ -29,6 +30,7 @@ FACT_RESOURCE_SUBSETS = dict( lag_interfaces=Lag_interfacesFacts, lldp_global=Lldp_globalFacts, lldp_interfaces=Lldp_interfacesFacts, + static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts ) @@ -55,5 +57,4 @@ class Facts(FactsBase): self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) if self.VALID_LEGACY_GATHER_SUBSETS: self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) - return self.ansible_facts, self._warnings diff --git a/lib/ansible/module_utils/network/vyos/facts/static_routes/__init__.py b/lib/ansible/module_utils/network/vyos/facts/static_routes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/vyos/facts/static_routes/static_routes.py b/lib/ansible/module_utils/network/vyos/facts/static_routes/static_routes.py new file mode 100644 index 00000000000..4acbab4da74 --- /dev/null +++ b/lib/ansible/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -0,0 +1,161 @@ +# +# -*- 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.module_utils.network.common import utils +from ansible.module_utils.network.vyos.argspec.static_routes.static_routes import Static_routesArgs +from ansible.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/lib/ansible/module_utils/network/vyos/utils/utils.py b/lib/ansible/module_utils/network/vyos/utils/utils.py index 294f5f5e138..3efcaf47463 100644 --- a/lib/ansible/module_utils/network/vyos/utils/utils.py +++ b/lib/ansible/module_utils/network/vyos/utils/utils.py @@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible.module_utils.six import iteritems +from ansible.module_utils.compat import ipaddress def search_obj_in_list(name, lst, key='name'): @@ -86,6 +87,23 @@ 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 @@ -162,3 +180,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/lib/ansible/modules/network/vyos/vyos_static_route.py b/lib/ansible/modules/network/vyos/_vyos_static_route.py similarity index 97% rename from lib/ansible/modules/network/vyos/vyos_static_route.py rename to lib/ansible/modules/network/vyos/_vyos_static_route.py index 58b52837e06..d3a0f057c52 100644 --- a/lib/ansible/modules/network/vyos/vyos_static_route.py +++ b/lib/ansible/modules/network/vyos/_vyos_static_route.py @@ -20,7 +20,7 @@ # ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], + 'status': ['deprecated'], 'supported_by': 'network'} @@ -33,6 +33,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). @@ -42,22 +46,28 @@ options: - 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. default: present choices: ['present', 'absent'] + type: str extends_documentation_fragment: vyos """ diff --git a/lib/ansible/modules/network/vyos/vyos_facts.py b/lib/ansible/modules/network/vyos/vyos_facts.py index cdd71781eb1..bfb12f24f3c 100644 --- a/lib/ansible/modules/network/vyos/vyos_facts.py +++ b/lib/ansible/modules/network/vyos/vyos_facts.py @@ -52,7 +52,7 @@ options: 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', 'firewall_rules'. + 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules'. required: false version_added: "2.9" """ diff --git a/lib/ansible/modules/network/vyos/vyos_static_routes.py b/lib/ansible/modules/network/vyos/vyos_static_routes.py new file mode 100644 index 00000000000..95bb738094b --- /dev/null +++ b/lib/ansible/modules/network/vyos/vyos_static_routes.py @@ -0,0 +1,1147 @@ +#!/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 +version_added: '2.10' +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.module_utils.network.vyos.argspec.static_routes.static_routes import Static_routesArgs +from ansible.module_utils.network.vyos.config.static_routes.static_routes import Static_routes + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [('state', 'merged', ('config',)), + ('state', 'replaced', ('config',)), + ('state', 'overridden', ('config',)), + ('state', 'parsed', ('running_config',))] + mutually_exclusive = [('config', 'running_config')] + + module = AnsibleModule(argument_spec=Static_routesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive) + result = Static_routes(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vyos_static_routes/defaults/main.yaml b/test/integration/targets/vyos_static_routes/defaults/main.yaml new file mode 100644 index 00000000000..164afead284 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/test/integration/targets/vyos_static_routes/meta/main.yaml b/test/integration/targets/vyos_static_routes/meta/main.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/integration/targets/vyos_static_routes/tasks/cli.yaml b/test/integration/targets/vyos_static_routes/tasks/cli.yaml new file mode 100644 index 00000000000..655e51ee63d --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tasks/cli.yaml @@ -0,0 +1,19 @@ +--- +- name: Collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + use_regex: true + register: test_cases + delegate_to: localhost + +- name: Set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: Run test case (connection=network_cli) + include: "{{ test_case_to_run }}" + vars: + ansible_connection: network_cli + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/vyos_static_routes/tasks/main.yaml b/test/integration/targets/vyos_static_routes/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg b/test/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg new file mode 100644 index 00000000000..b2ecd4e9c04 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/_parsed_config.cfg @@ -0,0 +1,6 @@ +set protocols static route 192.0.2.32/28 next-hop '192.0.2.9' +set protocols static route 192.0.2.32/28 next-hop '192.0.2.10' +set protocols static route 192.0.2.32/28 blackhole +set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' +set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' +set protocols static route6 2001:db8:1000::/36 blackhole distance '2' diff --git a/test/integration/targets/vyos_static_routes/tests/cli/_populate.yaml b/test/integration/targets/vyos_static_routes/tests/cli/_populate.yaml new file mode 100644 index 00000000000..c5b02f73eb7 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/_populate.yaml @@ -0,0 +1,14 @@ +--- +- name: Setup + cli_config: + config: "{{ lines }}" + vars: + lines: | + set protocols static route 192.0.2.32/28 next-hop '192.0.2.10' + set protocols static route 192.0.2.32/28 next-hop '192.0.2.9' + set protocols static route 192.0.2.32/28 blackhole + set protocols static route 192.0.2.32/28 + set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1' + set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2' + set protocols static route6 2001:db8:1000::/36 blackhole distance '2' + set protocols static route6 2001:db8:1000::/36 diff --git a/test/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml b/test/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml new file mode 100644 index 00000000000..026ef4580eb --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/_remove_config.yaml @@ -0,0 +1,8 @@ +--- +- name: Remove Config + cli_config: + config: "{{ lines }}" + vars: + lines: | + delete protocols static route + delete protocols static route6 diff --git a/test/integration/targets/vyos_static_routes/tests/cli/deleted.yaml b/test/integration/targets/vyos_static_routes/tests/cli/deleted.yaml new file mode 100644 index 00000000000..b4bad0cc7c9 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/deleted.yaml @@ -0,0 +1,51 @@ +--- +- debug: + msg: "Start vyos_static_routes deleted integration tests ansible_connection={{ ansible_connection }}" + +- include_tasks: _populate.yaml + +- block: + - name: Delete static route based on destiation. + vyos_static_routes: &deleted_dest + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: '192.0.2.32/28' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + state: deleted + register: result + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted_dest['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_dest['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + vyos_static_routes: *deleted_dest + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted_dest['after'] | symmetric_difference(result['before']) |length == 0 }}" + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml b/test/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml new file mode 100644 index 00000000000..3b6c7e017f4 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/deleted_afi.yaml @@ -0,0 +1,47 @@ +--- +- debug: + msg: "Start vyos_static_routes deleted integration tests ansible_connection={{ ansible_connection }}" + +- include_tasks: _populate.yaml + +- block: + - name: Delete static route based on afi. + vyos_static_routes: &deleted_afi + config: + - address_families: + - afi: 'ipv4' + - afi: 'ipv6' + state: deleted + register: result + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_afi_all['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + vyos_static_routes: *deleted_afi + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted_afi_all['after'] | symmetric_difference(result['before']) |length == 0 }}" + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml b/test/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml new file mode 100644 index 00000000000..39e3f14413f --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/deleted_all.yaml @@ -0,0 +1,44 @@ +--- +- debug: + msg: "Start vyos_static_routes deleted integration tests ansible_connection={{ ansible_connection }}" + +- include_tasks: _populate.yaml + +- block: + - name: Delete all the static routes. + vyos_static_routes: &deleted_all + config: + state: deleted + register: result + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted_afi_all['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_afi_all['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + vyos_static_routes: *deleted_all + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted_afi_all['after'] | symmetric_difference(result['before']) |length == 0 }}" + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml b/test/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml new file mode 100644 index 00000000000..976b384e790 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/deleted_nh.yaml @@ -0,0 +1,55 @@ +--- +- debug: + msg: "Start vyos_static_routes deleted integration tests ansible_connection={{ ansible_connection }}" + +- include_tasks: _populate.yaml + +- block: + - name: Delete static route based on next_hop. + vyos_static_routes: &deleted_nh + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: '192.0.2.32/28' + next_hops: + - forward_router_address: '192.0.2.9' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + next_hops: + - forward_router_address: '2001:db8:2000:2::1' + state: deleted + register: result + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted_nh['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_nh['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + vyos_static_routes: *deleted_nh + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted_nh['after'] | symmetric_difference(result['before']) |length == 0 }}" + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml b/test/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml new file mode 100644 index 00000000000..96bec582a55 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/empty_config.yaml @@ -0,0 +1,58 @@ +--- +- debug: + msg: "START vyos_static_routes empty_config integration tests on connection={{ ansible_connection }}" + +- name: Merged with empty config should give appropriate error message + vyos_static_routes: + config: + state: merged + register: result + ignore_errors: true + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state merged' + +- name: Replaced with empty config should give appropriate error message + vyos_static_routes: + config: + state: replaced + register: result + ignore_errors: true + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty config should give appropriate error message + vyos_static_routes: + config: + state: overridden + register: result + ignore_errors: true + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Parsed with empty running_config should give appropriate error message + vyos_static_routes: + running_config: + state: parsed + register: result + ignore_errors: true + +- assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state parsed' + +- name: Rendered with empty config should give appropriate error message + vyos_static_routes: + config: + state: rendered + register: result + ignore_errors: true + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' diff --git a/test/integration/targets/vyos_static_routes/tests/cli/gathered.yaml b/test/integration/targets/vyos_static_routes/tests/cli/gathered.yaml new file mode 100644 index 00000000000..58a4279ec2d --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/gathered.yaml @@ -0,0 +1,31 @@ +--- +- debug: + msg: "START vyos_static_routes gathered integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Merge the provided configuration with the exisiting running configuration + vyos_static_routes: &gathered + config: + state: gathered + register: result + + - name: Assert that gathered dicts was correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['gathered']) |length == 0 }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + vyos_static_routes: *gathered + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/merged.yaml b/test/integration/targets/vyos_static_routes/tests/cli/merged.yaml new file mode 100644 index 00000000000..e9399ad77b6 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/merged.yaml @@ -0,0 +1,61 @@ +--- +- debug: + msg: "START vyos_static_routes merged integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- block: + - name: Merge the provided configuration with the exisiting running configuration + vyos_static_routes: &merged + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: 192.0.2.10 + - forward_router_address: 192.0.2.9 + - address_families: + - afi: 'ipv6' + routes: + - dest: 2001:db8:1000::/36 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 2001:db8:2000:2::1 + - forward_router_address: 2001:db8:2000:2::2 + state: merged + register: result + + - name: Assert that before dicts were correctly generated + assert: + that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dicts was correctly generated + assert: + that: + - "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) + vyos_static_routes: *merged + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/overridden.yaml b/test/integration/targets/vyos_static_routes/tests/cli/overridden.yaml new file mode 100644 index 00000000000..9e838d7bcf7 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/overridden.yaml @@ -0,0 +1,52 @@ +--- +- debug: + msg: "START vyos_static_routes overridden integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Overrides all device configuration with provided configuration + vyos_static_routes: &overridden + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 198.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.18 + state: overridden + register: result + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that correct commands were generated + assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dicts were correctly generated + assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Overrides all device configuration with provided configurations (IDEMPOTENT) + vyos_static_routes: *overridden + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/parsed.yaml b/test/integration/targets/vyos_static_routes/tests/cli/parsed.yaml new file mode 100644 index 00000000000..18e5781e71c --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/parsed.yaml @@ -0,0 +1,39 @@ +--- +- debug: + msg: "START vyos_static_routes parsed integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Gather static_routes facts + vyos_facts: + gather_subset: + - default + gather_network_resources: + - static_routes + register: static_routes_facts + + - name: Provide the running configuration for parsing (config to be parsed) + vyos_static_routes: &parsed + running_config: + "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + register: result + + - name: Assert that correct parsing done + assert: + that: "{{ ansible_facts['network_resources']['static_routes'] | symmetric_difference(result['parsed']) |length == 0 }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + vyos_static_routes: *parsed + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/rendered.yaml b/test/integration/targets/vyos_static_routes/tests/cli/rendered.yaml new file mode 100644 index 00000000000..80b90ebe5a1 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/rendered.yaml @@ -0,0 +1,49 @@ +--- +- debug: + msg: "START vyos_static_routes rendered integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Structure provided configuration into device specific commands + vyos_static_routes: &rendered + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: 192.0.2.10 + - forward_router_address: 192.0.2.9 + - address_families: + - afi: 'ipv6' + routes: + - dest: 2001:db8:1000::/36 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 2001:db8:2000:2::1 + - forward_router_address: 2001:db8:2000:2::2 + state: rendered + register: result + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ rendered['commands'] | symmetric_difference(result['rendered']) |length == 0 }}" + + - name: Structure provided configuration into device specific commands (IDEMPOTENT) + vyos_static_routes: *rendered + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/replaced.yaml b/test/integration/targets/vyos_static_routes/tests/cli/replaced.yaml new file mode 100644 index 00000000000..1daf4069456 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/replaced.yaml @@ -0,0 +1,56 @@ +--- +- debug: + msg: "START vyos_static_routes replaced integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Replace device configurations of listed static routes with provided configurations + vyos_static_routes: &replaced + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 192.0.2.7 + - forward_router_address: 192.0.2.8 + - forward_router_address: 192.0.2.9 + state: replaced + register: result + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that before dicts are correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that after dict is correctly generated + assert: + that: + - "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Replace device configurations of listed static routes with provided configurarions (IDEMPOTENT) + vyos_static_routes: *replaced + register: result + + - name: Assert that task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dict is correctly generated + assert: + that: + - "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/tests/cli/rtt.yaml b/test/integration/targets/vyos_static_routes/tests/cli/rtt.yaml new file mode 100644 index 00000000000..81ece8b3831 --- /dev/null +++ b/test/integration/targets/vyos_static_routes/tests/cli/rtt.yaml @@ -0,0 +1,71 @@ +--- +- debug: + msg: "START vyos_static_routes round trip integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- block: + - name: Apply the provided configuration (base config) + vyos_static_routes: + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: 192.0.2.10 + - forward_router_address: 192.0.2.9 + - address_families: + - afi: 'ipv6' + routes: + - dest: 2001:db8:1000::/36 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 2001:db8:2000:2::1 + - forward_router_address: 2001:db8:2000:2::2 + + state: merged + register: base_config + + - name: Gather static_routes facts + vyos_facts: + gather_subset: + - default + gather_network_resources: + - static_routes + + - name: Apply the provided configuration (config to be reverted) + vyos_static_routes: + config: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 192.0.2.7 + - forward_router_address: 192.0.2.8 + - forward_router_address: 192.0.2.9 + state: merged + register: result + + - name: Assert that changes were applied + assert: + that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Revert back to base config using facts round trip + vyos_static_routes: + config: "{{ ansible_facts['network_resources']['static_routes'] }}" + state: overridden + register: revert + + - name: Assert that config was reverted + assert: + that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_static_routes/vars/main.yaml b/test/integration/targets/vyos_static_routes/vars/main.yaml new file mode 100644 index 00000000000..eeed9bbe90e --- /dev/null +++ b/test/integration/targets/vyos_static_routes/vars/main.yaml @@ -0,0 +1,163 @@ +--- +merged: + before: [] + + commands: + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'" + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'" + - "set protocols static route 192.0.2.32/28 blackhole" + - "set protocols static route 192.0.2.32/28" + - "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'" + - "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'" + - "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'" + - "set protocols static route6 2001:db8:1000::/36" + + after: + - address_families: + - afi: 'ipv4' + routes: + - dest: '192.0.2.32/28' + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: '192.0.2.9' + - forward_router_address: '192.0.2.10' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '2001:db8:2000:2::1' + - forward_router_address: '2001:db8:2000:2::2' + +populate: + - address_families: + - afi: 'ipv4' + routes: + - dest: '192.0.2.32/28' + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: '192.0.2.9' + - forward_router_address: '192.0.2.10' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '2001:db8:2000:2::1' + - forward_router_address: '2001:db8:2000:2::2' + +replaced: + commands: + - "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.10'" + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.7'" + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.8'" + - "set protocols static route 192.0.2.32/28 blackhole distance '2'" + + after: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: 192.0.2.7 + - forward_router_address: 192.0.2.8 + - forward_router_address: 192.0.2.9 + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '2001:db8:2000:2::1' + - forward_router_address: '2001:db8:2000:2::2' +overridden: + commands: + - "delete protocols static route 192.0.2.32/28" + - "delete protocols static route6 2001:db8:1000::/36" + - "set protocols static route 198.0.2.48/28 next-hop '192.0.2.18'" + - "set protocols static route 198.0.2.48/28" + + after: + - address_families: + - afi: 'ipv4' + routes: + - dest: 198.0.2.48/28 + next_hops: + - forward_router_address: '192.0.2.18' + +rendered: + commands: + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.10'" + - "set protocols static route 192.0.2.32/28 next-hop '192.0.2.9'" + - "set protocols static route 192.0.2.32/28 blackhole" + - "set protocols static route 192.0.2.32/28" + - "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'" + - "set protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::2'" + - "set protocols static route6 2001:db8:1000::/36 blackhole distance '2'" + - "set protocols static route6 2001:db8:1000::/36" + +deleted_dest: + + commands: + - "delete protocols static route 192.0.2.32/28" + - "delete protocols static route6 2001:db8:1000::/36" + + after: [] + +deleted_nh: + + commands: + - "delete protocols static route 192.0.2.32/28 next-hop '192.0.2.9'" + - "delete protocols static route6 2001:db8:1000::/36 next-hop '2001:db8:2000:2::1'" + + after: + - address_families: + - afi: 'ipv4' + routes: + - dest: '192.0.2.32/28' + blackhole_config: + type: 'blackhole' + next_hops: + - forward_router_address: '192.0.2.10' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '2001:db8:2000:2::2' + +deleted_afi_all: + + commands: + - "delete protocols static route" + - "delete protocols static route6" + after: [] + +round_trip: + after: + - address_families: + - afi: 'ipv4' + routes: + - dest: 192.0.2.32/28 + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '192.0.2.7' + - forward_router_address: '192.0.2.8' + - forward_router_address: '192.0.2.9' + - forward_router_address: '192.0.2.10' + - afi: 'ipv6' + routes: + - dest: '2001:db8:1000::/36' + blackhole_config: + distance: 2 + next_hops: + - forward_router_address: '2001:db8:2000:2::1' + - forward_router_address: '2001:db8:2000:2::2' diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 7dd5c7aa158..5c6a185e8c3 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -6436,16 +6436,16 @@ lib/ansible/modules/network/vyos/vyos_logging.py validate-modules:undocumented-p lib/ansible/modules/network/vyos/vyos_ping.py validate-modules:doc-default-does-not-match-spec lib/ansible/modules/network/vyos/vyos_ping.py validate-modules:doc-required-mismatch lib/ansible/modules/network/vyos/vyos_ping.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/network/vyos/vyos_static_route.py future-import-boilerplate -lib/ansible/modules/network/vyos/vyos_static_route.py metaclass-boilerplate -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:doc-choices-do-not-match-spec -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:doc-default-does-not-match-spec -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:doc-elements-mismatch -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:doc-missing-type -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:doc-required-mismatch -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:missing-suboption-docs -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/network/vyos/vyos_static_route.py validate-modules:undocumented-parameter +lib/ansible/modules/network/vyos/_vyos_static_route.py future-import-boilerplate +lib/ansible/modules/network/vyos/_vyos_static_route.py metaclass-boilerplate +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:undocumented-parameter +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:doc-default-does-not-match-spec +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:doc-choices-do-not-match-spec +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:doc-required-mismatch +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:parameter-type-not-in-doc +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:doc-missing-type +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:doc-elements-mismatch +lib/ansible/modules/network/vyos/_vyos_static_route.py validate-modules:missing-suboption-docs lib/ansible/modules/network/vyos/vyos_system.py future-import-boilerplate lib/ansible/modules/network/vyos/vyos_system.py metaclass-boilerplate lib/ansible/modules/network/vyos/vyos_system.py validate-modules:doc-default-does-not-match-spec diff --git a/test/units/modules/network/vyos/fixtures/vyos_static_routes_config.cfg b/test/units/modules/network/vyos/fixtures/vyos_static_routes_config.cfg new file mode 100644 index 00000000000..0411dc9a8e9 --- /dev/null +++ b/test/units/modules/network/vyos/fixtures/vyos_static_routes_config.cfg @@ -0,0 +1,2 @@ +'set protocols static route 192.0.2.32/28 next-hop 192.0.2.9' +'set protocols static route 192.0.2.32/28 next-hop 192.0.2.10' diff --git a/test/units/modules/network/vyos/test_vyos_static_route.py b/test/units/modules/network/vyos/test_vyos_static_route.py index 405b633992b..4a592ceacaa 100644 --- a/test/units/modules/network/vyos/test_vyos_static_route.py +++ b/test/units/modules/network/vyos/test_vyos_static_route.py @@ -20,22 +20,22 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type from units.compat.mock import patch -from ansible.modules.network.vyos import vyos_static_route +from ansible.modules.network.vyos import _vyos_static_route from units.modules.utils import set_module_args from .vyos_module import TestVyosModule class TestVyosStaticRouteModule(TestVyosModule): - module = vyos_static_route + module = _vyos_static_route def setUp(self): super(TestVyosStaticRouteModule, self).setUp() - self.mock_get_config = patch('ansible.modules.network.vyos.vyos_static_route.get_config') + self.mock_get_config = patch('ansible.modules.network.vyos._vyos_static_route.get_config') self.get_config = self.mock_get_config.start() - self.mock_load_config = patch('ansible.modules.network.vyos.vyos_static_route.load_config') + self.mock_load_config = patch('ansible.modules.network.vyos._vyos_static_route.load_config') self.load_config = self.mock_load_config.start() def tearDown(self): diff --git a/test/units/modules/network/vyos/test_vyos_static_routes.py b/test/units/modules/network/vyos/test_vyos_static_routes.py new file mode 100644 index 00000000000..209d407c2e0 --- /dev/null +++ b/test/units/modules/network/vyos/test_vyos_static_routes.py @@ -0,0 +1,202 @@ +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from units.compat.mock import patch, MagicMock +from ansible.modules.network.vyos import vyos_static_routes +from units.modules.utils import set_module_args +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosStaticRoutesModule(TestVyosModule): + + module = vyos_static_routes + + def setUp(self): + super(TestVyosStaticRoutesModule, self).setUp() + self.mock_get_config = patch('ansible.module_utils.network.common.network.Config.get_config') + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch('ansible.module_utils.network.common.network.Config.load_config') + self.load_config = self.mock_load_config.start() + + self.mock_get_resource_connection_config = patch('ansible.module_utils.network.common.cfg.base.get_resource_connection') + self.get_resource_connection_config = self.mock_get_resource_connection_config.start() + + self.mock_get_resource_connection_facts = patch('ansible.module_utils.network.common.facts.facts.get_resource_connection') + self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() + + self.mock_execute_show_command = patch('ansible.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data') + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestVyosStaticRoutesModule, self).tearDown() + self.mock_get_resource_connection_config.stop() + self.mock_get_resource_connection_facts.stop() + self.mock_get_config.stop() + self.mock_load_config.stop() + self.mock_execute_show_command.stop() + + def load_fixtures(self, commands=None): + def load_from_file(*args, **kwargs): + return load_fixture('vyos_static_routes_config.cfg') + self.execute_show_command.side_effect = load_from_file + + def test_vyos_static_routes_merged(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.48/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="merged")) + commands = ['set protocols static route 192.0.2.48/28', + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_static_routes_merged_idempotent(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.32/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="merged")) + self.execute_module(changed=False, commands=[]) + + def test_vyos_static_routes_replaced(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.48/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="replaced")) + commands = ["set protocols static route 192.0.2.48/28", + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_static_routes_replaced_idempotent(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.32/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="replaced")) + + self.execute_module(changed=False, commands=[]) + + def test_vyos_static_routes_overridden(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.48/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="overridden")) + commands = ['delete protocols static route 192.0.2.32/28', + 'set protocols static route 192.0.2.48/28', + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", + "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_static_routes_overridden_idempotent(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict( + afi='ipv4', + routes=[ + dict( + dest='192.0.2.32/28', + next_hops=[ + dict( + forward_router_address='192.0.2.9'), + dict(forward_router_address='192.0.2.10') + ]) + ]) + ]) + ], + state="overridden")) + self.execute_module(changed=False, commands=[]) + + def test_vyos_static_routes_deleted(self): + set_module_args( + dict(config=[ + dict(address_families=[ + dict(afi='ipv4', routes=[dict(dest='192.0.2.32/28')]) + ]) + ], + state="deleted")) + commands = ['delete protocols static route 192.0.2.32/28'] + self.execute_module(changed=True, commands=commands)