Skip to content

Commit

Permalink
VXLAN config and show utilities (sonic-net#870)
Browse files Browse the repository at this point in the history
Added support for VXLAN config and commands as described in the PR sonic-net/SONiC#437

config vxlan add/del and config vxlan evpn_nvo add/del 
config vxlan map/map_range add 
show vxlan remote vni/show vxlan remote mac 
show vxlan tunnel 

Co-authored-by: Tapash Das <[email protected]>
Co-authored-by: Karthikeyan Ananthakrishnan <[email protected]>
Co-authored-by: Tapash Das <[email protected]>
  • Loading branch information
4 people authored Dec 19, 2020
1 parent 9419627 commit aad2c38
Show file tree
Hide file tree
Showing 10 changed files with 896 additions and 5 deletions.
53 changes: 52 additions & 1 deletion config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import utilities_common.cli as clicommon
from .utils import log


from . import aaa
from . import chassis_modules
from . import console
Expand All @@ -35,6 +34,7 @@
from . import muxcable
from . import nat
from . import vlan
from . import vxlan
from .config_mgmt import ConfigMgmtDPB

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?'])
Expand Down Expand Up @@ -885,6 +885,7 @@ def config(ctx):
config.add_command(muxcable.muxcable)
config.add_command(nat.nat)
config.add_command(vlan.vlan)
config.add_command(vxlan.vxlan)

@config.command()
@click.option('-y', '--yes', is_flag=True, callback=_abort_if_false,
Expand Down Expand Up @@ -2978,6 +2979,56 @@ def del_vrf(ctx, vrf_name):
config_db.set_entry('VRF', vrf_name, None)


@vrf.command('add_vrf_vni_map')
@click.argument('vrfname', metavar='<vrf-name>', required=True, type=str)
@click.argument('vni', metavar='<vni>', required=True)
@click.pass_context
def add_vrf_vni_map(ctx, vrfname, vni):
config_db = ctx.obj['config_db']
found = 0
if vrfname not in config_db.get_table('VRF').keys():
ctx.fail("vrf {} doesnt exists".format(vrfname))
if not vni.isdigit():
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))

if clicommon.vni_id_is_valid(int(vni)) is False:
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))

vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP')
vxlan_keys = vxlan_table.keys()
if vxlan_keys is not None:
for key in vxlan_keys:
if (vxlan_table[key]['vni'] == vni):
found = 1
break

if (found == 0):
ctx.fail("VLAN VNI not mapped. Please create VLAN VNI map entry first")

found = 0
vrf_table = config_db.get_table('VRF')
vrf_keys = vrf_table.keys()
if vrf_keys is not None:
for vrf_key in vrf_keys:
if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni):
found = 1
break

if (found == 1):
ctx.fail("VNI already mapped to vrf {}".format(vrf_key))

config_db.mod_entry('VRF', vrfname, {"vni": vni})

@vrf.command('del_vrf_vni_map')
@click.argument('vrfname', metavar='<vrf-name>', required=True, type=str)
@click.pass_context
def del_vrf_vni_map(ctx, vrfname):
config_db = ctx.obj['config_db']
if vrfname not in config_db.get_table('VRF').keys():
ctx.fail("vrf {} doesnt exists".format(vrfname))

config_db.mod_entry('VRF', vrfname, {"vni": 0})

#
# 'route' group ('config route ...')
#
Expand Down
273 changes: 273 additions & 0 deletions config/vxlan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
import click
import utilities_common.cli as clicommon

#
# 'vxlan' group ('config vxlan ...')
#
@click.group()
def vxlan():
pass

@vxlan.command('add')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@click.argument('src_ip', metavar='<src_ip>', required=True)
@clicommon.pass_db
def add_vxlan(db, vxlan_name, src_ip):
"""Add VXLAN"""
ctx = click.get_current_context()

if not clicommon.is_ipaddress(src_ip):
ctx.fail("{} invalid src ip address".format(src_ip))

vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL')
if not vxlan_keys:
vxlan_count = 0
else:
vxlan_count = len(vxlan_keys)

if(vxlan_count > 0):
ctx.fail("VTEP already configured.")

