Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 113 additions & 93 deletions config/main.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,77 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port,

return True

def add_del_route(ctx, command_str, command):
if len(command_str) < 4 or len(command_str) > 9:
ctx.fail("argument is not in pattern prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>!")
if "prefix" not in command_str:
ctx.fail("argument is incomplete, prefix not found!")
if "nexthop" not in command_str:
ctx.fail("argument is incomplete, nexthop not found!")
for i in range(0,len(command_str)):
if "nexthop" == command_str[i]:
prefix_str = command_str[:i]
nexthop_str = command_str[i:]
vrf_name = ""
prefix_mask = ""
config_entry = {}

if command == 'add':
cmd = 'sudo vtysh -c "configure terminal" -c "ip route'
else:
cmd = 'sudo vtysh -c "configure terminal" -c "no ip route'

if prefix_str:
if len(prefix_str) == 2:
prefix_mask = prefix_str[1]
cmd += ' {}'.format(prefix_mask)
elif len(prefix_str) == 4:
vrf_name = prefix_str[2]
if not vrf_name.startswith("Vrf"):
ctx.fail("vrf is not start with Vrf")
prefix_mask = prefix_str[3]
cmd += ' {}'.format(prefix_mask)
else:
ctx.fail("prefix is not in pattern!")
if nexthop_str:
if len(nexthop_str) == 2:
ip = nexthop_str[1]
if vrf_name == "":
cmd += ' {}'.format(ip)
config_entry["nexthop"] = ip
else:
cmd += ' {} vrf {}'.format(ip, vrf_name)
config_entry["nexthop"] = ip
config_entry["vrf_name"] = vrf_name
elif len(nexthop_str) == 3:
dev_name = nexthop_str[2]
if vrf_name == "":
cmd += ' {}'.format(dev_name)
config_entry["dev_name"] = dev_name
else:
cmd += ' {} vrf {}'.format(dev_name, vrf_name)
config_entry["dev_name"] = dev_name
config_entry["vrf_name"] = vrf_name
elif len(nexthop_str) == 4:
vrf_name_dst = nexthop_str[2]
if not vrf_name_dst.startswith("Vrf"):
ctx.fail("vrf is not start with Vrf")
ip = nexthop_str[3]
if vrf_name == "":
cmd += ' {} nexthop-vrf {}'.format(ip, vrf_name_dst)
config_entry["nexthop"] = ip
config_entry["nexthop_vrf"] = vrf_name_dst
else:
cmd += ' {} vrf {} nexthop-vrf {}'.format(ip, vrf_name, vrf_name_dst)
config_entry["nexthop"] = ip
config_entry["vrf_name"] = vrf_name
config_entry["nexthop_vrf"] = vrf_name_dst
else:
ctx.fail("nexthop is not in pattern!")
cmd += '"'
# Return cmd to vtysh, dictionary to CONFIG_DB and nexthop name
return {'cmd':cmd, 'config_dict':config_entry, 'name':prefix_mask}

def update_sonic_environment():
"""Prepare sonic environment variable using SONiC environment template file.
"""
Expand Down Expand Up @@ -3153,111 +3224,60 @@ def del_vrf_vni_map(ctx, vrfname):
@click.pass_context
def route(ctx):
"""route-related configuration tasks"""
pass
config_db = ConfigDBConnector()
config_db.connect()
ctx.obj = {}
ctx.obj['config_db'] = config_db
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add the support for multi asic ?
Similar to how it is done for interface command https://github.com/Azure/sonic-utilities/blob/08337aa7637b290bb8407c38b2a5dbe3e8383b3e/config/main.py#L2359

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arlakshm, can we add multiasic as another enhancement PR?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prsunny, Sure. Can we create an issue or task to track this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created issue - #1608


@route.command('update',context_settings={"ignore_unknown_options":True})
@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output")
@click.pass_context
def update_route(ctx, verbose):
# Transfer static routes from CONFIG_DB to FRR
config_db = ctx.obj['config_db']
route_keys = config_db.get_keys("STATIC_ROUTE")
for key in route_keys:
entry = config_db.get_entry("STATIC_ROUTE", key)

argument = ['prefix']
if 'vrf_name' in entry:
argument.append('vrf')
argument.append(entry['vrf_name'])
argument.append(key)
argument.append('nexthop')
if 'nexthop_vrf' in entry:
argument.append('vrf')
argument.append(entry['nexthop_vrf'])
if 'nexthop' in entry:
argument.append(entry['nexthop'])
if 'dev_name' in entry:
argument.append('dev')
argument.append(entry['dev_name'])

