Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IP interface loopback action #2192

Merged
merged 9 commits into from
Jul 7, 2022
28 changes: 28 additions & 0 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4685,6 +4685,34 @@ def unbind(ctx, interface_name):
remove_router_interface_ip_address(config_db, interface_name, ipaddress)
config_db.set_entry(table_name, interface_name, None)

#
# 'config interface loopback-action <interface-name> <action>'
#

@interface.command()
@click.argument('interface_name', metavar='<interface_name>', required=True)
@click.argument('action', metavar='<action>', required=True)
@click.pass_context
def loopback_action(ctx, interface_name, action):
"""Set IP interface loopback action"""
config_db = ctx.obj['config_db']

if clicommon.get_interface_naming_mode() == "alias":
interface_name = interface_alias_to_name(config_db, interface_name)
if interface_name is None:
ctx.fail('Interface {} is invalid'.format(interface_name))

if not clicommon.is_interface_in_config_db(config_db, interface_name):
ctx.fail('Interface {} is not an IP interface'.format(interface_name))

allowed_actions = ['drop', 'forward']
if action not in allowed_actions:
ctx.fail('Invalid action')

table_name = get_interface_table_name(interface_name)
if not table_name:
ctx.fail('Interface {} is invalid'.format(interface_name))
config_db.set_entry(table_name, interface_name, {"loopback_action": action})

#
# 'ipv6' subgroup ('config interface ipv6 ...')
Expand Down
48 changes: 41 additions & 7 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,15 +805,49 @@ def ip():
# Addresses from all scopes are included. Interfaces with no addresses are
# excluded.
#
@ip.command()

@ip.group(invoke_without_command=True)
@multi_asic_util.multi_asic_click_options
def interfaces(namespace, display):
cmd = "sudo ipintutil -a ipv4"
if namespace is not None:
cmd += " -n {}".format(namespace)
@click.pass_context
def interfaces(ctx, namespace, display):
if ctx.invoked_subcommand is None:
cmd = "sudo ipintutil -a ipv4"
if namespace is not None:
cmd += " -n {}".format(namespace)

cmd += " -d {}".format(display)
clicommon.run_command(cmd)
cmd += " -d {}".format(display)
clicommon.run_command(cmd)

#
# 'show ip interfaces loopback-action' command
#

@interfaces.command()
def loopback_action():
"""show ip interfaces loopback-action"""
config_db = ConfigDBConnector()
config_db.connect()
header = ['Interface', 'Action']
body = []

if_tbl = config_db.get_table('INTERFACE')
vlan_if_tbl = config_db.get_table('VLAN_INTERFACE')
po_if_tbl = config_db.get_table('PORTCHANNEL_INTERFACE')
sub_if_tbl = config_db.get_table('VLAN_SUB_INTERFACE')

all_tables = {}
for tbl in [if_tbl, vlan_if_tbl, po_if_tbl, sub_if_tbl]:
all_tables.update(tbl)

if all_tables:
ifs_action = []
ifs = list(all_tables.keys())
for iface in ifs:
if 'loopback_action' in all_tables[iface]:
action = all_tables[iface]['loopback_action']
ifs_action.append([iface, action])
body = natsorted(ifs_action)
click.echo(tabulate(body, header))

#
# 'route' subcommand ("show ip route")
Expand Down
121 changes: 121 additions & 0 deletions tests/loopback_action_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import os
from click.testing import CliRunner
import config.main as config
import show.main as show
from utilities_common.db import Db

show_ip_interfaces_loopback_action_output="""\
Interface Action
--------------- --------
Eth32.10 drop
Ethernet0 forward
PortChannel0001 drop
Vlan3000 forward
"""

class TestLoopbackAction(object):
@classmethod
def setup_class(cls):
print("\nSETUP")
os.environ['UTILITIES_UNIT_TESTING'] = "1"