fvs = {'src_ip': src_ip}
db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, fvs)

@vxlan.command('del')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@clicommon.pass_db
def del_vxlan(db, vxlan_name):
"""Del VXLAN"""
ctx = click.get_current_context()

vxlan_keys = db.cfgdb.get_keys('VXLAN_EVPN_NVO')
if not vxlan_keys:
vxlan_count = 0
else:
vxlan_count = len(vxlan_keys)

if(vxlan_count > 0):
ctx.fail("Please delete the EVPN NVO configuration.")

vxlan_keys = db.cfgdb.get_keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*")
if not vxlan_keys:
vxlan_count = 0
else:
vxlan_count = len(vxlan_keys)

if(vxlan_count > 0):
ctx.fail("Please delete all VLAN VNI mappings.")

db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, None)

@vxlan.group('evpn_nvo')
def vxlan_evpn_nvo():
pass

@vxlan_evpn_nvo.command('add')
@click.argument('nvo_name', metavar='<nvo_name>', required=True)
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@clicommon.pass_db
def add_vxlan_evpn_nvo(db, nvo_name, vxlan_name):
"""Add NVO"""
ctx = click.get_current_context()
vxlan_keys = db.cfgdb.get_keys('CONFIG_DB', "VXLAN_EVPN_NVO|*")
if not vxlan_keys:
vxlan_count = 0
else:
vxlan_count = len(vxlan_keys)

if(vxlan_count > 0):
ctx.fail("EVPN NVO already configured")

if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
ctx.fail("VTEP {} not configured".format(vxlan_name))

fvs = {'source_vtep': vxlan_name}
db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs)

@vxlan_evpn_nvo.command('del')
@click.argument('nvo_name', metavar='<nvo_name>', required=True)
@clicommon.pass_db
def del_vxlan_evpn_nvo(db, nvo_name):
"""Del NVO"""
ctx = click.get_current_context()
vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL_MAP')
if not vxlan_keys:
vxlan_count = 0
else:
vxlan_count = len(vxlan_keys)

if(vxlan_count > 0):
ctx.fail("Please delete all VLAN VNI mappings.")
db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, None)

@vxlan.group('map')
def vxlan_map():
pass

@vxlan_map.command('add')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@click.argument('vlan', metavar='<vlan_id>', required=True)
@click.argument('vni', metavar='<vni>', required=True)
@clicommon.pass_db
def add_vxlan_map(db, vxlan_name, vlan, vni):
"""Add VLAN-VNI map entry"""
ctx = click.get_current_context()

if not vlan.isdigit():
ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni))
if clicommon.is_vlanid_in_range(int(vlan)) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if not vni.isdigit():
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))
if clicommon.vni_id_is_valid(int(vni)) is False:
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))

vlan_name = "Vlan" + vlan

if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
ctx.fail("VTEP {} not configured".format(vxlan_name))

if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0:
ctx.fail("{} not configured".format(vlan_name))

vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP')
vxlan_keys = vxlan_table.keys()
if vxlan_keys is not None:
for key in vxlan_keys:
if (vxlan_table[key]['vlan'] == vlan_name):
ctx.fail(" Vlan Id already mapped ")
if (vxlan_table[key]['vni'] == vni):
ctx.fail(" VNI Id already mapped ")

fvs = {'vni': vni,
'vlan' : vlan_name}
mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan_name
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs)

@vxlan_map.command('del')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@click.argument('vlan', metavar='<vlan_id>', required=True)
@click.argument('vni', metavar='<vni>', required=True)
@clicommon.pass_db
def del_vxlan_map(db, vxlan_name, vlan, vni):
"""Del VLAN-VNI map entry"""
ctx = click.get_current_context()

if not vlan.isdigit():
ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni))
if clicommon.is_vlanid_in_range(int(vlan)) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if not vni.isdigit():
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))
if clicommon.vni_id_is_valid(int(vni)) is False:
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))

if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
ctx.fail("VTEP {} not configured".format(vxlan_name))
found = 0
vrf_table = db.cfgdb.get_table('VRF')
vrf_keys = vrf_table.keys()
if vrf_keys is not None:
for vrf_key in vrf_keys:
if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni):
found = 1
break

