eos static_routes module added (#65480)

* Adding files for RM static_routes

* Added Integration tests

* Added Unit testcases

* Addressed review comments

* corrected lint errors

* corrected documentation errors

* Lint errors

* corrected test/sanity

* corrected documentation for deprecation

* corrected case sensitivity

* Again Documentation eroor

* Lint errors again

* corrected deprecated module in ignoretxt

* added new gethered,rendered,parsed state checks to unit test

* New code broke the old flow-fixed

* Lint errs

* Added check for running_config

* Add rtt testcase

* Fixed unit tcs

* lint errors

* lint errors

* Modified replaced operation behavior

* updated documentation and tests for delete opration

* fixed shippable errors

* review comments and flake8 error fix

* syntax errors fixed
This commit is contained in:
GomathiselviS 2020-02-25 07:50:28 -05:00 committed by GitHub
parent 6f0924a892
commit 726d6455d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 3380 additions and 8 deletions

View file

@ -0,0 +1,115 @@
#
# -*- 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 eos_static_routes module
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
class Static_routesArgs(object):
"""The arg spec for the eos_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': {
'dest': {
'required': True,
'type': 'str'
},
'next_hops': {
'elements': 'dict',
'options': {
'admin_distance': {
'type': 'int'
},
'description': {
'type': 'str'
},
'forward_router_address': {
'type': 'str'
},
'interface': {
'type': 'str'
},
'nexthop_grp': {
'type': 'str'
},
'mpls_label': {
'type': 'int'
},
'tag': {
'type': 'int'
},
'track': {
'type': 'str'
},
'vrf': {
'type': 'str'
}
},
'type': 'list'
}
},
'type': 'list'
}
},
'type': 'list'
},
'vrf': {
'type': 'str'
}
},
'type': 'list'
},
'running_config': {
'type': 'str'
},
'state': {
'choices': [
'deleted', 'merged', 'overridden', 'replaced', 'gathered',
'rendered', 'parsed'
],
'default':
'merged',
'type':
'str'
}
} # pylint: disable=C0301

View file

@ -0,0 +1,390 @@
#
# -*- 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 eos_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
import re
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import remove_empties
from ansible.module_utils.network.eos.facts.facts import Facts
class Static_routes(ConfigBase):
"""
The eos_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:
for command in commands:
self._connection.edit_config(command)
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':
if not self._module.params['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=self._module.params['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
"""
commands = []
onbox_configs = []
for h in existing_static_routes_facts:
return_command = add_commands(h)
for command in return_command:
onbox_configs.append(command)
config = self._module.params.get('config')
want = []
if config:
for w in config:
want.append(remove_empties(w))
have = existing_static_routes_facts
resp = self.set_state(want, have)
for want_config in resp:
if want_config not in onbox_configs:
commands.append(want_config)
return commands
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') and not want:
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state))
state = self._module.params['state']
if state == 'overridden':
commands = self._state_overridden(want, have)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged' or self.state == 'rendered':
commands = self._state_merged(want, have)
elif state == 'replaced':
commands = self._state_replaced(want, have)
return commands
@staticmethod
def _state_replaced(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 = []
haveconfigs = []
vrf = get_vrf(want)
dest = get_dest(want)
for h in have:
return_command = add_commands(h)
for command in return_command:
for d in dest:
if d in command:
if vrf is None:
if "vrf" not in command:
haveconfigs.append(command)
else:
if vrf in command:
haveconfigs.append(command)
wantconfigs = set_commands(want, have)
removeconfigs = list(set(haveconfigs) - set(wantconfigs))
for command in removeconfigs:
commands.append("no " + command)
for wantcmd in wantconfigs:
commands.append(wantcmd)
return commands
@staticmethod
def _state_overridden(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 = []
haveconfigs = []
for h in have:
return_command = add_commands(h)
for command in return_command:
haveconfigs.append(command)
wantconfigs = set_commands(want, have)
idempotentconfigs = list(set(haveconfigs) - set(wantconfigs))
if not idempotentconfigs:
return idempotentconfigs
removeconfigs = list(set(haveconfigs) - set(wantconfigs))
for command in removeconfigs:
commands.append("no " + command)
for wantcmd in wantconfigs:
commands.append(wantcmd)
return commands
@staticmethod
def _state_merged(want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
return set_commands(want, have)
@staticmethod
def _state_deleted(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 not want:
for h in have:
return_command = add_commands(h)
for command in return_command:
command = "no " + command
commands.append(command)
else:
for w in want:
return_command = del_commands(w, have)
for command in return_command:
commands.append(command)
return commands
def set_commands(want, have):
commands = []
for w in want:
return_command = add_commands(w)
for command in return_command:
commands.append(command)
return commands
def add_commands(want):
commandset = []
if not want:
return commandset
vrf = want["vrf"] if "vrf" in want.keys() and want["vrf"] is not None else None
for address_family in want["address_families"]:
for route in address_family["routes"]:
for next_hop in route["next_hops"]:
commands = []
if address_family["afi"] == "ipv4":
commands.append('ip route')
else:
commands.append('ipv6 route')
if vrf:
commands.append(' vrf ' + vrf)
if not re.search(r'/', route["dest"]):
mask = route["dest"].split()[1]
cidr = get_net_size(mask)
commands.append(' ' + route["dest"].split()[0] + '/' + cidr)
else:
commands.append(' ' + route["dest"])
if "interface" in next_hop.keys():
commands.append(' ' + next_hop["interface"])
if "nexthop_grp" in next_hop.keys():
commands.append(' Nexthop-Group' + ' ' + next_hop["nexthop_grp"])
if "forward_router_address" in next_hop.keys():
commands.append(' ' + next_hop["forward_router_address"])
if "mpls_label" in next_hop.keys():
commands.append(' label ' + str(next_hop["mpls_label"]))
if "track" in next_hop.keys():
commands.append(' track ' + next_hop["track"])
if "admin_distance" in next_hop.keys():
commands.append(' ' + str(next_hop["admin_distance"]))
if "description" in next_hop.keys():
commands.append(' name ' + str(next_hop["description"]))
if "tag" in next_hop.keys():
commands.append(' tag ' + str(next_hop["tag"]))
config_commands = "".join(commands)
commandset.append(config_commands)
return commandset
def del_commands(want, have):
commandset = []
haveconfigs = []
for h in have:
return_command = add_commands(h)
for command in return_command:
command = "no " + command
haveconfigs.append(command)
if want is None or "address_families" not in want.keys():
commandset = haveconfigs
if "address_families" not in want.keys() and "vrf" in want.keys():
commandset = []
for command in haveconfigs:
if want["vrf"] in command:
commandset.append(command)
elif want is not None and "vrf" not in want.keys() and "address_families" not in want.keys():
commandset = []
for command in haveconfigs:
if "vrf" not in command:
commandset.append(command)
elif want["address_families"]:
vrf = want["vrf"] if "vrf" in want.keys() and want["vrf"] else None
for address_family in want["address_families"]:
if "routes" not in address_family.keys():
for command in haveconfigs:
afi = "ip " if address_family["afi"] == "ipv4" else "ipv6"
if afi in command:
if vrf:
if vrf in command:
commandset.append(command)
else:
commandset.append(command)
else:
for route in address_family["routes"]:
if not re.search(r'/', route["dest"]):
mask = route["dest"].split()[1]
cidr = get_net_size(mask)
destination = route["dest"].split()[0] + '/' + cidr
else:
destination = route["dest"]
if "next_hops" not in route.keys():
for command in haveconfigs:
if destination in command:
if vrf:
if vrf in command:
commandset.append(command)
else:
commandset.append(command)
else:
for next_hop in route["next_hops"]:
commands = []
if address_family["afi"] == "ipv4":
commands.append('no ip route')
else:
commands.append('no ipv6 route')
if vrf:
commands.append(' vrf ' + vrf)
commands.append(' ' + destination)
if "interface" in next_hop.keys():
commands.append(' ' + next_hop["interface"])
if "nexhop_grp" in next_hop.keys():
commands.append(' Nexthop-Group' + ' ' + next_hop["nexthop_grp"])
if "forward_router_address" in next_hop.keys():
commands.append(' ' + next_hop["forward_router_address"])
if "mpls_label" in next_hop.keys():
commands.append(' label ' + str(next_hop["mpls_label"]))
if "track" in next_hop.keys():
commands.append(' track ' + next_hop["track"])
if "admin_distance" in next_hop.keys():
commands.append(' ' + str(next_hop["admin_distance"]))
if "description" in next_hop.keys():
commands.append(' name ' + str(next_hop["description"]))
if "tag" in next_hop.keys():
commands.append(' tag ' + str(next_hop["tag"]))
config_commands = "".join(commands)
commandset.append(config_commands)
return commandset
def get_net_size(netmask):
binary_str = ''
netmask = netmask.split('.')
for octet in netmask:
binary_str += bin(int(octet))[2:].zfill(8)
return str(len(binary_str.rstrip('0')))
def get_vrf(config):
vrf = ""
for c in config:
vrf = c["vrf"] if "vrf" in c.keys() and c["vrf"] else None
return vrf
def get_dest(config):
dest = []
for c in config:
for address_family in c["address_families"]:
for route in address_family["routes"]:
dest.append(route['dest'])
return dest

View file

@ -23,6 +23,7 @@ from ansible.module_utils.network.eos.facts.vlans.vlans import VlansFacts
from ansible.module_utils.network.eos.facts.legacy.base import Default, Hardware, Config, Interfaces
from ansible.module_utils.network.eos.facts.acl_interfaces.acl_interfaces import Acl_interfacesFacts
from ansible.module_utils.network.eos.facts.acls.acls import AclsFacts
from ansible.module_utils.network.eos.facts.static_routes.static_routes import Static_routesFacts
FACT_LEGACY_SUBSETS = dict(
@ -43,6 +44,7 @@ FACT_RESOURCE_SUBSETS = dict(
vlans=VlansFacts,
acl_interfaces=Acl_interfacesFacts,
acls=AclsFacts,
static_routes=Static_routesFacts,
)

View file

@ -0,0 +1,202 @@
#
# -*- 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 eos 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
import re
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.eos.argspec.static_routes.static_routes import Static_routesArgs
class Static_routesFacts(object):
""" The eos 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('show running-config | grep route')
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)
# split the config into instances of the resource
resource_delim = 'ip.* route'
find_pattern = r'(?:^|\n)%s.*?(?=(?:^|\n)%s|$)' % (resource_delim,
resource_delim)
resources = [p.strip() for p in re.findall(find_pattern, data)]
resources_without_vrf = []
resource_vrf = {}
for resource in resources:
if resource and "vrf" not in resource:
resources_without_vrf.append(resource)
else:
vrf = re.search(r'ip(v6)* route vrf (.*?) .*', resource)
if vrf.group(2) in resource_vrf.keys():
vrf_val = resource_vrf[vrf.group(2)]
vrf_val.append(resource)
resource_vrf.update({vrf.group(2): vrf_val})
else:
resource_vrf.update({vrf.group(2): [resource]})
resources_without_vrf = ["\n".join(resources_without_vrf)]
for vrf in resource_vrf.keys():
vrflist = ["\n".join(resource_vrf[vrf])]
resource_vrf.update({vrf: vrflist})
objs = []
for resource in resources_without_vrf:
if resource:
obj = self.render_config(self.generated_spec, resource)
if obj:
objs.append(obj)
for resource in resource_vrf.keys():
if resource:
obj = self.render_config(self.generated_spec, resource_vrf[resource][0])
if obj:
objs.append(obj)
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, spec, 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
"""
config = deepcopy(spec)
address_family_dict = {}
route_dict = {}
dest_list = []
afi_list = []
vrf_list = []
routes = []
config["address_families"] = []
next_hops = {}
interface_list = ["Ethernet", "Loopback", "Management",
"Port-Channel", "Tunnel", "Vlan", "Vxlan", "vtep"]
conf_list = conf.split('\n')
for conf_elem in conf_list:
matches = re.findall(r'(ip|ipv6) route ([\d\.\/:]+|vrf) (.+)$', conf_elem)
if matches:
remainder = matches[0][2].split()
route_update = False
if matches[0][1] == "vrf":
vrf = remainder.pop(0)
# new vrf
if vrf not in vrf_list and vrf_list:
route_dict.update({"next_hops": next_hops})
routes.append(route_dict)
address_family_dict.update({"routes": routes})
config["address_families"].append(address_family_dict)
route_update = True
config.update({"vrf": vrf})
vrf_list.append(vrf)
dest = remainder.pop(0)
else:
config["vrf"] = None
dest = matches[0][1]
afi = "ipv4" if matches[0][0] == "ip" else "ipv6"
if afi not in afi_list:
if afi_list and not route_update:
# new afi and not the first updating all prev configs
route_dict.update({"next_hops": next_hops})
routes.append(route_dict)
address_family_dict.update({"routes": routes})
config["address_families"].append(address_family_dict)
route_update = True
address_family_dict = {}
address_family_dict.update({"afi": afi})
routes = []
afi_list.append(afi)
# To check the format of the dest
prefix = re.search(r'/', dest)
if not prefix:
dest = dest + ' ' + remainder.pop(0)
if dest not in dest_list:
# For new dest and not the first dest
if dest_list and not route_update:
route_dict.update({"next_hops": next_hops})
routes.append(route_dict)
dest_list.append(dest)
next_hops = []
route_dict = {}
route_dict.update({"dest": dest})
nexthops = {}
nxthop_addr = re.search(r'[\.\:]', remainder[0])
if nxthop_addr:
nexthops.update({"interface": remainder.pop(0)})
if remainder and remainder[0] == "label":
nexthops.update({"mpls_label": remainder.pop(1)})
remainder.pop(0)
elif re.search(r'Nexthop-Group', remainder[0]):
nexthops.update({"nexthop_grp": remainder.pop(1)})
remainder.pop(0)
else:
interface = remainder.pop(0)
if interface in interface_list:
interface = interface + " " + remainder.pop(0)
nexthops.update({"interface": interface})
for attribute in remainder:
forward_addr = re.search(r'([\dA-Fa-f]+[:\.]+)+[\dA-Fa-f]+', attribute)
if forward_addr:
nexthops.update({"forward_router_address": remainder.pop(remainder.index(attribute))})
for attribute in remainder:
for params in ["tag", "name", "track"]:
if attribute == params:
keyname = params
if attribute == "name":
keyname = "description"
nexthops.update({keyname: remainder.pop(remainder.index(attribute) + 1)})
remainder.pop(remainder.index(attribute))
if remainder:
metric = re.search(r'\d+', remainder[0])
if metric:
nexthops.update({"admin_distance": remainder.pop(0)})
next_hops.append(nexthops)
route_dict.update({"next_hops": next_hops})
routes.append(route_dict)
address_family_dict.update({"routes": routes})
config["address_families"].append(address_family_dict)
return utils.remove_empties(config)

View file

@ -9,7 +9,7 @@ __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'status': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
@ -21,6 +21,10 @@ short_description: Manage static IP routes on Arista EOS network devices
description:
- This module provides declarative management of static
IP routes on Arista EOS network devices.
deprecated:
removed_in: '2.13'
alternative: eos_static_routes
why: Updated modules with more functionality
notes:
- Tested against EOS 4.15
options:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
---
testcase: "[^_].*"
test_items: []

View file

@ -0,0 +1,2 @@
dependencies:
- prepare_eos_tests

View file

@ -0,0 +1,17 @@
---
- 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 cases (connection=network_cli)
include: "{{ test_case_to_run }} ansible_connection=network_cli"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,16 @@
---
- name: collect all eapi test cases
find:
paths: "{{ role_path }}/tests/eapi"
patterns: "{{ testcase }}.yaml"
delegate_to: localhost
register: test_cases
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test cases (connection=httpapi)
include: "{{ test_case_to_run }} ansible_connection=httpapi"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,3 @@
---
- { include: cli.yaml, tags: ['cli'] }
- { include: eapi.yaml, tags: ['eapi'] }

View file

@ -0,0 +1,7 @@
ip route 10.1.1.0/24 Management1
ip route 10.1.1.0/24 Ethernet1 20.1.1.3 track bfd 200
ip route 10.50.0.0/16 Management1
ip route 23.1.0.0/16 Nexthop-Group testgrp tag 42
ip route vrf testvrf 120.1.1.0/24 Ethernet1 23
ip route vrf vrftest1 77.77.1.0/24 33.1.1.1
ipv6 route 1000:10::/64 Ethernet1 67 tag 98

View file

@ -0,0 +1,16 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
become: yes
vars:
lines: |
vrf definition testvrf
vrf definition vrftest1
ip route 10.1.1.0/24 Management1
ip route 10.1.1.0/24 Ethernet1 20.1.1.3 track bfd 200
ip route 10.50.0.0/16 Management1
ip route 23.1.0.0/16 Nexthop-Group testgrp tag 42
ip route vrf testvrf 120.1.1.0/24 Ethernet1 23
ip route vrf vrftest1 77.77.1.0/24 33.1.1.1
ipv6 route 1000:10::/64 Ethernet1 67 tag 98

View file

@ -0,0 +1,20 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
become: yes
vars:
lines: |
no vrf definition testvrf
no vrf definition vrftest1
no ip route 10.1.1.0/24 Management1
no ip route 10.1.1.0/24 Ethernet1 20.1.1.3 track bfd 200
no ip route 10.50.0.0/16 Management1
no ip route 23.1.0.0/16 Nexthop-Group testgrp tag 42
no ip route 155.55.1.0/24 Nexthop-Group testgrp tag 100
no ip route 122.1.19.0/24 Nexthop-Group testgrp 21
no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23
no ip route vrf vrftest1 77.77.1.0/24 33.1.1.1
no ipv6 route 1000:10::/64 Ethernet1 67 tag 98
no ipv6 route vrf testvrf 1120:10::/64 Ethernet1 55
no ipv6 route 1000:10::/64 Ethernet1 55

View file

@ -0,0 +1,224 @@
---
- debug:
msg: "Start eos_static_routes deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- set_fact:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- set_fact:
config1:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- interface: Ethernet1
admin_distance: 23
vrf: testvrf
- set_fact:
config2:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
vrf: testvrf
- set_fact:
config3:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- interface: Ethernet1
admin_distance: 23
vrf: testvrf
- name: Delete attributes of given static routes - dest specific.
eos_static_routes: &deleted
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv4'
routes:
- dest: '120.1.1.0/24'
- address_families:
- afi: 'ipv6'
routes:
- dest: '1000:10::/64'
state: deleted
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []"
- '"no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23" in result.commands'
- '"no ipv6 route 1000:10::/64 Ethernet1 67 tag 98" in result.commands'
become: yes
- name: Idempotency check
eos_static_routes: *deleted
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- include_tasks: _populate.yaml
- name: Delete attributes of given static routes - afi specific.
eos_static_routes:
config:
- address_families:
- afi: 'ipv6'
state: deleted
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config1) == []"
- '"no ipv6 route 1000:10::/64 Ethernet1 67 tag 98" in result.commands'
become: yes
- name: Delete attributes of given static routes - vrf specific.
eos_static_routes:
config:
- vrf: vrftest1
state: deleted
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config2) == []"
- '"no ip route vrf vrftest1 77.77.1.0/24 33.1.1.1" in result.commands'
become: yes
- name: Delete attributes of given static routes - nexthop specific.
eos_static_routes:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
state: deleted
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config3) == []"
- '"no ip route 10.1.1.0/24 Management1" in result.commands'
become: yes
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,75 @@
---
- debug:
msg: "START eos_static_routes gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Gathered the provided configuration with the exisiting running configuration
eos_static_routes: &gathered
config:
state: gathered
become: yes
register: result
- set_fact:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 67
interface: Ethernet1
tag: 98
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
vrf: testvrf
- name: Assert that gathered dicts was correctly generated
assert:
that:
- " config | symmetric_difference(result['gathered']) == []"
- name: Gather the existing running configuration (IDEMPOTENT)
eos_static_routes: *gathered
become: yes
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,102 @@
---
- debug:
msg: "Start eos_static_routes merged integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- set_fact:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- dest: 155.55.1.0/24
next_hops:
- nexthop_grp: testgrp
tag: 100
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 67
interface: Ethernet1
tag: 98
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
- afi: ipv6
routes:
- dest: 1120:10::/64
next_hops:
- admin_distance: 55
interface: Ethernet1
vrf: testvrf
- name: merge attributes of given static routes.
eos_static_routes: &merged
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv6'
routes:
- dest: '1120:10::/64'
next_hops:
- interface: Ethernet1
admin_distance: 55
- address_families:
- afi: 'ipv4'
routes:
- dest: '155.55.1.0/24'
next_hops:
- nexthop_grp: testgrp
tag: 100
state: merged
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []"
- '"ipv6 route vrf testvrf 1120:10::/64 Ethernet1 55" in result.commands'
- '"ip route 155.55.1.0/24 Nexthop-Group testgrp tag 100" in result.commands'
become: yes
- name: Idempotency check
eos_static_routes: *merged
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,61 @@
---
- debug:
msg: "Start eos_static_routes merged integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- set_fact:
config:
- address_families:
- afi: ipv6
routes:
- dest: 1120:10::/64
next_hops:
- admin_distance: 55
interface: Ethernet1
vrf: testvrf
- name: Override attributes of given static routes.
eos_static_routes: &overridden
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv6'
routes:
- dest: '1120:10::/64'
next_hops:
- interface: Ethernet1
admin_distance: 55
state: overridden
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []"
- "result.commands|length == 8"
- '"no ipv6 route 1000:10::/64 Ethernet1 67 tag 98" in result.commands'
- '"no ip route 23.1.0.0/16 Nexthop-Group testgrp tag 42" in result.commands'
- '"no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23" in result.commands'
- '"no ip route 10.50.0.0/16 Management1" in result.commands'
- '"no ip route 10.1.1.0/24 Management1" in result.commands'
- '"no ip route 10.1.1.0/24 Ethernet1 20.1.1.3 track bfd 200" in result.commands'
- '"no ip route vrf vrftest1 77.77.1.0/24 33.1.1.1" in result.commands'
- '"ipv6 route vrf testvrf 1120:10::/64 Ethernet1 55" in result.commands'
become: yes
- name: Idempotency check
eos_static_routes: *overridden
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,37 @@
---
- debug:
msg: "START eos_static_routes parsed integration tests on connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- name: Gather static_routes facts
eos_facts:
gather_subset:
- default
gather_network_resources:
- static_routes
become: yes
register: static_routes_facts
- name: Provide the running configuration for parsing (config to be parsed)
eos_static_routes: &parsed
running_config:
"{{ lookup('file', '_parsed.cfg') }}"
state: parsed
become: yes
register: result
- assert:
that:
- "{{ ansible_facts['network_resources']['static_routes'] | symmetric_difference(result['parsed']) |length == 0 }}"
- name: Gather the existing running configuration (IDEMPOTENT)
eos_static_routes: *parsed
become: yes
register: result
- assert:
that:
- "result['changed'] == false"
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,53 @@
---
- debug:
msg: "START eos_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
eos_static_routes: &rendered
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv6'
routes:
- dest: '1120:10::/64'
next_hops:
- interface: Ethernet1
admin_distance: 55
- address_families:
- afi: 'ipv4'
routes:
- dest: '155.55.1.0/24'
next_hops:
- nexthop_grp: testgrp
tag: 100
state: rendered
become: yes
register: result
- name: Assert that correct set of commands were generated
vars:
lines:
- ipv6 route vrf testvrf 1120:10::/64 Ethernet1 55
- ip route 155.55.1.0/24 Nexthop-Group testgrp tag 100
assert:
that:
- "{{ lines | symmetric_difference(result['rendered']) |length == 0 }}"
- name: Structure provided configuration into device specific commands (IDEMPOTENT)
eos_static_routes: *rendered
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,90 @@
---
- debug:
msg: "Start eos_static_routes merged integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- set_fact:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 67
interface: Ethernet1
tag: 98
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 55
interface: Ethernet1
vrf: testvrf
- name: Replace attributes of given static routes.
eos_static_routes: &replaced
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv6'
routes:
- dest: '1000:10::/64'
next_hops:
- interface: Ethernet1
admin_distance: 55
state: replaced
become: yes
register: result
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []"
- '"ipv6 route vrf testvrf 1000:10::/64 Ethernet1 55" in result.commands'
become: yes
- name: Idempotency check
eos_static_routes: *replaced
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,177 @@
---
- debug:
msg: "Start eos_static_routes merged integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- set_fact:
config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- dest: 155.55.1.0/24
next_hops:
- nexthop_grp: testgrp
tag: 100
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 67
interface: Ethernet1
tag: 98
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
- afi: ipv6
routes:
- dest: 1120:10::/64
next_hops:
- admin_distance: 55
interface: Ethernet1
vrf: testvrf
revert_config:
- address_families:
- afi: ipv4
routes:
- dest: 10.1.1.0/24
next_hops:
- interface: Management1
- admin_distance: 200
forward_router_address: 20.1.1.3
interface: Ethernet1
track: bfd
- dest: 10.50.0.0/16
next_hops:
- interface: Management1
- dest: 23.1.0.0/16
next_hops:
- nexthop_grp: testgrp
tag: 42
- dest: 122.1.19.0/24
next_hops:
- admin_distance: 21
nexthop_grp: testgrp
- dest: 155.55.1.0/24
next_hops:
- nexthop_grp: testgrp
tag: 100
- afi: ipv6
routes:
- dest: 1000:10::/64
next_hops:
- admin_distance: 67
interface: Ethernet1
tag: 98
- address_families:
- afi: ipv4
routes:
- dest: 77.77.1.0/24
next_hops:
- interface: 33.1.1.1
vrf: vrftest1
- address_families:
- afi: ipv4
routes:
- dest: 120.1.1.0/24
next_hops:
- admin_distance: 23
interface: Ethernet1
- afi: ipv6
routes:
- dest: 1120:10::/64
next_hops:
- admin_distance: 55
interface: Ethernet1
vrf: testvrf
- block:
- name: merge attributes of given static routes.
eos_static_routes:
config:
- vrf: "testvrf"
address_families:
- afi: 'ipv6'
routes:
- dest: '1120:10::/64'
next_hops:
- interface: Ethernet1
admin_distance: 55
- address_families:
- afi: 'ipv4'
routes:
- dest: '155.55.1.0/24'
next_hops:
- nexthop_grp: testgrp
tag: 100
state: merged
become: yes
register: base_config
- eos_facts:
gather_network_resources: static_routes
become: yes
- assert:
that:
- "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []"
- name: Apply the provided configuration (config to be reverted)
eos_static_routes:
config:
- address_families:
- afi: 'ipv4'
routes:
- dest: '122.1.19.0/24'
next_hops:
- nexthop_grp: testgrp
admin_distance: 21
state: merged
become: yes
register: result
- assert:
that:
- "result.after|symmetric_difference(revert_config) == []"
- name: Revert back to base config using facts round trip
eos_static_routes:
config: "{{ ansible_facts['network_resources']['static_routes'] }}"
state: overridden
become: yes
register: revert
- name: Assert that config was reverted
assert:
that:
- "ansible_facts.network_resources.static_routes | symmetric_difference(revert.after) == []"
always:
- include_tasks: _remove_config.yaml

View file

@ -4509,13 +4509,13 @@ lib/ansible/modules/network/eos/eos_logging.py validate-modules:doc-required-mis
lib/ansible/modules/network/eos/eos_logging.py validate-modules:missing-suboption-docs
lib/ansible/modules/network/eos/eos_logging.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/network/eos/eos_logging.py validate-modules:undocumented-parameter
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:doc-choices-do-not-match-spec
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:doc-elements-mismatch
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:doc-missing-type
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:doc-required-mismatch
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:missing-suboption-docs
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/network/eos/eos_static_route.py validate-modules:undocumented-parameter
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:doc-choices-do-not-match-spec
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:doc-elements-mismatch
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:doc-missing-type
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:doc-required-mismatch
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:missing-suboption-docs
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/network/eos/_eos_static_route.py validate-modules:undocumented-parameter
lib/ansible/modules/network/eos/eos_system.py future-import-boilerplate
lib/ansible/modules/network/eos/eos_system.py metaclass-boilerplate
lib/ansible/modules/network/eos/eos_system.py validate-modules:doc-missing-type

View file

@ -0,0 +1,3 @@
ip route 10.1.1.0/24 Management1
ip route vrf testvrf 120.1.1.0/24 Ethernet1 23
ipv6 route 1000:10::/64 Ethernet1 67 tag 98

View file

@ -0,0 +1 @@
ip route 10.1.1.0/24 Management1

View file

@ -0,0 +1,308 @@
#
# (c) 2019, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from units.compat.mock import patch
from ansible.modules.network.eos import eos_static_routes
from ansible.module_utils.network.eos.config.static_routes.static_routes import add_commands
from units.modules.utils import set_module_args
from .eos_module import TestEosModule, load_fixture
import itertools
class TestEosStaticRoutesModule(TestEosModule):
module = eos_static_routes
def setUp(self):
super(TestEosStaticRoutesModule, 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_edit_config = patch(
'ansible.module_utils.network.eos.providers.providers.CliProvider.edit_config'
)
self.edit_config = self.mock_edit_config.start()
self.mock_execute_show_command = patch(
'ansible.module_utils.network.eos.facts.static_routes.static_routes.Static_routesFacts.get_device_data'
)
self.execute_show_command = self.mock_execute_show_command.start()
def tearDown(self):
super(TestEosStaticRoutesModule, self).tearDown()
self.mock_get_resource_connection_config.stop()
self.mock_get_resource_connection_facts.stop()
self.mock_edit_config.stop()
self.mock_get_config.stop()
self.mock_load_config.stop()
self.mock_execute_show_command.stop()
def load_fixtures(self, commands=None, transport='cli', filename=None):
if filename is None:
filename = 'eos_static_routes_config.cfg'
def load_from_file(*args, **kwargs):
output = load_fixture(filename)
return output
self.execute_show_command.side_effect = load_from_file
def test_eos_static_routes_merged(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1200:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=55)
])
])
])
], state="merged"))
commands = ['ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55']
result = self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_merged_idempotent(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv4",
routes=[
dict(dest="120.1.1.0/24",
next_hops=[
dict(interface="Ethernet1",
admin_distance=23)
])
])
])
], state="merged"))
self.execute_module(changed=False, commands=[])
def test_eos_static_routes_default(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1200:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=55)
])
])
])
]))
commands = ['ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55']
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_default_idempotent(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv4",
routes=[
dict(dest="120.1.1.0/24",
next_hops=[
dict(interface="Ethernet1",
admin_distance=23)
])
])
])
]))
self.execute_module(changed=False, commands=[])
def test_eos_static_routes_replaced(self):
set_module_args(
dict(config=[
dict(address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1000:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=55)
])
])
])
], state="replaced"))
commands = [
'ipv6 route 1000:10::/64 Ethernet1 55',
'no ipv6 route 1000:10::/64 Ethernet1 67 tag 98'
]
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_replaced_idempotent(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv4",
routes=[
dict(dest="120.1.1.0/24",
next_hops=[
dict(interface="Ethernet1",
admin_distance=23)
])
])
])
], state="replaced"))
self.execute_module(changed=False, commands=[])
def test_eos_static_routes_overridden(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1200:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=55)
])
])
])
], state="overridden"))
commands = [
'ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55',
'no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23',
'no ip route 10.1.1.0/24 Management1',
'no ipv6 route 1000:10::/64 Ethernet1 67 tag 98'
]
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_overridden_idempotent(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv4",
routes=[
dict(dest="120.1.1.0/24",
next_hops=[
dict(interface="Ethernet1",
admin_distance=23)
])
])
]),
dict(address_families=[
dict(afi="ipv4",
routes=[
dict(dest="10.1.1.0/24",
next_hops=[
dict(interface="Management1")
])
])
]),
dict(address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1000:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=67,
tag=98)
])
])
])
], state="overridden"))
self.execute_module(changed=False, commands=[])
def test_eos_static_routes_deletedvrf(self):
set_module_args(dict(config=[dict(vrf="testvrf", )], state="deleted"))
commands = ['no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23']
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_deletedroute(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv4", routes=[dict(dest="120.1.1.0/24")])
])
], state="deleted"))
commands = ['no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23']
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_deletedafi(self):
set_module_args(
dict(config=[
dict(vrf="testvrf", address_families=[dict(afi="ipv4")])
], state="deleted"))
commands = ['no ip route vrf testvrf 120.1.1.0/24 Ethernet1 23']
self.execute_module(changed=True, commands=commands)
def test_eos_static_routes_gathered(self):
set_module_args(
dict(config=[],
state="gathered"))
result = self.execute_module(changed=False, filename='eos_static_routes_config.cfg')
commands = []
for gathered_cmds in result['gathered']:
cfg = add_commands(gathered_cmds)
commands.append(cfg)
commands = list(itertools.chain(*commands))
config_commands = ['ip route 10.1.1.0/24 Management1', 'ipv6 route 1000:10::/64 Ethernet1 67 tag 98',
'ip route vrf testvrf 120.1.1.0/24 Ethernet1 23']
self.assertEqual(sorted(config_commands), sorted(commands), result['gathered'])
def test_eos_static_routes_rendered(self):
set_module_args(
dict(config=[
dict(vrf="testvrf",
address_families=[
dict(afi="ipv6",
routes=[
dict(dest="1200:10::/64",
next_hops=[
dict(interface="Ethernet1",
admin_distance=55)
])
])
])
], state="rendered"))
commands = ['ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55']
result = self.execute_module(changed=False)
self.assertEqual(sorted(result['rendered']), sorted(commands), result['rendered'])
def test_eos_static_routes_parsed(self):
set_module_args(
dict(running_config="ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55",
state="parsed"))
commands = ['ipv6 route vrf testvrf 1200:10::/64 Ethernet1 55']
result = self.execute_module(changed=False)
parsed_commands = []
for cmds in result['parsed']:
cfg = add_commands(cmds)
parsed_commands.append(cfg)
parsed_commands = list(itertools.chain(*parsed_commands))
self.assertEqual(sorted(parsed_commands), sorted(commands), result['parsed'])