route = add_del_route(ctx, argument, 'add')
clicommon.run_command(route['cmd'])
if verbose:
click.echo('Added static route {}'.format(route['name']))

@route.command('add', context_settings={"ignore_unknown_options":True})
@click.argument('command_str', metavar='prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>', nargs=-1, type=click.Path())
@click.pass_context
def add_route(ctx, command_str):
"""Add route command"""
if len(command_str) < 4 or len(command_str) > 9:
ctx.fail("argument is not in pattern prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>!")
if "prefix" not in command_str:
ctx.fail("argument is incomplete, prefix not found!")
if "nexthop" not in command_str:
ctx.fail("argument is incomplete, nexthop not found!")
for i in range(0, len(command_str)):
if "nexthop" == command_str[i]:
prefix_str = command_str[:i]
nexthop_str = command_str[i:]
vrf_name = ""
cmd = 'sudo vtysh -c "configure terminal" -c "ip route'
if prefix_str:
if len(prefix_str) == 2:
prefix_mask = prefix_str[1]
cmd += ' {}'.format(prefix_mask)
elif len(prefix_str) == 4:
vrf_name = prefix_str[2]
prefix_mask = prefix_str[3]
cmd += ' {}'.format(prefix_mask)
else:
ctx.fail("prefix is not in pattern!")
if nexthop_str:
if len(nexthop_str) == 2:
ip = nexthop_str[1]
if vrf_name == "":
cmd += ' {}'.format(ip)
else:
cmd += ' {} vrf {}'.format(ip, vrf_name)
elif len(nexthop_str) == 3:
dev_name = nexthop_str[2]
if vrf_name == "":
cmd += ' {}'.format(dev_name)
else:
cmd += ' {} vrf {}'.format(dev_name, vrf_name)
elif len(nexthop_str) == 4:
vrf_name_dst = nexthop_str[2]
ip = nexthop_str[3]
if vrf_name == "":
cmd += ' {} nexthop-vrf {}'.format(ip, vrf_name_dst)
else:
cmd += ' {} vrf {} nexthop-vrf {}'.format(ip, vrf_name, vrf_name_dst)
else:
ctx.fail("nexthop is not in pattern!")
cmd += '"'
clicommon.run_command(cmd)
route = add_del_route(ctx, command_str, 'add')
config_db = ctx.obj['config_db']
config_db.set_entry("STATIC_ROUTE", route['name'], route['config_dict'])
clicommon.run_command(route['cmd'])

@route.command('del', context_settings={"ignore_unknown_options":True})
@click.argument('command_str', metavar='prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>', nargs=-1, type=click.Path())
@click.pass_context
def del_route(ctx, command_str):
"""Del route command"""
if len(command_str) < 4 or len(command_str) > 9:
ctx.fail("argument is not in pattern prefix [vrf <vrf_name>] <A.B.C.D/M> nexthop <[vrf <vrf_name>] <A.B.C.D>>|<dev <dev_name>>!")
if "prefix" not in command_str:
ctx.fail("argument is incomplete, prefix not found!")
if "nexthop" not in command_str:
ctx.fail("argument is incomplete, nexthop not found!")
for i in range(0, len(command_str)):
if "nexthop" == command_str[i]:
prefix_str = command_str[:i]
nexthop_str = command_str[i:]
vrf_name = ""
cmd = 'sudo vtysh -c "configure terminal" -c "no ip route'
if prefix_str:
if len(prefix_str) == 2:
prefix_mask = prefix_str[1]
cmd += ' {}'.format(prefix_mask)
elif len(prefix_str) == 4:
vrf_name = prefix_str[2]
prefix_mask = prefix_str[3]
cmd += ' {}'.format(prefix_mask)
else:
ctx.fail("prefix is not in pattern!")
if nexthop_str:
if len(nexthop_str) == 2:
ip = nexthop_str[1]
if vrf_name == "":
cmd += ' {}'.format(ip)
else:
cmd += ' {} vrf {}'.format(ip, vrf_name)
elif len(nexthop_str) == 3:
dev_name = nexthop_str[2]
if vrf_name == "":
cmd += ' {}'.format(dev_name)
else:
cmd += ' {} vrf {}'.format(dev_name, vrf_name)
elif len(nexthop_str) == 4:
vrf_name_dst = nexthop_str[2]
ip = nexthop_str[3]
if vrf_name == "":
cmd += ' {} nexthop-vrf {}'.format(ip, vrf_name_dst)
else:
cmd += ' {} vrf {} nexthop-vrf {}'.format(ip, vrf_name, vrf_name_dst)
else:
ctx.fail("nexthop is not in pattern!")
cmd += '"'
clicommon.run_command(cmd)
route = add_del_route(ctx, command_str, 'del')
config_db = ctx.obj['config_db']
config_db.set_entry("STATIC_ROUTE", route['name'], None)
clicommon.run_command(route['cmd'])

