diff --git a/config/main.py b/config/main.py index 83637c1421..93f49c570f 100644 --- a/config/main.py +++ b/config/main.py @@ -2435,6 +2435,7 @@ def remove_portchannel(ctx, portchannel_name): ctx.fail("Error: Portchannel {} contains members. Remove members before deleting Portchannel!".format(portchannel_name)) try: + db.set_entry('PORTCHANNEL_INTERFACE', portchannel_name, None) db.set_entry('PORTCHANNEL', portchannel_name, None) except JsonPatchConflict: ctx.fail("{} is not present.".format(portchannel_name)) @@ -2673,6 +2674,38 @@ def set_portchannel_retry_count(ctx, portchannel_name, retry_count): ctx.fail("Unable to set the retry count: {}".format(e)) +@portchannel.group(cls=clicommon.AbbreviationGroup, name='mac-addr') +@click.pass_context +def portchannel_mac_addr(ctx): + pass + + +@portchannel_mac_addr.command('set') +@click.argument('portchannel_name', metavar='', required=True) +@click.argument('mac_addr', metavar='', required=True) +@click.pass_context +def set_portchannel_mac_addr(ctx, portchannel_name, mac_addr): + """Set the mac address for a port channel""" + db = ValidatedConfigDBConnector(ctx.obj['db']) + + # Don't proceed if the port channel name is not valid + if is_portchannel_name_valid(portchannel_name) is False: + ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" + .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) + + # Don't proceed if the port channel does not exist + if is_portchannel_present_in_db(db, portchannel_name) is False: + ctx.fail("{} is not present.".format(portchannel_name)) + + if not re.match("^[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac_addr.lower()): + ctx.fail("Provided mac address is not valid") + + try: + db.set_entry("PORTCHANNEL_INTERFACE", portchannel_name, {"mac_addr": mac_addr}) + except ValueError: + ctx.fail("Portchannel name is invalid or nonexistent") + + # # 'mirror_session' group ('config mirror_session ...') # @@ -4975,9 +5008,12 @@ def remove(ctx, interface_name, ip_addr): clicommon.run_command(command) remove_router_interface_ip_address(config_db, interface_name, ip_address) interface_addresses = get_interface_ipaddresses(config_db, interface_name) - if len(interface_addresses) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False and get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) != "enable": + if len(interface_addresses) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False and \ + get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) != "enable": if table_name != "VLAN_SUB_INTERFACE": - config_db.set_entry(table_name, interface_name, None) + interface_entry = config_db.get_entry(table_name, interface_name) + if len(interface_entry) == 0: + config_db.set_entry(table_name, interface_name, None) # # 'loopback-action' subcommand diff --git a/tests/portchannel_test.py b/tests/portchannel_test.py index 9b8bf56863..7cdb6774fc 100644 --- a/tests/portchannel_test.py +++ b/tests/portchannel_test.py @@ -318,6 +318,42 @@ def test_set_non_existing_portchannel_retry_count(self): assert result.exit_code != 0 assert "Error: PortChannel0005 is not present." in result.output + def test_set_invalid_portchannel_mac_addr(self): + runner = CliRunner() + db = Db() + obj = {'db': db.cfgdb} + + result = runner.invoke(config.config.commands["portchannel"].commands["mac-addr"].commands["set"], + ["Ethernet48", "00:11:22:33:44:55"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Ethernet48 is invalid!" in result.output + + def test_set_non_existing_portchannel_mac_addr(self): + runner = CliRunner() + db = Db() + obj = {'db': db.cfgdb} + + result = runner.invoke(config.config.commands["portchannel"].commands["mac-addr"].commands["set"], + ["PortChannel0005", "00:11:22:33:44:55"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: PortChannel0005 is not present." in result.output + + def test_set_portchannel_mac_addr(self): + runner = CliRunner() + db = Db() + obj = {'db': db.cfgdb} + + result = runner.invoke(config.config.commands["portchannel"].commands["mac-addr"].commands["set"], + ["PortChannel1001", "00:11:22:33:44:55"], obj=obj) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == "" + originalSubprocessPopen = subprocess.Popen class SubprocessMock: diff --git a/utilities_common/cli.py b/utilities_common/cli.py index c8a314b704..0774d56647 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -415,10 +415,14 @@ def is_port_router_interface(config_db, port): """Check if port is a router interface""" interface_table = config_db.get_table('INTERFACE') - for intf in interface_table: - if port == intf: + for intf in interface_table.keys(): + if is_ip_prefix_in_key(intf) and port == intf[0]: return True + entry = config_db.get_entry('INTERFACE', port) + if entry.get('ipv6_use_link_local_only') == 'enable': + return True + return False @@ -426,10 +430,14 @@ def is_pc_router_interface(config_db, pc): """Check if portchannel is a router interface""" pc_interface_table = config_db.get_table('PORTCHANNEL_INTERFACE') - for intf in pc_interface_table: - if pc == intf: + for intf in pc_interface_table.keys(): + if is_ip_prefix_in_key(intf) and pc == intf[0]: return True + entry = config_db.get_entry('PORTCHANNEL_INTERFACE', pc) + if entry.get('ipv6_use_link_local_only') == 'enable': + return True + return False