if (found == 1):
ctx.fail("VNI mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key))

mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)
mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)

@vxlan.group('map_range')
def vxlan_map_range():
pass

@vxlan_map_range.command('add')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@click.argument('vlan_start', metavar='<vlan_start>', required=True, type=int)
@click.argument('vlan_end', metavar='<vlan_end>', required=True, type=int)
@click.argument('vni_start', metavar='<vni_start>', required=True, type=int)
@clicommon.pass_db
def add_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start):
"""Add Range of vlan-vni mappings"""
ctx = click.get_current_context()
if clicommon.is_vlanid_in_range(vlan_start) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if clicommon.is_vlanid_in_range(vlan_end) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if (vlan_start > vlan_end):
ctx.fail("vlan_end should be greater or equal to vlan_start")
if clicommon.vni_id_is_valid(vni_start) is False:
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start))
if clicommon.vni_id_is_valid(vni_start+vlan_end-vlan_start) is False:
ctx.fail("Invalid VNI End {}. Valid range [1 to 16777215].".format(vni_start))

if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
ctx.fail("VTEP {} not configured".format(vxlan_name))
vlan_end = vlan_end + 1
vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP')
vxlan_keys = vxlan_table.keys()

for vid in range (vlan_start, vlan_end):
vlan_name = 'Vlan{}'.format(vid)
vnid = vni_start+vid-vlan_start
vni_name = '{}'.format(vnid)
match_found = 'no'
if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0:
click.echo("{} not configured".format(vlan_name))
continue
if vxlan_keys is not None:
for key in vxlan_keys:
if (vxlan_table[key]['vlan'] == vlan_name):
print(vlan_name + " already mapped")
match_found = 'yes'
break
if (vxlan_table[key]['vni'] == vni_name):
print("VNI:" + vni_name + " already mapped ")
match_found = 'yes'
break
if (match_found == 'yes'):
continue
fvs = {'vni': vni_name,
'vlan' : vlan_name}
mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs)

@vxlan_map_range.command('del')
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
@click.argument('vlan_start', metavar='<vlan_start>', required=True, type=int)
@click.argument('vlan_end', metavar='<vlan_end>', required=True, type=int)
@click.argument('vni_start', metavar='<vni_start>', required=True, type=int)
@clicommon.pass_db
def del_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start):
"""Del Range of vlan-vni mappings"""
ctx = click.get_current_context()
if clicommon.is_vlanid_in_range(vlan_start) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if clicommon.is_vlanid_in_range(vlan_end) is False:
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
if (vlan_start > vlan_end):
ctx.fail("vlan_end should be greater or equal to vlan_start")
if clicommon.vni_id_is_valid(vni_start) is False:
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start))
if clicommon.vni_id_is_valid(vni_start+vlan_end-vlan_start) is False:
ctx.fail("Invalid VNI End {}. Valid range [1 to 16777215].".format(vni_start))

if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
ctx.fail("VTEP {} not configured".format(vxlan_name))

vlan_end = vlan_end + 1
for vid in range (vlan_start, vlan_end):
vlan_name = 'Vlan{}'.format(vid)
vnid = vni_start+vid-vlan_start
vni_name = '{}'.format(vnid)
if clicommon.is_vni_vrf_mapped(db, vni_name) is False:
print("Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name))
continue

mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)

1 change: 1 addition & 0 deletions scripts/fast-reboot
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ function backup_database()
if not string.match(k, 'FDB_TABLE|') and not string.match(k, 'WARM_RESTART_TABLE|') \
and not string.match(k, 'MIRROR_SESSION_TABLE|') \
and not string.match(k, 'WARM_RESTART_ENABLE_TABLE|') \
and not string.match(k, 'VXLAN_TUNNEL_TABLE|') \
and not string.match(k, 'BUFFER_MAX_PARAM_TABLE|') then
redis.call('del', k)
end
Expand Down
1 change: 0 additions & 1 deletion show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,5 @@ def ztp(status, verbose):
cmd = cmd + " --verbose"
run_command(cmd, display_cmd=verbose)


if __name__ == '__main__':
cli()
Loading

0 comments on commit aad2c38

Please sign in to comment.