diff --git a/lib/ansible/modules/network/nxos/nxos_interface.py b/lib/ansible/modules/network/nxos/nxos_interface.py index e47939520d5..db408768f4b 100644 --- a/lib/ansible/modules/network/nxos/nxos_interface.py +++ b/lib/ansible/modules/network/nxos/nxos_interface.py @@ -56,7 +56,7 @@ options: version_added: 2.2 speed: description: - - Interface link speed. + - Interface link speed. Applicable for ethernet interface only. version_added: 2.5 admin_state: description: @@ -73,12 +73,14 @@ options: description: - Manage Layer 2 or Layer 3 state of the interface. This option is supported for ethernet and portchannel interface. + Applicable for ethernet and portchannel interface only. required: false default: null choices: ['layer2','layer3'] mtu: description: - MTU for a specific interface. Must be an even number between 576 and 9216. + Applicable for ethernet interface only. required: false version_added: 2.5 ip_forward: @@ -91,28 +93,31 @@ options: fabric_forwarding_anycast_gateway: description: - Associate SVI with anycast gateway under VLAN configuration mode. + Applicable for SVI interface only. required: false default: null choices: ['true','false'] version_added: 2.2 duplex: description: - - Interface link status + - Interface link status. Applicable for ethernet interface only. default: auto choices: ['full', 'half', 'auto'] version_added: 2.5 tx_rate: description: - Transmit rate in bits per second (bps). + This is state check parameter only. version_added: 2.5 rx_rate: description: - Receiver rate in bits per second (bps). + This is state check parameter only. version_added: 2.5 neighbors: description: - Check the operational state of given interface C(name) for LLDP neighbor. - - The following suboptions are available. + - The following suboptions are available. This is state check parameter only. suboptions: host: description: @@ -430,31 +435,27 @@ def map_obj_to_commands(updates, module): if name: w['interface_type'] = None - obj_in_have = search_obj_in_list(name, have) - is_default = is_default_interface(name, module) + if interface_type: + obj_in_have = {} + if state in ('present', 'default'): + module.fail_json(msg='The interface_type param can be used only with state absent.') + else: + obj_in_have = search_obj_in_list(name, have) + is_default = is_default_interface(name, module) + if name: interface = 'interface ' + name - if interface_type and state == 'present': - module.fail_json(msg='The interface_type param can be used only with state absent.') - if state == 'absent': if obj_in_have: commands.append('no interface {0}'.format(name)) - elif interface_type: + elif interface_type and not obj_in_have: intfs = get_interfaces_dict(module)[interface_type] cmds = get_interface_type_removed_cmds(intfs) commands.extend(cmds) elif state == 'present': if obj_in_have: - for item in args: - candidate = w.get(item) - - if candidate and candidate != obj_in_have.get(item): - cmd = item + ' ' + str(candidate) - add_command_to_interface(interface, cmd, commands) - if mode == 'layer2' and mode != obj_in_have.get('mode'): add_command_to_interface(interface, 'switchport', commands) elif mode == 'layer3' and mode != obj_in_have.get('mode'): @@ -471,13 +472,19 @@ def map_obj_to_commands(updates, module): add_command_to_interface(interface, 'no ip forward', commands) if (fabric_forwarding_anycast_gateway is True and - obj_in_have.get('fabric_forwarding_anycast_gateway') is True): + obj_in_have.get('fabric_forwarding_anycast_gateway') is False): add_command_to_interface(interface, 'fabric forwarding mode anycast-gateway', commands) elif (fabric_forwarding_anycast_gateway is False and - obj_in_have.get('fabric_forwarding_anycast_gateway') is False): + obj_in_have.get('fabric_forwarding_anycast_gateway') is True): add_command_to_interface(interface, 'no fabric forwarding mode anycast-gateway', commands) + for item in args: + candidate = w.get(item) + if candidate and candidate != obj_in_have.get(item): + cmd = item + ' ' + str(candidate) + add_command_to_interface(interface, cmd, commands) + if name and get_interface_type(name) == 'ethernet': if mode != obj_in_have.get('mode'): admin_state = w.get('admin_state') or obj_in_have.get('admin_state') @@ -489,11 +496,6 @@ def map_obj_to_commands(updates, module): else: commands.append(interface) - for item in args: - candidate = w.get(item) - if candidate: - commands.append(item + ' ' + str(candidate)) - if mode == 'layer2': commands.append('switchport') elif mode == 'layer3': @@ -515,6 +517,11 @@ def map_obj_to_commands(updates, module): elif fabric_forwarding_anycast_gateway is False: commands.append('no fabric forwarding mode anycast-gateway') + for item in args: + candidate = w.get(item) + if candidate: + commands.append(item + ' ' + str(candidate)) + elif state == 'default': if is_default is False: commands.append('default interface {0}'.format(name)) @@ -567,6 +574,9 @@ def map_config_to_obj(want, module): mtu=None, mode=None, duplex=None, interface_type=None, ip_forward=None, fabric_forwarding_anycast_gateway=None) + if not w['name']: + return obj + command = 'show interface {0}'.format(w['name']) try: body = execute_show_command(command, module)[0] @@ -592,13 +602,9 @@ def map_config_to_obj(want, module): obj['name'] = normalize_interface(interface_table.get('interface')) obj['admin_state'] = interface_table.get('admin_state') obj['description'] = interface_table.get('desc') - obj['mtu'] = int(interface_table.get('eth_mtu')) + obj['mtu'] = interface_table.get('eth_mtu') obj['duplex'] = interface_table.get('eth_duplex') speed = interface_table.get('eth_speed') - if 'auto' in speed: - obj['speed'] = speed - else: - obj['speed'] = int(speed.split()[0]) mode = interface_table.get('eth_mode') if mode in ('access', 'trunk'): obj['mode'] = 'layer2' @@ -607,14 +613,21 @@ def map_config_to_obj(want, module): command = 'show run interface {0}'.format(obj['name']) body = execute_show_command(command, module)[0] + + if 'speed' in body: + obj['speed'] = re.search(r'speed (\d+)', body).group(1) + else: + obj['speed'] = 'auto' + + if 'duplex' in body: + obj['duplex'] = re.search(r'duplex (\S+)', body).group(1) + else: + obj['duplex'] = 'auto' + if 'ip forward' in body: obj['ip_forward'] = 'enable' else: obj['ip_forward'] = 'disable' - if 'fabric forwarding mode anycast-gateway' in body: - obj['fabric_forwarding_anycast_gateway'] = True - else: - obj['fabric_forwarding_anycast_gateway'] = False elif intf_type == 'svi': obj['name'] = normalize_interface(interface_table.get('interface')) @@ -661,6 +674,9 @@ def check_declarative_intent_params(module, want): time.sleep(module.params['delay']) + if w['interface_type']: + return + cmd = [{'command': 'show interface {0}'.format(w['name']), 'output': 'text'}] output = run_commands(module, cmd, check_rc=False) if output: @@ -755,7 +771,7 @@ def main(): argument_spec.update(element_spec) argument_spec.update(nxos_argument_spec) - required_one_of = [['name', 'aggregate']] + required_one_of = [['name', 'aggregate', 'interface_type']] mutually_exclusive = [['name', 'aggregate'], ['name', 'interface_type']] diff --git a/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface b/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface new file mode 100644 index 00000000000..f50b8f24e90 --- /dev/null +++ b/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface @@ -0,0 +1,129 @@ +{ + "TABLE_interface": { + "ROW_interface": [ + { + "interface": "Ethernet2/1", + "state": "up", + "admin_state": "up", + "share_state": "Dedicated", + "eth_hw_desc": "Ethernet", + "eth_hw_addr": "fa16.3e50.6647", + "eth_bia_addr": "fa16.3e50.6619", + "desc": "Test aggregation on first interface", + "eth_mtu": "1500", + "eth_bw": 1000000, + "eth_dly": 10, + "eth_reliability": "255", + "eth_txload": "1", + "eth_rxload": "1", + "medium": "broadcast", + "eth_mode": "routed", + "eth_duplex": "full", + "eth_speed": "1000 Mb/s", + "eth_beacon": "off", + "eth_autoneg": "off", + "eth_in_flowctrl": "off", + "eth_out_flowctrl": "off", + "eth_mdix": "off", + "eth_swt_monitor": "off", + "eth_ethertype": "0x8100", + "eth_eee_state": "n/a", + "eth_link_flapped": "4d15h", + "eth_clear_counters": "never", + "eth_reset_cntr": 87, + "eth_load_interval1_rx": 0, + "eth_inrate1_bits": 0, + "eth_inrate1_pkts": 0, + "eth_load_interval1_tx": 0, + "eth_outrate1_bits": 0, + "eth_outrate1_pkts": 0, + "eth_inrate1_summary_bits": "0 bps", + "eth_inrate1_summary_pkts": "0 pps", + "eth_outrate1_summary_bits": "0 bps", + "eth_outrate1_summary_pkts": "0 pps", + "eth_load_interval2_rx": 0, + "eth_inrate2_bits": 0, + "eth_inrate2_pkts": 0, + "eth_load_interval2_tx": 0, + "eth_outrate2_bits": 0, + "eth_outrate2_pkts": 0, + "eth_inrate2_summary_bits": "0 bps", + "eth_inrate2_summary_pkts": "0 pps", + "eth_outrate2_summary_bits": "0 bps", + "eth_outrate2_summary_pkts": "0 pps", + "eth_inucast": 0, + "eth_inmcast": 0, + "eth_inbcast": 0, + "eth_inpkts": 0, + "eth_inbytes": 0, + "eth_jumbo_inpkts": 0, + "eth_storm_supp": 0, + "eth_runts": 0, + "eth_giants": 0, + "eth_crc": 0, + "eth_nobuf": 0, + "eth_inerr": 0, + "eth_frame": 0, + "eth_overrun": 0, + "eth_underrun": 0, + "eth_ignored": 0, + "eth_watchdog": 0, + "eth_bad_eth": 0, + "eth_bad_proto": 0, + "eth_in_ifdown_drops": 0, + "eth_dribble": 0, + "eth_indiscard": 0, + "eth_inpause": 0, + "eth_outucast": 0, + "eth_outmcast": 0, + "eth_outbcast": 0, + "eth_outpkts": 0, + "eth_outbytes": 0, + "eth_jumbo_outpkts": 0, + "eth_outerr": 0, + "eth_coll": 0, + "eth_deferred": 0, + "eth_latecoll": 0, + "eth_lostcarrier": 0, + "eth_nocarrier": 0, + "eth_babbles": 0, + "eth_outdiscard": 0, + "eth_outpause": 0 + }, + { + "interface": "loopback0", + "state": "up", + "admin_state": "up", + "eth_hw_desc": "Loopback", + "desc": "Loopback", + "eth_ip_addr": "192.168.255.1", + "eth_ip_mask": 32, + "eth_ip_prefix": "192.168.255.1", + "eth_mtu": "1500", + "eth_bw": 8000000, + "eth_dly": 5000, + "eth_reliability": "255", + "eth_txload": "1", + "eth_rxload": "1", + "medium": "broadcast", + "eth_mode": "routed", + "eth_mdix": "off", + "loop_in_pkts": 1451854, + "loop_in_bytes": 75138128, + "loop_in_mcast": 0, + "loop_in_compressed": 0, + "loop_in_errors": 0, + "loop_in_frame": 0, + "loop_in_overrun": 0, + "loop_in_fifo": 0, + "loop_out_pkts": 0, + "loop_out_bytes": 0, + "loop_out_underruns": 0, + "loop_out_errors": 0, + "loop_out_collisions": 0, + "loop_out_fifo": 0, + "loop_out_carriers": 0 + } + ] + } +} diff --git a/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface_Ethernet2_1 b/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface_Ethernet2_1 new file mode 100644 index 00000000000..f478350f0dd --- /dev/null +++ b/test/units/modules/network/nxos/fixtures/nxos_interface/show_interface_Ethernet2_1 @@ -0,0 +1,96 @@ +{ + "TABLE_interface": { + "ROW_interface": { + "interface": "Ethernet2/1", + "state": "up", + "admin_state": "up", + "share_state": "Dedicated", + "eth_hw_desc": "Ethernet", + "eth_hw_addr": "fa16.3e00.0001", + "eth_bia_addr": "fa16.3e27.f279", + "desc": "to nxos03", + "eth_ip_addr": "10.0.0.45", + "eth_ip_mask": 30, + "eth_ip_prefix": "10.0.0.44", + "eth_mtu": "1500", + "eth_bw": 1000000, + "eth_dly": 10, + "eth_reliability": "255", + "eth_txload": "1", + "eth_rxload": "1", + "medium": "broadcast", + "eth_mode": "routed", + "eth_duplex": "full", + "eth_speed": "1000 Mb/s", + "eth_beacon": "off", + "eth_autoneg": "off", + "eth_in_flowctrl": "off", + "eth_out_flowctrl": "off", + "eth_mdix": "off", + "eth_swt_monitor": "off", + "eth_ethertype": "0x8100", + "eth_eee_state": "n/a", + "eth_link_flapped": "14week(s) 0day(s)", + "eth_clear_counters": "never", + "eth_reset_cntr": 1, + "eth_load_interval1_rx": 0, + "eth_inrate1_bits": 0, + "eth_inrate1_pkts": 0, + "eth_load_interval1_tx": 0, + "eth_outrate1_bits": 0, + "eth_outrate1_pkts": 0, + "eth_inrate1_summary_bits": "0 bps", + "eth_inrate1_summary_pkts": "0 pps", + "eth_outrate1_summary_bits": "0 bps", + "eth_outrate1_summary_pkts": "0 pps", + "eth_load_interval2_rx": 0, + "eth_inrate2_bits": 0, + "eth_inrate2_pkts": 0, + "eth_load_interval2_tx": 0, + "eth_outrate2_bits": 0, + "eth_outrate2_pkts": 0, + "eth_inrate2_summary_bits": "0 bps", + "eth_inrate2_summary_pkts": "0 pps", + "eth_outrate2_summary_bits": "0 bps", + "eth_outrate2_summary_pkts": "0 pps", + "eth_inucast": 0, + "eth_inmcast": 0, + "eth_inbcast": 0, + "eth_inpkts": 0, + "eth_inbytes": 0, + "eth_jumbo_inpkts": 0, + "eth_storm_supp": 0, + "eth_runts": 0, + "eth_giants": 0, + "eth_crc": 0, + "eth_nobuf": 0, + "eth_inerr": 0, + "eth_frame": 0, + "eth_overrun": 0, + "eth_underrun": 0, + "eth_ignored": 0, + "eth_watchdog": 0, + "eth_bad_eth": 0, + "eth_bad_proto": 0, + "eth_in_ifdown_drops": 0, + "eth_dribble": 0, + "eth_indiscard": 0, + "eth_inpause": 0, + "eth_outucast": 0, + "eth_outmcast": 0, + "eth_outbcast": 0, + "eth_outpkts": 0, + "eth_outbytes": 0, + "eth_jumbo_outpkts": 0, + "eth_outerr": 0, + "eth_coll": 0, + "eth_deferred": 0, + "eth_latecoll": 0, + "eth_lostcarrier": 0, + "eth_nocarrier": 0, + "eth_babbles": 0, + "eth_outdiscard": 0, + "eth_outpause": 0 + } + } +} diff --git a/test/units/modules/network/nxos/fixtures/nxos_interface/show_run_interface_Ethernet2_1 b/test/units/modules/network/nxos/fixtures/nxos_interface/show_run_interface_Ethernet2_1 new file mode 100644 index 00000000000..e60c3b4c01d --- /dev/null +++ b/test/units/modules/network/nxos/fixtures/nxos_interface/show_run_interface_Ethernet2_1 @@ -0,0 +1,8 @@ +interface Ethernet1/5 + description to csr02 + no switchport + speed 1000 + mtu 1500 + mac-address fa16.3e00.000b + ip address 172.31.0.66/30 + no shutdown diff --git a/test/units/modules/network/nxos/test_nxos_interface.py b/test/units/modules/network/nxos/test_nxos_interface.py index 20e4cddf667..52752d62b07 100644 --- a/test/units/modules/network/nxos/test_nxos_interface.py +++ b/test/units/modules/network/nxos/test_nxos_interface.py @@ -73,3 +73,19 @@ class TestNxosInterfaceModule(TestNxosModule): set_module_args(dict(interface='loopback0', state='absent')) result = self.execute_module(changed=False) self.assertEqual(result['commands'], []) + + def test_nxos_interface_type(self): + set_module_args(dict(interface_type='loopback', state='absent')) + result = self.execute_module(changed=True) + self.assertEqual(result['commands'], ['no interface loopback0']) + + def test_nxos_interface_mtu(self): + set_module_args(dict(interface='Ethernet2/1', mode='layer2', mtu='1800')) + result = self.execute_module(changed=True) + self.assertEqual(result['commands'], ['interface Ethernet2/1', 'switchport', 'mtu 1800', + 'interface Ethernet2/1', 'no shutdown']) + + def test_nxos_interface_speed_idempotence(self): + set_module_args(dict(interface='Ethernet2/1', speed='1000')) + result = self.execute_module(changed=False) + self.assertEqual(result['commands'], [])