def test_config_loopback_action_on_physical_interface(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'drop'
iface = 'Ethernet0'

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

table = db.cfgdb.get_table('INTERFACE')
assert(table[iface]['loopback_action'] == action)

print(result.exit_code, result.output)
assert result.exit_code == 0

def test_config_loopback_action_on_port_channel_interface(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'forward'
iface = 'PortChannel0002'

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

table = db.cfgdb.get_table('PORTCHANNEL_INTERFACE')
assert(table[iface]['loopback_action'] == action)

print(result.exit_code, result.output)
assert result.exit_code == 0

def test_config_loopback_action_on_vlan_interface(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'drop'
iface = 'Vlan1000'

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

table = db.cfgdb.get_table('VLAN_INTERFACE')
assert(table[iface]['loopback_action'] == action)

print(result.exit_code, result.output)
assert result.exit_code == 0

def test_config_loopback_action_on_subinterface(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'forward'
iface = 'Ethernet0.10'

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

table = db.cfgdb.get_table('VLAN_SUB_INTERFACE')
assert(table[iface]['loopback_action'] == action)

print(result.exit_code, result.output)
assert result.exit_code == 0

def test_show_ip_interfaces_loopback_action(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["ip"].commands["interfaces"].commands["loopback-action"], [])

print(result.exit_code, result.output)
assert result.exit_code == 0
assert result.output == show_ip_interfaces_loopback_action_output

def test_config_loopback_action_on_non_ip_interface(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'forward'
iface = 'Ethernet0.11'
ERROR_MSG = "Error: Interface {} is not an IP interface".format(iface)

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

print(result.exit_code, result.output)
assert result.exit_code != 0
assert ERROR_MSG in result.output

def test_config_loopback_action_invalid_action(self):
runner = CliRunner()
db = Db()
obj = {'config_db':db.cfgdb}
action = 'xforwardx'
iface = 'Ethernet0'
ERROR_MSG = "Error: Invalid action"

result = runner.invoke(config.config.commands['interface'].commands['loopback-action'], [iface, action], obj=obj)

print(result.exit_code, result.output)
assert result.exit_code != 0
assert ERROR_MSG in result.output

@classmethod
def teardown_class(cls):
print("\nTEARDOWN")
os.environ['UTILITIES_UNIT_TESTING'] = "0"
10 changes: 8 additions & 2 deletions tests/mock_tables/config_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@
},
"VLAN_SUB_INTERFACE|Eth32.10": {
"admin_status": "up",
"loopback_action": "drop",
"vlan": "100"
},
"ACL_RULE|NULL_ROUTE_V4|DEFAULT_RULE": {
Expand Down Expand Up @@ -552,6 +553,9 @@
"VLAN_INTERFACE|Vlan2000": {
"proxy_arp": "enabled"
},
"VLAN_INTERFACE|Vlan3000": {
"loopback_action": "forward"
},
"VLAN_INTERFACE|Vlan1000|192.168.0.1/21": {
"NULL": "NULL"
},
Expand Down Expand Up @@ -636,7 +640,8 @@
"NULL": "NULL"
},
"PORTCHANNEL_INTERFACE|PortChannel0001": {
"ipv6_use_link_local_only": "disable"
"ipv6_use_link_local_only": "disable",
"loopback_action": "drop"
},
"PORTCHANNEL_INTERFACE|PortChannel0002": {
"NULL": "NULL"
Expand Down Expand Up @@ -672,7 +677,8 @@
"NULL": "NULL"
},
"INTERFACE|Ethernet0": {
"ipv6_use_link_local_only": "disable"
"ipv6_use_link_local_only": "disable",
"loopback_action": "forward"
},
"INTERFACE|Ethernet0|14.14.0.1/24": {
"NULL": "NULL"
Expand Down
1 change: 1 addition & 0 deletions utilities_common/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ def is_interface_in_config_db(config_db, interface_name):
if (not interface_name in config_db.get_keys('VLAN_INTERFACE') and
not interface_name in config_db.get_keys('INTERFACE') and
not interface_name in config_db.get_keys('PORTCHANNEL_INTERFACE') and
not interface_name in config_db.get_keys('VLAN_SUB_INTERFACE') and
not interface_name == 'null'):
return False

Expand Down