diff --git a/lib/ansible/module_utils/network/eos/argspec/static_routes/static_routes.py b/lib/ansible/module_utils/network/eos/argspec/static_routes/static_routes.py index 8ad6c92e389..51f4cfdb4dd 100644 --- a/lib/ansible/module_utils/network/eos/argspec/static_routes/static_routes.py +++ b/lib/ansible/module_utils/network/eos/argspec/static_routes/static_routes.py @@ -66,6 +66,9 @@ class Static_routesArgs(object): # pylint: disable=R0903 'interface': { 'type': 'str' }, + 'nexthop_grp': { + 'type': 'str' + }, 'mpls_label': { 'type': 'int' }, diff --git a/lib/ansible/module_utils/network/eos/config/static_routes/static_routes.py b/lib/ansible/module_utils/network/eos/config/static_routes/static_routes.py index e8de402ff6f..ef13f1e0102 100644 --- a/lib/ansible/module_utils/network/eos/config/static_routes/static_routes.py +++ b/lib/ansible/module_utils/network/eos/config/static_routes/static_routes.py @@ -158,7 +158,7 @@ class Static_routes(ConfigBase): for h in have: return_command = add_commands(h) for command in return_command: - if vrf == "default": + if vrf == None: if "vrf" not in command: haveconfigs.append(command) else: @@ -236,18 +236,12 @@ def set_commands(want, have): for command in return_command: commands.append(command) return commands - # if not obj_in_have: - # commands = self.add_commands(w) - # else: - # diff = self.diff_of_dicts(w, obj_in_have) - # commands = self.add_commands(diff) - # return commands def add_commands(want): commandset = [] if not want: return commandset - vrf = want["vrf"] if "vrf" in want.keys() and want["vrf"] != "default" else None + vrf = want["vrf"] if "vrf" in want.keys() and want["vrf"] != None else None for address_family in want["address_families"]: for route in address_family["routes"]: for next_hop in route["next_hops"]: @@ -264,7 +258,10 @@ def add_commands(want): commands.append(' ' + route["dest"].split( )[0] + '/' + cidr) else: commands.append(' ' + route["dest"]) - commands.append(' ' + next_hop["interface"]) + 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(): @@ -341,7 +338,10 @@ def del_commands(want,have): if vrf: commands.append(' vrf ' + vrf) commands.append(' ' + destination) - commands.append(' ' + next_hop["interface"]) + 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(): @@ -370,5 +370,5 @@ def get_net_size(netmask): def get_vrf(config): vrf = "" for c in config: - vrf = c["vrf"] if "vrf" in c.keys() and c["vrf"] else "default" + vrf = c["vrf"] if "vrf" in c.keys() and c["vrf"] else None return vrf diff --git a/lib/ansible/module_utils/network/eos/facts/static_routes/static_routes.py b/lib/ansible/module_utils/network/eos/facts/static_routes/static_routes.py index 368be552e63..855d318ac9e 100644 --- a/lib/ansible/module_utils/network/eos/facts/static_routes/static_routes.py +++ b/lib/ansible/module_utils/network/eos/facts/static_routes/static_routes.py @@ -117,7 +117,7 @@ class Static_routesFacts(object): vrf_config_list = [] config["address_families"] = [] next_hops = {} - interface_list = ["Ethernet", "Loopback", "Management", "Nexthop-Group", + interface_list = ["Ethernet", "Loopback", "Management", "Port-Channel", "Tunnel", "Vlan", "Vxlan", "vtep"] conf_list = conf.split('\n') for conf_elem in conf_list: @@ -138,7 +138,7 @@ class Static_routesFacts(object): vrf_list.append(vrf) dest = remainder.pop(0) else: - config["vrf"] = "default" + config["vrf"] = None dest = matches[0][1] afi = "ipv4" if matches[0][0] == "ip" else "ipv6" if afi not in afi_list: @@ -173,6 +173,9 @@ class Static_routesFacts(object): 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: diff --git a/test/integration/targets/eos_static_routes/defaults/main.yaml b/test/integration/targets/eos_static_routes/defaults/main.yaml new file mode 100644 index 00000000000..164afead284 --- /dev/null +++ b/test/integration/targets/eos_static_routes/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/test/integration/targets/eos_static_routes/meta/main.yaml b/test/integration/targets/eos_static_routes/meta/main.yaml new file mode 100644 index 00000000000..e5c8cd02f04 --- /dev/null +++ b/test/integration/targets/eos_static_routes/meta/main.yaml @@ -0,0 +1,2 @@ +dependencies: + - prepare_eos_tests diff --git a/test/integration/targets/eos_static_routes/tasks/cli.yaml b/test/integration/targets/eos_static_routes/tasks/cli.yaml new file mode 100644 index 00000000000..5b16f46bde2 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tasks/cli.yaml @@ -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 diff --git a/test/integration/targets/eos_static_routes/tasks/eapi.yaml b/test/integration/targets/eos_static_routes/tasks/eapi.yaml new file mode 100644 index 00000000000..1109c6bf6ac --- /dev/null +++ b/test/integration/targets/eos_static_routes/tasks/eapi.yaml @@ -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 diff --git a/test/integration/targets/eos_static_routes/tasks/main.yaml b/test/integration/targets/eos_static_routes/tasks/main.yaml new file mode 100644 index 00000000000..970e74171ea --- /dev/null +++ b/test/integration/targets/eos_static_routes/tasks/main.yaml @@ -0,0 +1,3 @@ +--- +- { include: cli.yaml, tags: ['cli'] } +- { include: eapi.yaml, tags: ['eapi'] } diff --git a/test/integration/targets/eos_static_routes/tests/cli/_parsed.cfg b/test/integration/targets/eos_static_routes/tests/cli/_parsed.cfg new file mode 100644 index 00000000000..a4bb4d65496 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/_parsed.cfg @@ -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 diff --git a/test/integration/targets/eos_static_routes/tests/cli/_populate.yaml b/test/integration/targets/eos_static_routes/tests/cli/_populate.yaml new file mode 100644 index 00000000000..d1714f45348 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/_populate.yaml @@ -0,0 +1,16 @@ +--- +- name: Setup + cli_config: + config: "{{ lines }}" + 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 + diff --git a/test/integration/targets/eos_static_routes/tests/cli/_remove_config.yaml b/test/integration/targets/eos_static_routes/tests/cli/_remove_config.yaml new file mode 100644 index 00000000000..c3626a9f4b5 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/_remove_config.yaml @@ -0,0 +1,18 @@ +--- +- name: Setup + cli_config: + config: "{{ lines }}" + 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 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 + diff --git a/test/integration/targets/eos_static_routes/tests/cli/deleted.yaml b/test/integration/targets/eos_static_routes/tests/cli/deleted.yaml new file mode 100644 index 00000000000..d5782bc3c61 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/deleted.yaml @@ -0,0 +1,67 @@ +--- +- 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 + +- name: Delete attributes of given static routes. + 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 + register: result + +- eos_facts: + gather_network_resources: static_routes + become: yes + +- assert: + that: + - "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []" + become: yes + +- name: Idempotency check + eos_static_routes: *deleted + register: result + +- assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + +- include_tasks: _remove_config.yaml diff --git a/test/integration/targets/eos_static_routes/tests/cli/gathered.yaml b/test/integration/targets/eos_static_routes/tests/cli/gathered.yaml new file mode 100644 index 00000000000..44933319c27 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/gathered.yaml @@ -0,0 +1,73 @@ +--- +- 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 + 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 + 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/eos_static_routes/tests/cli/merged.yaml b/test/integration/targets/eos_static_routes/tests/cli/merged.yaml new file mode 100644 index 00000000000..ea96acc44c8 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/merged.yaml @@ -0,0 +1,98 @@ +--- +- 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 + register: result + +- eos_facts: + gather_network_resources: static_routes + become: yes + +- assert: + that: + - "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []" + become: yes + +- name: Idempotency check + eos_static_routes: *merged + register: result + +- assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + +- include_tasks: _remove_config.yaml diff --git a/test/integration/targets/eos_static_routes/tests/cli/overridden.yaml b/test/integration/targets/eos_static_routes/tests/cli/overridden.yaml new file mode 100644 index 00000000000..e9828d8ce70 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/overridden.yaml @@ -0,0 +1,50 @@ +--- +- 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 + register: result + +- eos_facts: + gather_network_resources: static_routes + become: yes + +- assert: + that: + - "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []" + become: yes + +- name: Idempotency check + eos_static_routes: *overridden + register: result + +- assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + +- include_tasks: _remove_config.yaml diff --git a/test/integration/targets/eos_static_routes/tests/cli/parsed.yaml b/test/integration/targets/eos_static_routes/tests/cli/parsed.yaml new file mode 100644 index 00000000000..64e97f8b982 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/parsed.yaml @@ -0,0 +1,34 @@ +--- +- 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 + 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 + 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 + register: result + +- assert: + that: + - "result['changed'] == false" + +- include_tasks: _remove_config.yaml diff --git a/test/integration/targets/eos_static_routes/tests/cli/rendered.yaml b/test/integration/targets/eos_static_routes/tests/cli/rendered.yaml new file mode 100644 index 00000000000..734d0c68784 --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/rendered.yaml @@ -0,0 +1,52 @@ +--- +- 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 + 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 diff --git a/test/integration/targets/eos_static_routes/tests/cli/replaced.yaml b/test/integration/targets/eos_static_routes/tests/cli/replaced.yaml new file mode 100644 index 00000000000..f01d80762ee --- /dev/null +++ b/test/integration/targets/eos_static_routes/tests/cli/replaced.yaml @@ -0,0 +1,81 @@ +--- +- 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: ipv6 + routes: + - dest: 1120: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: '1120:10::/64' + next_hops: + - interface: Ethernet1 + admin_distance: 55 + state: replaced + register: result + +- eos_facts: + gather_network_resources: static_routes + become: yes + +- assert: + that: + - "ansible_facts.network_resources.static_routes|symmetric_difference(config) == []" + become: yes + +- name: Idempotency check + eos_static_routes: *replaced + register: result + +- assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + +- include_tasks: _remove_config.yaml