Skip to content

Commit aad2c38

Browse files
srj102tapashdaskananthak
authored
VXLAN config and show utilities (sonic-net#870)
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]>
1 parent 9419627 commit aad2c38

File tree

10 files changed

+896
-5
lines changed

10 files changed

+896
-5
lines changed

config/main.py

+52-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import utilities_common.cli as clicommon
2525
from .utils import log
2626

27-
2827
from . import aaa
2928
from . import chassis_modules
3029
from . import console
@@ -35,6 +34,7 @@
3534
from . import muxcable
3635
from . import nat
3736
from . import vlan
37+
from . import vxlan
3838
from .config_mgmt import ConfigMgmtDPB
3939

4040
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?'])
@@ -885,6 +885,7 @@ def config(ctx):
885885
config.add_command(muxcable.muxcable)
886886
config.add_command(nat.nat)
887887
config.add_command(vlan.vlan)
888+
config.add_command(vxlan.vxlan)
888889

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

29802981

2982+
@vrf.command('add_vrf_vni_map')
2983+
@click.argument('vrfname', metavar='<vrf-name>', required=True, type=str)
2984+
@click.argument('vni', metavar='<vni>', required=True)
2985+
@click.pass_context
2986+
def add_vrf_vni_map(ctx, vrfname, vni):
2987+
config_db = ctx.obj['config_db']
2988+
found = 0
2989+
if vrfname not in config_db.get_table('VRF').keys():
2990+
ctx.fail("vrf {} doesnt exists".format(vrfname))
2991+
if not vni.isdigit():
2992+
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))
2993+
2994+
if clicommon.vni_id_is_valid(int(vni)) is False:
2995+
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))
2996+
2997+
vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP')
2998+
vxlan_keys = vxlan_table.keys()
2999+
if vxlan_keys is not None:
3000+
for key in vxlan_keys:
3001+
if (vxlan_table[key]['vni'] == vni):
3002+
found = 1
3003+
break
3004+
3005+
if (found == 0):
3006+
ctx.fail("VLAN VNI not mapped. Please create VLAN VNI map entry first")
3007+
3008+
found = 0
3009+
vrf_table = config_db.get_table('VRF')
3010+
vrf_keys = vrf_table.keys()
3011+
if vrf_keys is not None:
3012+
for vrf_key in vrf_keys:
3013+
if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni):
3014+
found = 1
3015+
break
3016+
3017+
if (found == 1):
3018+
ctx.fail("VNI already mapped to vrf {}".format(vrf_key))
3019+
3020+
config_db.mod_entry('VRF', vrfname, {"vni": vni})
3021+
3022+
@vrf.command('del_vrf_vni_map')
3023+
@click.argument('vrfname', metavar='<vrf-name>', required=True, type=str)
3024+
@click.pass_context
3025+
def del_vrf_vni_map(ctx, vrfname):
3026+
config_db = ctx.obj['config_db']
3027+
if vrfname not in config_db.get_table('VRF').keys():
3028+
ctx.fail("vrf {} doesnt exists".format(vrfname))
3029+
3030+
config_db.mod_entry('VRF', vrfname, {"vni": 0})
3031+
29813032
#
29823033
# 'route' group ('config route ...')
29833034
#

