diff --git a/config/main.py b/config/main.py index 1fe8df3cdfa..e544d4b9d86 100644 --- a/config/main.py +++ b/config/main.py @@ -7120,6 +7120,50 @@ def disable(): # 'config interface ipv6 enable use-link-local-only ' # +def _get_ipv6_ll_intf_type(interface_name): + """Map an interface name (full or shortname) to the CONFIG_DB table used + for ipv6_use_link_local_only. + """ + table_name = get_interface_table_name(interface_name) + if table_name in ("INTERFACE", "PORTCHANNEL_INTERFACE", + "VLAN_INTERFACE", "VLAN_SUB_INTERFACE"): + return table_name + return "" + + +def _validate_ipv6_ll_interface(ctx, db, interface_name, interface_type): + """Shared validation for ipv6 link-local enable/disable commands.""" + if not interface_type: + ctx.fail("'interface_name' is not valid. Valid names " + "[Ethernet/Eth/PortChannel/Po/Vlan/Sub-interface]") + + if interface_type == "VLAN_INTERFACE": + if not clicommon.is_valid_vlan_interface(db, interface_name): + ctx.fail("Interface name %s is invalid." + " Please enter a valid interface name!!" + % (interface_name)) + else: + if interface_name_is_valid(db, interface_name) is False: + ctx.fail("Interface name %s is invalid." + " Please enter a valid interface name!!" + % (interface_name)) + + is_sub_intf = VLAN_SUB_INTERFACE_SEPARATOR in interface_name + if not is_sub_intf: + portchannel_member_table = db.get_table('PORTCHANNEL_MEMBER') + if interface_is_in_portchannel(portchannel_member_table, + interface_name): + ctx.fail("{} is configured as a member of portchannel." + " Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + vlan_member_table = db.get_table('VLAN_MEMBER') + if interface_is_in_vlan(vlan_member_table, interface_name): + ctx.fail("{} is configured as a member of vlan." + " Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + @enable.command('use-link-local-only') @click.pass_context @click.argument('interface_name', metavar='', required=True) @@ -7132,34 +7176,8 @@ def enable_use_link_local_only(ctx, interface_name): if interface_name is None: ctx.fail("'interface_name' is None!") - if interface_name.startswith("Ethernet"): - interface_type = "INTERFACE" - elif interface_name.startswith("PortChannel"): - interface_type = "PORTCHANNEL_INTERFACE" - elif interface_name.startswith("Vlan"): - interface_type = "VLAN_INTERFACE" - else: - ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan]") - - if (interface_type == "INTERFACE" ) or (interface_type == "PORTCHANNEL_INTERFACE"): - if interface_name_is_valid(db, interface_name) is False: - ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) - - if (interface_type == "VLAN_INTERFACE"): - if not clicommon.is_valid_vlan_interface(db, interface_name): - ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) - - portchannel_member_table = db.get_table('PORTCHANNEL_MEMBER') - - if interface_is_in_portchannel(portchannel_member_table, interface_name): - ctx.fail("{} is configured as a member of portchannel. Cannot configure the IPv6 link local mode!" - .format(interface_name)) - - vlan_member_table = db.get_table('VLAN_MEMBER') - - if interface_is_in_vlan(vlan_member_table, interface_name): - ctx.fail("{} is configured as a member of vlan. Cannot configure the IPv6 link local mode!" - .format(interface_name)) + interface_type = _get_ipv6_ll_intf_type(interface_name) + _validate_ipv6_ll_interface(ctx, db, interface_name, interface_type) interface_dict = db.get_table(interface_type) set_ipv6_link_local_only_on_interface(db, interface_dict, interface_type, interface_name, "enable") @@ -7180,34 +7198,8 @@ def disable_use_link_local_only(ctx, interface_name): if interface_name is None: ctx.fail("'interface_name' is None!") - interface_type = "" - if interface_name.startswith("Ethernet"): - interface_type = "INTERFACE" - elif interface_name.startswith("PortChannel"): - interface_type = "PORTCHANNEL_INTERFACE" - elif interface_name.startswith("Vlan"): - interface_type = "VLAN_INTERFACE" - else: - ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan]") - - if (interface_type == "INTERFACE" ) or (interface_type == "PORTCHANNEL_INTERFACE"): - if interface_name_is_valid(db, interface_name) is False: - ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) - - if (interface_type == "VLAN_INTERFACE"): - if not clicommon.is_valid_vlan_interface(db, interface_name): - ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) - - portchannel_member_table = db.get_table('PORTCHANNEL_MEMBER') - - if interface_is_in_portchannel(portchannel_member_table, interface_name): - ctx.fail("{} is configured as a member of portchannel. Cannot configure the IPv6 link local mode!" - .format(interface_name)) - - vlan_member_table = db.get_table('VLAN_MEMBER') - if interface_is_in_vlan(vlan_member_table, interface_name): - ctx.fail("{} is configured as a member of vlan. Cannot configure the IPv6 link local mode!" - .format(interface_name)) + interface_type = _get_ipv6_ll_intf_type(interface_name) + _validate_ipv6_ll_interface(ctx, db, interface_name, interface_type) interface_dict = db.get_table(interface_type) set_ipv6_link_local_only_on_interface(db, interface_dict, interface_type, interface_name, "disable") diff --git a/show/main.py b/show/main.py index 5691638bea6..ed48216bd27 100755 --- a/show/main.py +++ b/show/main.py @@ -1636,6 +1636,18 @@ def link_local_mode(namespace, verbose): else: body.append([port, 'Disabled']) + sub_intf_dict = config_db.get_table('VLAN_SUB_INTERFACE') + for sub_intf in sub_intf_dict.keys(): + if not isinstance(sub_intf, tuple): + value = sub_intf_dict[sub_intf] + if 'ipv6_use_link_local_only' in value: + if value['ipv6_use_link_local_only'] == 'enable': + body.append([sub_intf, 'Enabled']) + else: + body.append([sub_intf, 'Disabled']) + else: + body.append([sub_intf, 'Disabled']) + click.echo(tabulate(body, header, tablefmt="grid")) # diff --git a/tests/ipv6_link_local_test.py b/tests/ipv6_link_local_test.py index 47f0c2a1152..d3d1ff4ed89 100644 --- a/tests/ipv6_link_local_test.py +++ b/tests/ipv6_link_local_test.py @@ -92,6 +92,14 @@ +------------------+----------+ | Vlan4000 | Disabled | +------------------+----------+ +| Vlan1111 | Disabled | ++------------------+----------+ +| Ethernet0.10 | Disabled | ++------------------+----------+ +| Eth36.10 | Disabled | ++------------------+----------+ +| Po0001.10 | Disabled | ++------------------+----------+ """ class TestIPv6LinkLocal(object): @@ -224,6 +232,128 @@ def test_config_enable_disable_ipv6_link_local_on_all_valid_interfaces(self): assert result.exit_code == 0 assert result.output == '' + def test_config_enable_disable_ipv6_link_local_on_sub_interface(self): + runner = CliRunner() + db = Db() + obj = {'config_db': db.cfgdb} + + enable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["enable"] + .commands["use-link-local-only"]) + disable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["disable"] + .commands["use-link-local-only"]) + + result = runner.invoke(enable_cmd, ["Ethernet0.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + result = runner.invoke(disable_cmd, ["Ethernet0.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + def test_config_enable_disable_ipv6_link_local_on_shortname_sub_interface(self): + runner = CliRunner() + db = Db() + obj = {'config_db': db.cfgdb} + + enable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["enable"] + .commands["use-link-local-only"]) + disable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["disable"] + .commands["use-link-local-only"]) + + result = runner.invoke(enable_cmd, ["Eth36.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + result = runner.invoke(disable_cmd, ["Eth36.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + result = runner.invoke(enable_cmd, ["Po0001.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + result = runner.invoke(disable_cmd, ["Po0001.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == '' + + def test_config_enable_ipv6_link_local_on_nonexistent_sub_interface(self): + runner = CliRunner() + db = Db() + obj = {'config_db': db.cfgdb} + + enable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["enable"] + .commands["use-link-local-only"]) + + result = runner.invoke(enable_cmd, ["Ethernet0.999"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert ('invalid' in result.output.lower() + or 'Invalid' in result.output) + + def test_config_enable_ipv6_link_local_on_nonexistent_shortname(self): + runner = CliRunner() + db = Db() + obj = {'config_db': db.cfgdb} + + enable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["enable"] + .commands["use-link-local-only"]) + + result = runner.invoke(enable_cmd, ["Eth99.99"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert 'invalid' in result.output.lower() + + def test_config_enable_ipv6_link_local_on_bogus_prefix(self): + runner = CliRunner() + db = Db() + obj = {'config_db': db.cfgdb} + + enable_cmd = (config.config.commands["interface"] + .commands["ipv6"].commands["enable"] + .commands["use-link-local-only"]) + + result = runner.invoke(enable_cmd, ["Et36.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert 'not valid' in result.output + + result = runner.invoke(enable_cmd, ["Foo0.10"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert 'not valid' in result.output + + def test_show_ipv6_link_local_mode_includes_sub_interfaces(self): + runner = CliRunner() + db = Db() + obj = {'db': db.cfgdb} + + show_cmd = show.cli.commands["ipv6"].commands["link-local-mode"] + result = runner.invoke(show_cmd, [], obj=obj) + print(result.output) + assert 'Ethernet0.10' in result.output + def test_vlan_member_add_on_link_local_interface(self): runner = CliRunner() db = Db()