From 60676add334eab592f3081633c9f865ed8e06390 Mon Sep 17 00:00:00 2001 From: Ganesh Nalawade Date: Tue, 1 Aug 2017 20:19:54 +0530 Subject: [PATCH] iosxr implemetation for net_interface (#27513) * iosxr implemetation for net_interface * iosxr_interface implementation * Add integration test * iosxr_interface integration test * net_interface intergration test for iosxr * update boilerplate --- .../modules/network/iosxr/iosxr_interface.py | 330 ++++++++++++++++++ test/integration/iosxr.yaml | 9 + .../iosxr_interface/defaults/main.yaml | 3 + .../targets/iosxr_interface/meta/main.yaml | 2 + .../targets/iosxr_interface/tasks/cli.yaml | 16 + .../targets/iosxr_interface/tasks/main.yaml | 2 + .../iosxr_interface/tests/cli/basic.yaml | 267 ++++++++++++++ .../net_interface/tests/cli/basic.yaml | 3 + .../net_interface/tests/iosxr/basic.yaml | 267 ++++++++++++++ 9 files changed, 899 insertions(+) create mode 100644 lib/ansible/modules/network/iosxr/iosxr_interface.py create mode 100644 test/integration/targets/iosxr_interface/defaults/main.yaml create mode 100644 test/integration/targets/iosxr_interface/meta/main.yaml create mode 100644 test/integration/targets/iosxr_interface/tasks/cli.yaml create mode 100644 test/integration/targets/iosxr_interface/tasks/main.yaml create mode 100644 test/integration/targets/iosxr_interface/tests/cli/basic.yaml create mode 100644 test/integration/targets/net_interface/tests/iosxr/basic.yaml diff --git a/lib/ansible/modules/network/iosxr/iosxr_interface.py b/lib/ansible/modules/network/iosxr/iosxr_interface.py new file mode 100644 index 00000000000..cc09d2fa82e --- /dev/null +++ b/lib/ansible/modules/network/iosxr/iosxr_interface.py @@ -0,0 +1,330 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, 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 + + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'core'} + + +DOCUMENTATION = """ +--- +module: iosxr_interface +version_added: "2.4" +author: "Ganesh Nalawade (@ganeshrn)" +short_description: Manage Interface on Cisco IOS XR network devices +description: + - This module provides declarative management of Interfaces + on Cisco IOS XR network devices. +options: + name: + description: + - Name of the Interface. + required: true + description: + description: + - Description of Interface. + enabled: + description: + - Interface link status. + speed: + description: + - Interface link speed. + mtu: + description: + - Maximum size of transmit packet. + duplex: + description: + - Interface link status + choices: ['full', 'half'] + tx_rate: + description: + - Transmit rate + rx_rate: + description: + - Receiver rate + aggregate: + description: List of Interfaces definitions. + purge: + description: + - Purge Interfaces not defined in the aggregate parameter. + This applies only for logical interface. + default: no + state: + description: + - State of the Interface configuration, C(up) means present and + operationally up and C(down) means present and operationally C(down) + default: present + choices: ['present', 'absent', 'up', 'down'] +""" + +EXAMPLES = """ +- name: configure interface + iosxr_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + +- name: remove interface + iosxr_interface: + name: GigabitEthernet0/0/0/2 + state: absent + +- name: make interface up + iosxr_interface: + name: GigabitEthernet0/0/0/2 + state: up + +- name: make interface down + iosxr_interface: + name: GigabitEthernet0/0/0/2 + state: down +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device. + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface GigabitEthernet0/0/0/2 + - description test-interface + - duplex half + - mtu 512 +""" +import re + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.iosxr import get_config, load_config +from ansible.module_utils.iosxr import iosxr_argument_spec, check_args + +DEFAULT_DESCRIPTION = "configured by iosxr_interface" + + +def validate_mtu(value, module): + if value and not 64 <= int(value) <= 65535: + module.fail_json(msg='mtu must be between 64 and 65535') + + +def validate_param_values(module, obj, param=None): + if param is None: + param = module.params + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get('validate_%s' % key) + if callable(validator): + validator(param.get(key), module) + + +def parse_shutdown(intf_config): + for cfg in intf_config: + match = re.search(r'%s' % 'shutdown', cfg, re.M) + if match: + return True + return False + + +def parse_config_argument(intf_config, arg): + for cfg in intf_config: + match = re.search(r'%s (.+)$' % arg, cfg, re.M) + if match: + return match.group(1) + + +def search_obj_in_list(name, lst): + for o in lst: + if o['name'] == name: + return o + + return None + + +def map_params_to_obj(module): + obj = [] + args = ['name', 'description', 'speed', 'duplex', 'mtu'] + + aggregate = module.params.get('aggregate') + if aggregate: + for param in aggregate: + validate_param_values(module, args, param) + d = param.copy() + + if 'name' not in d: + module.fail_json(msg="missing required arguments: %s" % 'name') + + # set default value + for item in args: + if item not in d: + if item == 'description': + d['description'] = DEFAULT_DESCRIPTION + else: + d[item] = None + else: + d[item] = str(d[item]) + + if not d.get('state'): + d['state'] = module.params['state'] + + if d['state'] in ('present', 'up'): + d['disable'] = False + else: + d['disable'] = True + + obj.append(d) + + else: + validate_param_values(module, args) + params = { + 'name': module.params['name'], + 'description': module.params['description'], + 'speed': module.params['speed'], + 'mtu': module.params['mtu'], + 'duplex': module.params['duplex'], + 'state': module.params['state'] + } + + state = module.params['state'] + if state == 'present' or state == 'up': + params.update({'disable': False}) + else: + params.update({'disable': True}) + + obj.append(params) + return obj + + +def map_config_to_obj(module): + data = get_config(module, flags=['interface']) + interfaces = data.strip().rstrip('!').split('!') + + if not interfaces: + return list() + + instances = list() + + for interface in interfaces: + intf_config = interface.strip().splitlines() + + name = intf_config[0].strip().split()[1] + + if name == 'preconfigure': + name = intf_config[0].strip().split()[2] + + obj = { + 'name': name, + 'description': parse_config_argument(intf_config, 'description'), + 'speed': parse_config_argument(intf_config, 'speed'), + 'duplex': parse_config_argument(intf_config, 'duplex'), + 'mtu': parse_config_argument(intf_config, 'mtu'), + 'disable': True if parse_shutdown(intf_config) else False, + 'state': 'present' + } + instances.append(obj) + return instances + + +def map_obj_to_commands(updates): + commands = list() + want, have = updates + + args = ('speed', 'description', 'duplex', 'mtu') + for w in want: + name = w['name'] + disable = w['disable'] + state = w['state'] + + obj_in_have = search_obj_in_list(name, have) + interface = 'interface ' + name + + if state == 'absent' and obj_in_have: + commands.append('no ' + interface) + + elif state in ('present', 'up', 'down'): + if obj_in_have: + for item in args: + candidate = w.get(item) + running = obj_in_have.get(item) + if candidate != running: + if candidate: + cmd = interface + ' ' + item + ' ' + str(candidate) + commands.append(cmd) + elif running: + cmd = 'no ' + interface + ' ' + item + ' ' + str(running) + commands.append(cmd) + + if disable and not obj_in_have.get('disable', False): + commands.append(interface + ' shutdown') + elif not disable and obj_in_have.get('disable', False): + commands.append('no ' + interface + ' shutdown') + else: + for item in args: + value = w.get(item) + if value: + commands.append(interface + ' ' + item + ' ' + str(value)) + + if disable: + commands.append('no ' + interface + ' shutdown') + return commands + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + name=dict(), + description=dict(default=DEFAULT_DESCRIPTION), + speed=dict(), + mtu=dict(), + duplex=dict(choices=['full', 'half']), + enabled=dict(), + tx_rate=dict(), + rx_rate=dict(), + aggregate=dict(type='list'), + purge=dict(default=False, type='bool'), + state=dict(default='present', + choices=['present', 'absent', 'up', 'down']) + ) + + argument_spec.update(iosxr_argument_spec) + + required_one_of = [['name', 'aggregate']] + mutually_exclusive = [['name', 'aggregate']] + + module = AnsibleModule(argument_spec=argument_spec, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True) + + warnings = list() + check_args(module, warnings) + + result = {'changed': False} + + want = map_params_to_obj(module) + have = map_config_to_obj(module) + + commands = map_obj_to_commands((want, have)) + + result['commands'] = commands + result['warnings'] = warnings + + if 'no username admin' in commands: + module.fail_json(msg='cannot delete the `admin` account') + + if commands: + if not module.check_mode: + load_config(module, commands, result['warnings'], commit=True) + result['changed'] = True + + module.exit_json(**result) + +if __name__ == '__main__': + main() diff --git a/test/integration/iosxr.yaml b/test/integration/iosxr.yaml index cba9a89c306..8e3a0aa3f0d 100644 --- a/test/integration/iosxr.yaml +++ b/test/integration/iosxr.yaml @@ -78,6 +78,15 @@ failed_modules: "{{ failed_modules }} + [ 'iosxr_logging' ]" test_failed: true + - block: + - include_role: + name: iosxr_interface + when: "limit_to in ['*', 'iosxr_interface']" + rescue: + - set_fact: + failed_modules: "{{ failed_modules }} + [ 'iosxr_interface' ]" + test_failed: true + ########### - debug: var=failed_modules when: test_failed diff --git a/test/integration/targets/iosxr_interface/defaults/main.yaml b/test/integration/targets/iosxr_interface/defaults/main.yaml new file mode 100644 index 00000000000..9ef5ba51651 --- /dev/null +++ b/test/integration/targets/iosxr_interface/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "*" +test_items: [] diff --git a/test/integration/targets/iosxr_interface/meta/main.yaml b/test/integration/targets/iosxr_interface/meta/main.yaml new file mode 100644 index 00000000000..d4da833dd50 --- /dev/null +++ b/test/integration/targets/iosxr_interface/meta/main.yaml @@ -0,0 +1,2 @@ +dependencies: + - prepare_iosxr_tests diff --git a/test/integration/targets/iosxr_interface/tasks/cli.yaml b/test/integration/targets/iosxr_interface/tasks/cli.yaml new file mode 100644 index 00000000000..46d86dd6988 --- /dev/null +++ b/test/integration/targets/iosxr_interface/tasks/cli.yaml @@ -0,0 +1,16 @@ +--- +- name: collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + 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 + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/iosxr_interface/tasks/main.yaml b/test/integration/targets/iosxr_interface/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/iosxr_interface/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/iosxr_interface/tests/cli/basic.yaml b/test/integration/targets/iosxr_interface/tests/cli/basic.yaml new file mode 100644 index 00000000000..b49497886e7 --- /dev/null +++ b/test/integration/targets/iosxr_interface/tests/cli/basic.yaml @@ -0,0 +1,267 @@ +--- +- debug: msg="START iosxr_interface cli/basic.yaml" + +- name: Setup interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: absent + provider: "{{ cli }}" + register: result + + +- name: Confgure interface + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface-initial" in result.commands' + +- name: Confgure interface (idempotent) + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Confgure interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex half" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 512" in result.commands' + +- name: Change interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-1 + speed: 10 + duplex: full + mtu: 256 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface-1" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 256" in result.commands' + +- name: Delete interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 speed 10" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 mtu 256" in result.commands' + +- name: Delete interface parameters (idempotent) + net_interface: + name: GigabitEthernet0/0/0/2 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Disable interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: down + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Enable interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: up + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Confgure second interface (setup) + net_interface: + name: GigabitEthernet0/0/0/3 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 description test-interface-initial" in result.commands' + +- name: Add interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/0/0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/3 description test-interface-1" in result.commands' + - '"interface GigabitEthernet0/0/0/3 duplex half" in result.commands' + - '"interface GigabitEthernet0/0/0/3 mtu 256" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 description test-interface-2" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 516" in result.commands' + +- name: Add interface aggregate (idempotent) + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/0/0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + + +- name: Change interface parameters in aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3 } + - { name: GigabitEthernet0/0/0/2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/3 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/3 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/3 duplex half" in result.commands' + - '"no interface GigabitEthernet0/0/0/3 mtu 256" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 mtu 516" in result.commands' + +- name: Disable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, state: down } + - { name: GigabitEthernet0/0/0/2, state: down } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 shutdown" in result.commands' + - '"interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Enable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, state: up } + - { name: GigabitEthernet0/0/0/2, state: up } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/3 shutdown" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Create interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4 } + - { name: GigabitEthernet0/0/0/5 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/4 description configured by iosxr_interface" in result.commands' + - '"interface GigabitEthernet0/0/0/5 description configured by iosxr_interface" in result.commands' + +- name: Delete interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4, state: absent } + - { name: GigabitEthernet0/0/0/5, state: absent } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/4" in result.commands' + - '"no interface GigabitEthernet0/0/0/5" in result.commands' + +- name: Delete interface aggregate (idempotent) + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4, state: absent } + - { name: GigabitEthernet0/0/0/5, state: absent } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- debug: msg="END iosxr_interface cli/basic.yaml" diff --git a/test/integration/targets/net_interface/tests/cli/basic.yaml b/test/integration/targets/net_interface/tests/cli/basic.yaml index d2bab54d28f..610ab6e11ae 100644 --- a/test/integration/targets/net_interface/tests/cli/basic.yaml +++ b/test/integration/targets/net_interface/tests/cli/basic.yaml @@ -7,4 +7,7 @@ - include: "{{ role_path }}/tests/vyos/basic.yaml" when: hostvars[inventory_hostname]['ansible_network_os'] == 'vyos' +- include: "{{ role_path }}/tests/iosxr/basic.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'iosxr' + - debug: msg="END cli/contains.yaml" diff --git a/test/integration/targets/net_interface/tests/iosxr/basic.yaml b/test/integration/targets/net_interface/tests/iosxr/basic.yaml new file mode 100644 index 00000000000..c55414fc37e --- /dev/null +++ b/test/integration/targets/net_interface/tests/iosxr/basic.yaml @@ -0,0 +1,267 @@ +--- +- debug: msg="START net_interface iosxr/basic.yaml" + +- name: Setup interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: absent + provider: "{{ cli }}" + register: result + + +- name: Confgure interface + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface-initial" in result.commands' + +- name: Confgure interface (idempotent) + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Confgure interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex half" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 512" in result.commands' + +- name: Change interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface-1 + speed: 10 + duplex: full + mtu: 256 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description test-interface-1" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 256" in result.commands' + +- name: Delete interface parameters + net_interface: + name: GigabitEthernet0/0/0/2 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 speed 10" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 mtu 256" in result.commands' + +- name: Delete interface parameters (idempotent) + net_interface: + name: GigabitEthernet0/0/0/2 + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Disable interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: down + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Enable interface + net_interface: + name: GigabitEthernet0/0/0/2 + state: up + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Confgure second interface (setup) + net_interface: + name: GigabitEthernet0/0/0/3 + description: test-interface-initial + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 description test-interface-initial" in result.commands' + +- name: Add interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/0/0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/3 description test-interface-1" in result.commands' + - '"interface GigabitEthernet0/0/0/3 duplex half" in result.commands' + - '"interface GigabitEthernet0/0/0/3 mtu 256" in result.commands' + - '"interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 description test-interface-2" in result.commands' + - '"interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"interface GigabitEthernet0/0/0/2 mtu 516" in result.commands' + +- name: Add interface aggregate (idempotent) + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/0/0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + + +- name: Change interface parameters in aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3 } + - { name: GigabitEthernet0/0/0/2 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/3 speed 10" in result.commands' + - '"interface GigabitEthernet0/0/0/3 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/3 duplex half" in result.commands' + - '"no interface GigabitEthernet0/0/0/3 mtu 256" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 speed 100" in result.commands' + - '"interface GigabitEthernet0/0/0/2 description configured by iosxr_interface" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 duplex full" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 mtu 516" in result.commands' + +- name: Disable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, state: down } + - { name: GigabitEthernet0/0/0/2, state: down } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/3 shutdown" in result.commands' + - '"interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Enable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/3, state: up } + - { name: GigabitEthernet0/0/0/2, state: up } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/3 shutdown" in result.commands' + - '"no interface GigabitEthernet0/0/0/2 shutdown" in result.commands' + +- name: Create interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4 } + - { name: GigabitEthernet0/0/0/5 } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/0/0/4 description configured by iosxr_interface" in result.commands' + - '"interface GigabitEthernet0/0/0/5 description configured by iosxr_interface" in result.commands' + +- name: Delete interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4, state: absent } + - { name: GigabitEthernet0/0/0/5, state: absent } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface GigabitEthernet0/0/0/4" in result.commands' + - '"no interface GigabitEthernet0/0/0/5" in result.commands' + +- name: Delete interface aggregate (idempotent) + net_interface: + aggregate: + - { name: GigabitEthernet0/0/0/4, state: absent } + - { name: GigabitEthernet0/0/0/5, state: absent } + state: present + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- debug: msg="END net_interface iosxr/basic.yaml"