#
# 'acl' group ('config acl ...')
Expand Down
100 changes: 100 additions & 0 deletions tests/static_routes_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os
import traceback
from unittest import mock

from click.testing import CliRunner

import config.main as config
import show.main as show
from .utils import get_result_and_return_code
from utilities_common.db import Db

ROUTE_UPDATE_STR = '''
Added static route 1.2.3.4/32
Added static route 2.2.3.4/32
Added static route 3.2.3.4/32
'''


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

'''Add'''
def test_add_simple_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route add prefix 1.2.3.4/32 nexthop 30.0.0.5
result = runner.invoke(config.config.commands["route"].commands["add"], \
["prefix", "1.2.3.4/32", "nexthop", "30.0.0.5"], obj=obj)
print(result.exit_code, result.output)
assert ('1.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')
assert db.cfgdb.get_entry('STATIC_ROUTE', '1.2.3.4/32') == {"nexthop": "30.0.0.5"}

def test_add_vrf_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route add prefix 2.2.3.4/32 nexthop 30.0.0.6
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please update the comment to include vrf

result = runner.invoke(config.config.commands["route"].commands["add"], \
["prefix", "vrf", "Vrfblue", "2.2.3.4/32", "nexthop", "30.0.0.6"], obj=obj)
print(result.exit_code, result.output)
assert ('2.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')
assert db.cfgdb.get_entry('STATIC_ROUTE', '2.2.3.4/32') == {"nexthop": "30.0.0.6", "vrf_name": "Vrfblue"}

def test_add_dest_vrf_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route add prefix 3.2.3.4/32 nexthop 30.0.0.6
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, please update the correct command with vrf

result = runner.invoke(config.config.commands["route"].commands["add"], \
["prefix", "3.2.3.4/32", "nexthop", "vrf", "Vrfred", "30.0.0.6"], obj=obj)
print(result.exit_code, result.output)
assert ('3.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')
assert db.cfgdb.get_entry('STATIC_ROUTE', '3.2.3.4/32') == {"nexthop": "30.0.0.6", "nexthop_vrf": "Vrfred"}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to follow a certain schema design for the STATIC_ROUTE table. In sonic-net/SONiC#585, there is a schema for the table but it seems different here.


'''Del'''
def test_del_simple_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route del prefix 1.2.3.4/32 nexthop 30.0.0.5
result = runner.invoke(config.config.commands["route"].commands["del"], \
["prefix", "1.2.3.4/32", "nexthop", "30.0.0.5"], obj=obj)
print(result.exit_code, result.output)
assert not ('1.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')

def test_del_vrf_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route del prefix 2.2.3.4/32 nexthop 30.0.0.6
result = runner.invoke(config.config.commands["route"].commands["del"], \
["prefix", "vrf", "Vrfblue", "2.2.3.4/32", "nexthop", "30.0.0.6"], obj=obj)
print(result.exit_code, result.output)
assert not ('2.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')

def test_del_dest_vrf_static_route(self):
db = Db()
runner = CliRunner()
obj = {'config_db':db.cfgdb}

# config route del prefix 3.2.3.4/32 nexthop 30.0.0.6
result = runner.invoke(config.config.commands["route"].commands["del"], \
["prefix", "3.2.3.4/32", "nexthop", "vrf", "Vrfred", "30.0.0.6"], obj=obj)
print(result.exit_code, result.output)
assert not ('3.2.3.4/32') in db.cfgdb.get_table('STATIC_ROUTE')

@classmethod
def teardown_class(cls):
os.environ['UTILITIES_UNIT_TESTING'] = "0"
print("TEARDOWN")