config/vxlan.py

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
import click
2+
import utilities_common.cli as clicommon
3+
4+
#
5+
# 'vxlan' group ('config vxlan ...')
6+
#
7+
@click.group()
8+
def vxlan():
9+
pass
10+
11+
@vxlan.command('add')
12+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
13+
@click.argument('src_ip', metavar='<src_ip>', required=True)
14+
@clicommon.pass_db
15+
def add_vxlan(db, vxlan_name, src_ip):
16+
"""Add VXLAN"""
17+
ctx = click.get_current_context()
18+
19+
if not clicommon.is_ipaddress(src_ip):
20+
ctx.fail("{} invalid src ip address".format(src_ip))
21+
22+
vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL')
23+
if not vxlan_keys:
24+
vxlan_count = 0
25+
else:
26+
vxlan_count = len(vxlan_keys)
27+
28+
if(vxlan_count > 0):
29+
ctx.fail("VTEP already configured.")
30+
31+
fvs = {'src_ip': src_ip}
32+
db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, fvs)
33+
34+
@vxlan.command('del')
35+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
36+
@clicommon.pass_db
37+
def del_vxlan(db, vxlan_name):
38+
"""Del VXLAN"""
39+
ctx = click.get_current_context()
40+
41+
vxlan_keys = db.cfgdb.get_keys('VXLAN_EVPN_NVO')
42+
if not vxlan_keys:
43+
vxlan_count = 0
44+
else:
45+
vxlan_count = len(vxlan_keys)
46+
47+
if(vxlan_count > 0):
48+
ctx.fail("Please delete the EVPN NVO configuration.")
49+
50+
vxlan_keys = db.cfgdb.get_keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*")
51+
if not vxlan_keys:
52+
vxlan_count = 0
53+
else:
54+
vxlan_count = len(vxlan_keys)
55+
56+
if(vxlan_count > 0):
57+
ctx.fail("Please delete all VLAN VNI mappings.")
58+
59+
db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, None)
60+
61+
@vxlan.group('evpn_nvo')
62+
def vxlan_evpn_nvo():
63+
pass
64+
65+
@vxlan_evpn_nvo.command('add')
66+
@click.argument('nvo_name', metavar='<nvo_name>', required=True)
67+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
68+
@clicommon.pass_db
69+
def add_vxlan_evpn_nvo(db, nvo_name, vxlan_name):
70+
"""Add NVO"""
71+
ctx = click.get_current_context()
72+
vxlan_keys = db.cfgdb.get_keys('CONFIG_DB', "VXLAN_EVPN_NVO|*")
73+
if not vxlan_keys:
74+
vxlan_count = 0
75+
else:
76+
vxlan_count = len(vxlan_keys)
77+
78+
if(vxlan_count > 0):
79+
ctx.fail("EVPN NVO already configured")
80+
81+
if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
82+
ctx.fail("VTEP {} not configured".format(vxlan_name))
83+
84+
fvs = {'source_vtep': vxlan_name}
85+
db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs)
86+
87+
@vxlan_evpn_nvo.command('del')
88+
@click.argument('nvo_name', metavar='<nvo_name>', required=True)
89+
@clicommon.pass_db
90+
def del_vxlan_evpn_nvo(db, nvo_name):
91+
"""Del NVO"""
92+
ctx = click.get_current_context()
93+
vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL_MAP')
94+
if not vxlan_keys:
95+
vxlan_count = 0
96+
else:
97+
vxlan_count = len(vxlan_keys)
98+
99+
if(vxlan_count > 0):
100+
ctx.fail("Please delete all VLAN VNI mappings.")
101+
db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, None)
102+
103+
@vxlan.group('map')
104+
def vxlan_map():
105+
pass
106+
107+
@vxlan_map.command('add')
108+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
109+
@click.argument('vlan', metavar='<vlan_id>', required=True)
110+
@click.argument('vni', metavar='<vni>', required=True)
111+
@clicommon.pass_db
112+
def add_vxlan_map(db, vxlan_name, vlan, vni):
113+
"""Add VLAN-VNI map entry"""
114+
ctx = click.get_current_context()
115+
116+
if not vlan.isdigit():
117+
ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni))
118+
if clicommon.is_vlanid_in_range(int(vlan)) is False:
119+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
120+
if not vni.isdigit():
121+
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))
122+
if clicommon.vni_id_is_valid(int(vni)) is False:
123+
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))
124+
125+
vlan_name = "Vlan" + vlan
126+
127+
if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
128+
ctx.fail("VTEP {} not configured".format(vxlan_name))
129+
130+
if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0:
131+
ctx.fail("{} not configured".format(vlan_name))
132+
133+
vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP')
134+
vxlan_keys = vxlan_table.keys()
135+
if vxlan_keys is not None:
136+
for key in vxlan_keys:
137+
if (vxlan_table[key]['vlan'] == vlan_name):
138+
ctx.fail(" Vlan Id already mapped ")
139+
if (vxlan_table[key]['vni'] == vni):
140+
ctx.fail(" VNI Id already mapped ")
141+
142+
fvs = {'vni': vni,
143+
'vlan' : vlan_name}
144+
mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan_name
145+
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs)
146+
147+
@vxlan_map.command('del')
148+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
149+
@click.argument('vlan', metavar='<vlan_id>', required=True)
150+
@click.argument('vni', metavar='<vni>', required=True)
151+
@clicommon.pass_db
152+
def del_vxlan_map(db, vxlan_name, vlan, vni):
153+
"""Del VLAN-VNI map entry"""
154+
ctx = click.get_current_context()
155+
156+
if not vlan.isdigit():
157+
ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni))
158+
if clicommon.is_vlanid_in_range(int(vlan)) is False:
159+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
160+
if not vni.isdigit():
161+
ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni))
162+
if clicommon.vni_id_is_valid(int(vni)) is False:
163+
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni))
164+
165+
if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
166+
ctx.fail("VTEP {} not configured".format(vxlan_name))
167+
found = 0
168+
vrf_table = db.cfgdb.get_table('VRF')
169+
vrf_keys = vrf_table.keys()
170+
if vrf_keys is not None:
171+
for vrf_key in vrf_keys:
172+
if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni):
173+
found = 1
174+
break
175+
176+
if (found == 1):
177+
ctx.fail("VNI mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key))
178+
179+
mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan
180+
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)
181+
mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan
182+
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)
183+
184+
@vxlan.group('map_range')
185+
def vxlan_map_range():
186+
pass
187+
188+
@vxlan_map_range.command('add')
189+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
190+
@click.argument('vlan_start', metavar='<vlan_start>', required=True, type=int)
191+
@click.argument('vlan_end', metavar='<vlan_end>', required=True, type=int)
192+
@click.argument('vni_start', metavar='<vni_start>', required=True, type=int)
193+
@clicommon.pass_db
194+
def add_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start):
195+
"""Add Range of vlan-vni mappings"""
196+
ctx = click.get_current_context()
197+
if clicommon.is_vlanid_in_range(vlan_start) is False:
198+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
199+
if clicommon.is_vlanid_in_range(vlan_end) is False:
200+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
201+
if (vlan_start > vlan_end):
202+
ctx.fail("vlan_end should be greater or equal to vlan_start")
203+
if clicommon.vni_id_is_valid(vni_start) is False:
204+
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start))
205+
if clicommon.vni_id_is_valid(vni_start+vlan_end-vlan_start) is False:
206+
ctx.fail("Invalid VNI End {}. Valid range [1 to 16777215].".format(vni_start))
207+
208+
if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
209+
ctx.fail("VTEP {} not configured".format(vxlan_name))
210+
vlan_end = vlan_end + 1
211+
vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP')
212+
vxlan_keys = vxlan_table.keys()
213+
214+
for vid in range (vlan_start, vlan_end):
215+
vlan_name = 'Vlan{}'.format(vid)
216+
vnid = vni_start+vid-vlan_start
217+
vni_name = '{}'.format(vnid)
218+
match_found = 'no'
219+
if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0:
220+
click.echo("{} not configured".format(vlan_name))
221+
continue
222+
if vxlan_keys is not None:
223+
for key in vxlan_keys:
224+
if (vxlan_table[key]['vlan'] == vlan_name):
225+
print(vlan_name + " already mapped")
226+
match_found = 'yes'
227+
break
228+
if (vxlan_table[key]['vni'] == vni_name):
229+
print("VNI:" + vni_name + " already mapped ")
230+
match_found = 'yes'
231+
break
232+
if (match_found == 'yes'):
233+
continue
234+
fvs = {'vni': vni_name,
235+
'vlan' : vlan_name}
236+
mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name
237+
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs)
238+
239+
@vxlan_map_range.command('del')
240+
@click.argument('vxlan_name', metavar='<vxlan_name>', required=True)
241+
@click.argument('vlan_start', metavar='<vlan_start>', required=True, type=int)
242+
@click.argument('vlan_end', metavar='<vlan_end>', required=True, type=int)
243+
@click.argument('vni_start', metavar='<vni_start>', required=True, type=int)
244+
@clicommon.pass_db
245+
def del_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start):
246+
"""Del Range of vlan-vni mappings"""
247+
ctx = click.get_current_context()
248+
if clicommon.is_vlanid_in_range(vlan_start) is False:
249+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
250+
if clicommon.is_vlanid_in_range(vlan_end) is False:
251+
ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ")
252+
if (vlan_start > vlan_end):
253+
ctx.fail("vlan_end should be greater or equal to vlan_start")
254+
if clicommon.vni_id_is_valid(vni_start) is False:
255+
ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start))
256+
if clicommon.vni_id_is_valid(vni_start+vlan_end-vlan_start) is False:
257+
ctx.fail("Invalid VNI End {}. Valid range [1 to 16777215].".format(vni_start))
258+
259+
if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0:
260+
ctx.fail("VTEP {} not configured".format(vxlan_name))
261+
262+
vlan_end = vlan_end + 1
263+
for vid in range (vlan_start, vlan_end):
264+
vlan_name = 'Vlan{}'.format(vid)
265+
vnid = vni_start+vid-vlan_start
266+
vni_name = '{}'.format(vnid)
267+
if clicommon.is_vni_vrf_mapped(db, vni_name) is False:
268+
print("Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name))
269+
continue
270+
271+
mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name
272+
db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None)
273+

scripts/fast-reboot

+1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ function backup_database()
248248
if not string.match(k, 'FDB_TABLE|') and not string.match(k, 'WARM_RESTART_TABLE|') \
249249
and not string.match(k, 'MIRROR_SESSION_TABLE|') \
250250
and not string.match(k, 'WARM_RESTART_ENABLE_TABLE|') \
251+
and not string.match(k, 'VXLAN_TUNNEL_TABLE|') \
251252
and not string.match(k, 'BUFFER_MAX_PARAM_TABLE|') then
252253
redis.call('del', k)
253254
end

show/main.py

-1
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,5 @@ def ztp(status, verbose):
15221522
cmd = cmd + " --verbose"
15231523
run_command(cmd, display_cmd=verbose)
15241524

1525-
15261525
if __name__ == '__main__':
15271526
cli()

0 commit comments

Comments
 (0)