From c4e06a1735ed30f310385d53d1aa62f0d2576a0e Mon Sep 17 00:00:00 2001 From: Ganesh Nalawade Date: Tue, 1 Aug 2017 23:20:08 +0530 Subject: [PATCH] ios implementation for net_interface (#27459) * ios implementation for net_interface * ios_interface implementation * ios_interface integration test * net_interface integration test for ios and other refactor * Update boilerplate and minor refactor --- .../network/interface/net_interface.py | 18 +- .../modules/network/ios/ios_interface.py | 337 ++++++++++++++++++ test/integration/ios.yaml | 8 + .../targets/ios_interface/defaults/main.yaml | 2 + .../targets/ios_interface/meta/main.yaml | 2 + .../targets/ios_interface/tasks/cli.yaml | 15 + .../targets/ios_interface/tasks/main.yaml | 2 + .../ios_interface/tests/cli/basic.yaml | 293 +++++++++++++++ .../net_interface/tests/cli/basic.yaml | 3 + .../net_interface/tests/ios/basic.yaml | 293 +++++++++++++++ 10 files changed, 962 insertions(+), 11 deletions(-) create mode 100644 lib/ansible/modules/network/ios/ios_interface.py create mode 100644 test/integration/targets/ios_interface/defaults/main.yaml create mode 100644 test/integration/targets/ios_interface/meta/main.yaml create mode 100644 test/integration/targets/ios_interface/tasks/cli.yaml create mode 100644 test/integration/targets/ios_interface/tasks/main.yaml create mode 100644 test/integration/targets/ios_interface/tests/cli/basic.yaml create mode 100644 test/integration/targets/net_interface/tests/ios/basic.yaml diff --git a/lib/ansible/modules/network/interface/net_interface.py b/lib/ansible/modules/network/interface/net_interface.py index fab4704ebf2..44565a2a27f 100644 --- a/lib/ansible/modules/network/interface/net_interface.py +++ b/lib/ansible/modules/network/interface/net_interface.py @@ -32,10 +32,7 @@ options: - Description of Interface. enabled: description: - - Configure operational status of the interface link. - If value is I(yes) interface is configured in up state, - for I(no) interface is configured in down state. - default: yes + - Interface link status. speed: description: - Interface link speed. @@ -57,14 +54,15 @@ options: description: List of Interfaces definitions. purge: description: - - Purge Interfaces not defined in the aggregates parameter. + - Purge Interfaces not defined in the aggregate parameter. This applies only for logical interface. default: no state: description: - - State of the Interface configuration. + - 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'] + choices: ['present', 'absent', 'up', 'down'] """ EXAMPLES = """ @@ -82,15 +80,13 @@ EXAMPLES = """ net_interface: name: ge-0/0/1 description: test-interface - state: present - enabled: True + state: up - name: make interface down net_interface: name: ge-0/0/1 description: test-interface - state: present - enabled: False + state: down """ RETURN = """ diff --git a/lib/ansible/modules/network/ios/ios_interface.py b/lib/ansible/modules/network/ios/ios_interface.py new file mode 100644 index 00000000000..ac7b2bf116d --- /dev/null +++ b/lib/ansible/modules/network/ios/ios_interface.py @@ -0,0 +1,337 @@ +#!/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: ios_interface +version_added: "2.4" +author: "Ganesh Nalawade (@ganeshrn)" +short_description: Manage Interface on Cisco IOS network devices +description: + - This module provides declarative management of Interfaces + on Cisco IOS 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 + default: auto + choices: ['full', 'half', 'auto'] + 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 + ios_interface: + name: GigabitEthernet0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + +- name: remove interface + ios_interface: + name: Loopback9 + state: absent + +- name: make interface up + ios_interface: + name: GigabitEthernet0/2 + state: up + +- name: make interface down + ios_interface: + name: GigabitEthernet0/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/2 + - description test-interface + - duplex half + - mtu 512 +""" +import re + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ios import get_config, load_config +from ansible.module_utils.ios import ios_argument_spec, check_args +from ansible.module_utils.netcfg import NetworkConfig + +DEFAULT_DESCRIPTION = "configured by ios_interface" + + +def validate_mtu(value, module): + if value and not 64 <= int(value) <= 9600: + module.fail_json(msg='mtu must be between 64 and 9600') + + +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(configobj, name): + cfg = configobj['interface %s' % name] + cfg = '\n'.join(cfg.children) + match = re.search(r'shutdown', cfg, re.M) + if match: + return True + else: + return False + + +def parse_config_argument(configobj, name, arg=None): + cfg = configobj['interface %s' % name] + cfg = '\n'.join(cfg.children) + 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 add_command_to_interface(interface, cmd, commands): + if interface not in commands: + commands.append(interface) + commands.append(cmd) + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=1, contents=config) + + match = re.findall(r'^interface (\S+)', config, re.M) + if not match: + return list() + + instances = list() + + for item in set(match): + obj = { + 'name': item, + 'description': parse_config_argument(configobj, item, 'description'), + 'speed': parse_config_argument(configobj, item, 'speed'), + 'duplex': parse_config_argument(configobj, item, 'duplex'), + 'mtu': parse_config_argument(configobj, item, 'mtu'), + 'disable': True if parse_shutdown(configobj, item) else False, + 'state': 'present' + } + instances.append(obj) + return instances + + +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_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 = item + ' ' + str(candidate) + add_command_to_interface(interface, cmd, commands) + elif running: + # if value present in device is default value for + # interface, don't delete + if running == 'auto' and item in ('speed', 'duplex'): + continue + cmd = 'no ' + item + ' ' + str(running) + add_command_to_interface(interface, cmd, commands) + + if disable and not obj_in_have.get('disable', False): + add_command_to_interface(interface, 'shutdown', commands) + elif not disable and obj_in_have.get('disable', False): + add_command_to_interface(interface, 'no shutdown', commands) + else: + commands.append(interface) + for item in args: + value = w.get(item) + if value: + commands.append(item + ' ' + str(value)) + + if disable: + commands.append('no 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', 'auto']), + 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(ios_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} + if warnings: + result['warnings'] = warnings + + want = map_params_to_obj(module) + have = map_config_to_obj(module) + + commands = map_obj_to_commands((want, have)) + result['commands'] = commands + + if commands: + if not module.check_mode: + load_config(module, commands) + result['changed'] = True + + module.exit_json(**result) + +if __name__ == '__main__': + main() diff --git a/test/integration/ios.yaml b/test/integration/ios.yaml index d63499ddd20..4d5e535884c 100644 --- a/test/integration/ios.yaml +++ b/test/integration/ios.yaml @@ -85,6 +85,14 @@ failed_modules: "{{ failed_modules }} + [ 'ios_logging' ]" test_failed: true + - block: + - include_role: + name: ios_interface + when: "limit_to in ['*', 'ios_interface']" + rescue: + - set_fact: + failed_modules: "{{ failed_modules }} + [ 'ios_logging' ]" + test_failed: true ########### - debug: var=failed_modules diff --git a/test/integration/targets/ios_interface/defaults/main.yaml b/test/integration/targets/ios_interface/defaults/main.yaml new file mode 100644 index 00000000000..5f709c5aac1 --- /dev/null +++ b/test/integration/targets/ios_interface/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/test/integration/targets/ios_interface/meta/main.yaml b/test/integration/targets/ios_interface/meta/main.yaml new file mode 100644 index 00000000000..159cea8d383 --- /dev/null +++ b/test/integration/targets/ios_interface/meta/main.yaml @@ -0,0 +1,2 @@ +dependencies: + - prepare_ios_tests diff --git a/test/integration/targets/ios_interface/tasks/cli.yaml b/test/integration/targets/ios_interface/tasks/cli.yaml new file mode 100644 index 00000000000..d675462dd02 --- /dev/null +++ b/test/integration/targets/ios_interface/tasks/cli.yaml @@ -0,0 +1,15 @@ +--- +- name: collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + register: test_cases + +- 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/ios_interface/tasks/main.yaml b/test/integration/targets/ios_interface/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/ios_interface/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/ios_interface/tests/cli/basic.yaml b/test/integration/targets/ios_interface/tests/cli/basic.yaml new file mode 100644 index 00000000000..b47e056f6eb --- /dev/null +++ b/test/integration/targets/ios_interface/tests/cli/basic.yaml @@ -0,0 +1,293 @@ +--- +- debug: msg="START ios_interface netconf/basic.yaml" + +- name: Confgure interface + ios_interface: + name: GigabitEthernet0/2 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface-initial" in result.commands' + +- name: Confgure interface (idempotent) + ios_interface: + name: GigabitEthernet0/2 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Confgure interface parameters + ios_interface: + name: GigabitEthernet0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface" in result.commands' + - '"speed 100" in result.commands' + - '"duplex half" in result.commands' + - '"mtu 512" in result.commands' + +- name: Change interface parameters + ios_interface: + name: GigabitEthernet0/2 + description: test-interface-1 + speed: 10 + duplex: full + mtu: 256 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface-1" in result.commands' + - '"speed 10" in result.commands' + - '"duplex full" in result.commands' + - '"mtu 256" in result.commands' + +- name: Delete interface parameters + ios_interface: + name: GigabitEthernet0/2 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no speed 10" in result.commands' + - '"no duplex full" in result.commands' + - '"no mtu 256" in result.commands' + +- name: Delete interface parameters (idempotent) + ios_interface: + name: GigabitEthernet0/2 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Disable interface + ios_interface: + name: GigabitEthernet0/2 + state: down + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"shutdown" in result.commands' + +- name: Enable interface + ios_interface: + name: GigabitEthernet0/2 + state: up + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no shutdown" in result.commands' + +- name: Confgure second interface (setup) + ios_interface: + name: GigabitEthernet0/1 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"description test-interface-initial" in result.commands' + +- name: Add interface aggregate + ios_interface: + aggregate: + - { name: GigabitEthernet0/1, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"speed 10" in result.commands' + - '"description test-interface-1" in result.commands' + - '"duplex half" in result.commands' + - '"mtu 256" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"speed 100" in result.commands' + - '"description test-interface-2" in result.commands' + - '"duplex full" in result.commands' + - '"mtu 516" in result.commands' + +- name: Add interface aggregate (idempotent) + ios_interface: + aggregate: + - { name: GigabitEthernet0/1, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + + +- name: Change interface parameters in aggregate + ios_interface: + aggregate: + - { name: GigabitEthernet0/1 } + - { name: GigabitEthernet0/2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"no speed 10" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no duplex half" in result.commands' + - '"no mtu 256" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no speed 100" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no duplex full" in result.commands' + - '"no mtu 516" in result.commands' + +- name: Disable interface aggregate + ios_interface: + aggregate: + - { name: GigabitEthernet0/1, state: down } + - { name: GigabitEthernet0/2, state: down } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"shutdown" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"shutdown" in result.commands' + +- name: Enable interface aggregate + ios_interface: + aggregate: + - { name: GigabitEthernet0/1, state: up } + - { name: GigabitEthernet0/2, state: up } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"no shutdown" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no shutdown" in result.commands' + +- name: Create loopback interface aggregate + ios_interface: + aggregate: + - { name: Loopback9 } + - { name: Loopback10 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface Loopback9" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"interface Loopback10" in result.commands' + - '"description configured by ios_interface" in result.commands' + +- name: Delete loopback interface aggregate + ios_interface: + aggregate: + - { name: Loopback9, state: absent } + - { name: Loopback10, state: absent } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface Loopback9" in result.commands' + - '"no interface Loopback10" in result.commands' + +- name: Delete loopback interface aggregate (idempotent) + ios_interface: + aggregate: + - { name: Loopback9, state: absent } + - { name: Loopback10, state: absent } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- debug: msg="END ios_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 610ab6e11ae..8a6f5347e67 100644 --- a/test/integration/targets/net_interface/tests/cli/basic.yaml +++ b/test/integration/targets/net_interface/tests/cli/basic.yaml @@ -10,4 +10,7 @@ - include: "{{ role_path }}/tests/iosxr/basic.yaml" when: hostvars[inventory_hostname]['ansible_network_os'] == 'iosxr' +- include: "{{ role_path }}/tests/ios/basic.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'ios' + - debug: msg="END cli/contains.yaml" diff --git a/test/integration/targets/net_interface/tests/ios/basic.yaml b/test/integration/targets/net_interface/tests/ios/basic.yaml new file mode 100644 index 00000000000..4f18588e96f --- /dev/null +++ b/test/integration/targets/net_interface/tests/ios/basic.yaml @@ -0,0 +1,293 @@ +--- +- debug: msg="START net_interface ios/basic.yaml" + +- name: Confgure interface + net_interface: + name: GigabitEthernet0/2 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface-initial" in result.commands' + +- name: Confgure interface (idempotent) + net_interface: + name: GigabitEthernet0/2 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Confgure interface parameters + net_interface: + name: GigabitEthernet0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface" in result.commands' + - '"speed 100" in result.commands' + - '"duplex half" in result.commands' + - '"mtu 512" in result.commands' + +- name: Change interface parameters + net_interface: + name: GigabitEthernet0/2 + description: test-interface-1 + speed: 10 + duplex: full + mtu: 256 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description test-interface-1" in result.commands' + - '"speed 10" in result.commands' + - '"duplex full" in result.commands' + - '"mtu 256" in result.commands' + +- name: Delete interface parameters + net_interface: + name: GigabitEthernet0/2 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no speed 10" in result.commands' + - '"no duplex full" in result.commands' + - '"no mtu 256" in result.commands' + +- name: Delete interface parameters (idempotent) + net_interface: + name: GigabitEthernet0/2 + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Disable interface + net_interface: + name: GigabitEthernet0/2 + state: down + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"shutdown" in result.commands' + +- name: Enable interface + net_interface: + name: GigabitEthernet0/2 + state: up + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no shutdown" in result.commands' + +- name: Confgure second interface (setup) + net_interface: + name: GigabitEthernet0/1 + description: test-interface-initial + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"description test-interface-initial" in result.commands' + +- name: Add interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/1, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"speed 10" in result.commands' + - '"description test-interface-1" in result.commands' + - '"duplex half" in result.commands' + - '"mtu 256" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"speed 100" in result.commands' + - '"description test-interface-2" in result.commands' + - '"duplex full" in result.commands' + - '"mtu 516" in result.commands' + +- name: Add interface aggregate (idempotent) + net_interface: + aggregate: + - { name: GigabitEthernet0/1, speed: 10, duplex: half, mtu: 256, description: test-interface-1 } + - { name: GigabitEthernet0/2, speed: 100, duplex: full, mtu: 516, description: test-interface-2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + + +- name: Change interface parameters in aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/1 } + - { name: GigabitEthernet0/2 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"no speed 10" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no duplex half" in result.commands' + - '"no mtu 256" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no speed 100" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"no duplex full" in result.commands' + - '"no mtu 516" in result.commands' + +- name: Disable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/1, state: down } + - { name: GigabitEthernet0/2, state: down } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"shutdown" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"shutdown" in result.commands' + +- name: Enable interface aggregate + net_interface: + aggregate: + - { name: GigabitEthernet0/1, state: up } + - { name: GigabitEthernet0/2, state: up } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface GigabitEthernet0/1" in result.commands' + - '"no shutdown" in result.commands' + - '"interface GigabitEthernet0/2" in result.commands' + - '"no shutdown" in result.commands' + +- name: Create loopback interface aggregate + net_interface: + aggregate: + - { name: Loopback9 } + - { name: Loopback10 } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface Loopback9" in result.commands' + - '"description configured by ios_interface" in result.commands' + - '"interface Loopback10" in result.commands' + - '"description configured by ios_interface" in result.commands' + +- name: Delete loopback interface aggregate + net_interface: + aggregate: + - { name: Loopback9, state: absent } + - { name: Loopback10, state: absent } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"no interface Loopback9" in result.commands' + - '"no interface Loopback10" in result.commands' + +- name: Delete loopback interface aggregate (idempotent) + net_interface: + aggregate: + - { name: Loopback9, state: absent } + - { name: Loopback10, state: absent } + state: present + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - 'result.changed == false' + +- debug: msg="END net_interface ios/basic.yaml" \ No newline at end of file