From 42b055d6549bbf867169b1b689acf1da001bec93 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Tue, 7 Apr 2020 23:20:41 -0700 Subject: [PATCH 01/19] Changes to support EVPN VXLAN. Config and show commands added to click infra for VXLAN objects. Please refer to https://github.com/Azure/SONiC/pull/437 for the commands. The fast-reboot script is changed to not clear VXLAN tunnel state table during a warm reboot. --- config/main.py | 333 ++++++++++++++++++++++++++++++++++++++++++++ scripts/fast-reboot | 1 + show/main.py | 248 +++++++++++++++++++++++++++++++++ 3 files changed, 582 insertions(+) diff --git a/config/main.py b/config/main.py index 2316d7e38c..1b4c756cf6 100755 --- a/config/main.py +++ b/config/main.py @@ -3633,5 +3633,338 @@ def delete(ctx): sflow_tbl['global'].pop('agent_id') config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) +# +# 'feature' command ('config feature name state') +# +@config.command('feature') +@click.argument('name', metavar='', required=True) +@click.argument('state', metavar='', required=True, type=click.Choice(["enabled", "disabled"])) +def feature_status(name, state): + """ Configure status of feature""" + config_db = ConfigDBConnector() + config_db.connect() + status_data = config_db.get_entry('FEATURE', name) + + if not status_data: + click.echo(" Feature '{}' doesn't exist".format(name)) + return + + config_db.mod_entry('FEATURE', name, {'status': state}) + +# +# 'container' group ('config container ...') +# +@config.group(cls=AbbreviationGroup, name='container', invoke_without_command=False) +def container(): + """Modify configuration of containers""" + pass + +# +# 'feature' group ('config container feature ...') +# +@container.group(cls=AbbreviationGroup, name='feature', invoke_without_command=False) +def feature(): + """Modify configuration of container features""" + pass + +# +# 'autorestart' subcommand ('config container feature autorestart ...') +# +@feature.command(name='autorestart', short_help="Configure the status of autorestart feature for specific container") +@click.argument('container_name', metavar='', required=True) +@click.argument('autorestart_status', metavar='', required=True, type=click.Choice(["enabled", "disabled"])) +def autorestart(container_name, autorestart_status): + config_db = ConfigDBConnector() + config_db.connect() + container_feature_table = config_db.get_table('CONTAINER_FEATURE') + if not container_feature_table: + click.echo("Unable to retrieve container feature table from Config DB.") + return + + if not container_feature_table.has_key(container_name): + click.echo("Unable to retrieve features for container '{}'".format(container_name)) + return + + config_db.mod_entry('CONTAINER_FEATURE', container_name, {'auto_restart': autorestart_status}) + +# +# 'vxlan' group ('config vxlan ...') +# +@config.group() +@click.pass_context +def vxlan(ctx): + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + pass + +@vxlan.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('src_ip', metavar='', required=True) +@click.pass_context +def add_vxlan(ctx, vxlan_name, src_ip): + """Add VXLAN""" + if not is_ip_addr_valid(src_ip, True): + ctx.fail("{} invalid src ip address".format(src_ip)) + db = ctx.obj['db'] + + vxlan_keys = db.keys('CONFIG_DB', "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.set_entry('VXLAN_TUNNEL', vxlan_name, fvs) + +@vxlan.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.pass_context +def del_vxlan(ctx, vxlan_name): + """Del VXLAN""" + db = ctx.obj['db'] + + vxlan_keys = db.keys('CONFIG_DB', "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.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.set_entry('VXLAN_TUNNEL', vxlan_name, None) + +@vxlan.group('evpn_nvo') +@click.pass_context +def vxlan_evpn_nvo(ctx): + pass + +@vxlan_evpn_nvo.command('add') +@click.argument('nvo_name', metavar='', required=True) +@click.argument('vxlan_name', metavar='', required=True) +@click.pass_context +def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): + """Add NVO""" + db = ctx.obj['db'] + vxlan_keys = db.keys('CONFIG_DB', "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.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + + fvs = {'source_vtep': vxlan_name} + db.set_entry('EVPN_NVO', nvo_name, fvs) + +@vxlan_evpn_nvo.command('del') +@click.argument('nvo_name', metavar='', required=True) +@click.pass_context +def del_vxlan_evpn_nvo(ctx, nvo_name): + """Del NVO""" + db = ctx.obj['db'] + vxlan_keys = db.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.set_entry('EVPN_NVO', nvo_name, None) + +@vxlan.group('map') +@click.pass_context +def vxlan_map(ctx): + pass + +@vxlan_map.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan', metavar='', required=True) +@click.argument('vni', metavar='', required=True) +@click.pass_context +def add_vxlan_map(ctx, vxlan_name, vlan, vni): + """Add VLAN-VNI map entry""" + if not vlan.isdigit(): + ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni)) + if vlan_id_is_valid(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 (int(vni) < 1) or (int(vni) > 16777215): + if vni_id_is_valid(int(vni)) is False: + ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) + + db = ctx.obj['db'] + vlan_name = "Vlan" + vlan + + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + + if len(db.get_entry('VLAN', vlan_name)) == 0: + ctx.fail("{} not configured".format(vlan_name)) + + vxlan_table = 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]['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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + +@vxlan_map.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan', metavar='', required=True) +@click.argument('vni', metavar='', required=True) +@click.pass_context +def del_vxlan_map(ctx, vxlan_name, vlan, vni): + """Del VLAN-VNI map entry""" + if not vlan.isdigit(): + ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni)) + if vlan_id_is_valid(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 (int(vni) < 1) or (int(vni) > 16777215): + if vni_id_is_valid(int(vni)) is False: + ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) + + db = ctx.obj['db'] + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + found = 0 + vrf_table = 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 mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key)) + + mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan + db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan + db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + +@vxlan.group('map_range') +@click.pass_context +def vxlan_map_range(ctx): + pass + +@vxlan_map_range.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan_start', metavar='', required=True, type=int) +@click.argument('vlan_end', metavar='', required=True, type=int) +@click.argument('vni_start', metavar='', required=True, type=int) +@click.pass_context +def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): + """Add Range of vlan-vni mappings""" + if vlan_id_is_valid(vlan_start) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + if vlan_id_is_valid(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 vni_id_is_valid(vni_start) is False: + ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start)) + if 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)) + + db = ctx.obj['db'] + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + vlan_end = vlan_end + 1 + vxlan_table = db.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.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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + +@vxlan_map_range.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan_start', metavar='', required=True, type=int) +@click.argument('vlan_end', metavar='', required=True, type=int) +@click.argument('vni_start', metavar='', required=True, type=int) +@click.pass_context +def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): + """Del Range of vlan-vni mappings""" + if vlan_id_is_valid(vlan_start) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + if vlan_id_is_valid(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 vni_id_is_valid(vni_start) is False: + ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start)) + if 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)) + + db = ctx.obj['db'] + if len(db.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 is_vni_vrf_mapped(ctx, vni_name) is False: + print "Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name) + continue + + fvs = {'vni': vni_name, + 'vlan' : vlan_name} + mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name + db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + if __name__ == '__main__': config() + diff --git a/scripts/fast-reboot b/scripts/fast-reboot index 58ad650ffb..65a680c8c6 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -247,6 +247,7 @@ function backup_database() for _, k in ipairs(redis.call('keys', '*')) do 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, 'VXLAN_TUNNEL_TABLE|') \ and not string.match(k, 'WARM_RESTART_ENABLE_TABLE|') then redis.call('del', k) end diff --git a/show/main.py b/show/main.py index 087a67958b..37f846c88f 100755 --- a/show/main.py +++ b/show/main.py @@ -1491,5 +1491,253 @@ def ztp(status, verbose): run_command(cmd, display_cmd=verbose) +# +# 'vxlan' group ("show vxlan ...") +# + +@cli.group(cls=AliasedGroup, default_if_no_args=False) +def vxlan(): + """Show VXLAN information""" + pass + +@vxlan.command() +def interface(): + """Show VXLAN VTEP Information""" + + config_db = ConfigDBConnector() + config_db.connect() + + # Fetching VTEP keys from config DB + click.secho('VTEP Information:\n', bold=True, underline=True) + vxlan_table = config_db.get_table('VXLAN_TUNNEL') + vxlan_keys = vxlan_table.keys() + vtep_sip = '0.0.0.0' + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',1) + vtepname = key1.pop(); + if 'src_ip' in vxlan_table[key]: + vtep_sip = vxlan_table[key]['src_ip'] + if vtep_sip is not '0.0.0.0': + output = '\tVTEP Name : ' + vtepname + ', SIP : ' + vxlan_table[key]['src_ip'] + else: + output = '\tVTEP Name : ' + vtepname + + click.echo(output) + + if vtep_sip is not '0.0.0.0': + vxlan_table = config_db.get_table('EVPN_NVO') + vxlan_keys = vxlan_table.keys() + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',1) + vtepname = key1.pop(); + output = '\tNVO Name : ' + vtepname + ', VTEP : ' + vxlan_table[key]['source_vtep'] + click.echo(output) + + vxlan_keys = config_db.keys('CONFIG_DB', "LOOPBACK_INTERFACE|*") + loopback = 'Not Configured' + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',2) + if len(key1) == 3 and key1[2] == vtep_sip+'/32': + loopback = key1[1] + break + output = '\tSource interface : ' + loopback + if vtep_sip != '0.0.0.0': + click.echo(output) + +@vxlan.command() +@click.argument('count', required=False) +def vlanvnimap(count): + """Show VLAN VNI Mapping Information""" + + header = ['VLAN', 'VNI'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + if count is not None: + vxlan_keys = config_db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") + + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total mapping count:' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + vxlan_keys = vxlan_table.keys() + num=0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + body.append([vxlan_table[key]['vlan'], vxlan_table[key]['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +def vrfvnimap(): + """Show VRF VNI Mapping Information""" + + header = ['VRF', 'VNI'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vrf_table = config_db.get_table('VRF') + vrf_keys = vrf_table.keys() + num=0 + if vrf_keys is not None: + for key in natsorted(vrf_keys): + if ('vni' in vrf_table[key]): + body.append([key, vrf_table[key]['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('count', required=False) +def tunnel(count): + """Show All VXLAN Tunnels Information""" + + if (count is not None) and (count != 'count'): + click.echo("Unacceptable argument {}".format(count)) + return + + header = ['SIP', 'DIP', 'Creation Source', 'OperStatus'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB) + + vxlan_keys = db.keys(db.STATE_DB, 'VXLAN_TUNNEL_TABLE|*') + + if vxlan_keys is not None: + vxlan_count = len(vxlan_keys) + else: + vxlan_count = 0 + + if (count is not None): + output = 'Total mapping count:' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + vxlan_table = db.get_all(db.STATE_DB, key); + if vxlan_table is None: + continue + body.append([vxlan_table['src_ip'], vxlan_table['dst_ip'], vxlan_table['tnl_src'], 'oper_' + vxlan_table['operstatus']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('remote_vtep_ip', required=True) +@click.argument('count', required=False) +def remote_vni(remote_vtep_ip, count): + """Show Vlans extended to the remote VTEP""" + + if (remote_vtep_ip != 'all') and (is_ip4_addr_valid(remote_vtep_ip, True) is False): + click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) + return + + header = ['VLAN', 'RemoteVTEP', 'VNI'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.APPL_DB) + + if(remote_vtep_ip == 'all'): + vxlan_keys = db.keys(db.APPL_DB, 'EVPN_REMOTE_VNI_TABLE:*') + else: + vxlan_keys = db.keys(db.APPL_DB, 'EVPN_REMOTE_VNI_TABLE:*' + remote_vtep_ip + '*') + + if count is not None: + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total mapping count:' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split(':') + rmtip = key1.pop(); + #if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: + # continue + vxlan_table = db.get_all(db.APPL_DB, key); + if vxlan_table is None: + continue + body.append([key1.pop(), rmtip, vxlan_table['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('remote_vtep_ip', required=True) +@click.argument('count', required=False) +def remote_mac(remote_vtep_ip, count): + """Show MACs pointing to the remote VTEP""" + + if (remote_vtep_ip != 'all') and (is_ip4_addr_valid(remote_vtep_ip, True) is False): + click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) + return + + header = ['VLAN', 'MAC', 'RemoteVTEP', 'VNI', 'Type'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.APPL_DB) + + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_FDB_TABLE:*') + + if ((count is not None) and (remote_vtep_ip == 'all')): + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total count:' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split(':',2) + mac = key1.pop(); + vlan = key1.pop(); + vxlan_table = db.get_all(db.APPL_DB, key); + if vxlan_table is None: + continue + rmtip = vxlan_table['remote_vtep'] + if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: + continue + if count is None: + body.append([vlan, mac, rmtip, vxlan_table['vni'], vxlan_table['type']]) + num += 1 + if count is None: + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + if __name__ == '__main__': cli() From 2c609e21882521ce8a095b882b26234a7af46024 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Wed, 8 Apr 2020 02:46:22 -0700 Subject: [PATCH 02/19] Changed the same for REMOTE_VNI Table. --- show/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/show/main.py b/show/main.py index 37f846c88f..7744fb7fce 100755 --- a/show/main.py +++ b/show/main.py @@ -1660,9 +1660,9 @@ def remote_vni(remote_vtep_ip, count): db.connect(db.APPL_DB) if(remote_vtep_ip == 'all'): - vxlan_keys = db.keys(db.APPL_DB, 'EVPN_REMOTE_VNI_TABLE:*') + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*') else: - vxlan_keys = db.keys(db.APPL_DB, 'EVPN_REMOTE_VNI_TABLE:*' + remote_vtep_ip + '*') + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*' + remote_vtep_ip + '*') if count is not None: if not vxlan_keys: From 7e036dfa51197b6f6a73277f312e9ce222ed241f Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Wed, 8 Apr 2020 04:21:34 -0700 Subject: [PATCH 03/19] Fixed LGTM Warnings --- config/main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/main.py b/config/main.py index 1b4c756cf6..d001315bbd 100755 --- a/config/main.py +++ b/config/main.py @@ -3696,7 +3696,6 @@ def vxlan(ctx): config_db = ConfigDBConnector() config_db.connect() ctx.obj = {'db': config_db} - pass @vxlan.command('add') @click.argument('vxlan_name', metavar='', required=True) @@ -3960,8 +3959,6 @@ def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): print "Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name) continue - fvs = {'vni': vni_name, - 'vlan' : vlan_name} mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) From be0278f0f66c8294561a0211f0c195718c904bde Mon Sep 17 00:00:00 2001 From: Tapash Das Date: Fri, 17 Apr 2020 07:55:43 -0700 Subject: [PATCH 04/19] Added VRF VNI Map CLI. Added Neighbor Suppression CLI. --- config/main.py | 127 ++++++++++++++++++++++++++++++++++++++++++++++++- show/main.py | 63 ++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index d001315bbd..74b7791c06 100755 --- a/config/main.py +++ b/config/main.py @@ -341,7 +341,45 @@ def interface_name_is_valid(config_db, interface_name): return True return False -def interface_name_to_alias(config_db, interface_name): +def vlan_id_is_valid(vid): + """Check if the vlan id is in acceptable range (between 1 and 4094) + """ + + if vid<1 or vid>4094: + return False + + return True + +def vni_id_is_valid(vni): + """Check if the vni id is in acceptable range (between 1 and 2^24) + """ + + if (vni < 1) or (vni > 16777215): + return False + + return True + +def is_vni_vrf_mapped(ctx, vni): + """Check if the vni is mapped to vrf + """ + + found = 0 + db = ctx.obj['db'] + vrf_table = 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): + print "VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key) + return False + + return True + +def interface_name_to_alias(interface_name): """Return alias interface name if default name is given as argument """ # If the input parameter config_db is None, derive it from interface. @@ -2656,6 +2694,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='', required=True, type=str) +@click.argument('vni', metavar='', required=True) +@click.pass_context +def add_vrf_vni_map(ctx, vrfname, vni): + db = ctx.obj['db'] + found = 0 + if vrfname not in 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 (int(vni) < 1) or (int(vni) > 16777215): + ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) + + vxlan_table = 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 = 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)) + + db.mod_entry('VRF', vrfname, {"vni": vni}) + +@vrf.command('del_vrf_vni_map') +@click.argument('vrfname', metavar='', required=True, type=str) +@click.pass_context +def del_vrf_vni_map(ctx, vrfname): + db = ctx.obj['db'] + if vrfname not in db.get_table('VRF').keys(): + ctx.fail("vrf {} doesnt exists".format(vrfname)) + + db.mod_entry('VRF', vrfname, {"vni": 0}) + # # 'route' group ('config route ...') # @@ -3962,6 +4050,43 @@ def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) +####### +# +# 'neigh_suppress' group ('config neigh_suppress...') +# +@config.group() +@click.pass_context +def neigh_suppress(ctx): + """ Neighbour Suppress VLAN-related configuration """ + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + pass + +@neigh_suppress.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def enable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if vlan_id_is_valid(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + if len(db.get_entry('VLAN', vlan)) == 0: + click.echo("{} doesn't exist".format(vlan)) + return + fvs = {'suppress': "on"} + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + +@neigh_suppress.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def disable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if vlan_id_is_valid(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) + if __name__ == '__main__': config() diff --git a/show/main.py b/show/main.py index 7744fb7fce..c0569c9169 100755 --- a/show/main.py +++ b/show/main.py @@ -1739,5 +1739,68 @@ def remote_mac(remote_vtep_ip, count): output += ('%s \n' % (str(num))) click.echo(output) +#Neigh Suppress +@cli.group('neigh-suppress') +def neigh_suppress(): + """ show neigh_suppress """ + pass +@neigh_suppress.command('all') +def neigh_suppress_all(): + """ Show neigh_suppress all """ + + header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') + vxlan_keys = vxlan_table.keys() + num=0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = vxlan_table[key]['vlan'] + netdev = vxlan_keys[0][0]+"-"+key1[4:] + if key1 not in suppress_table: + supp_str = "Not Configured" + else: + supp_str = "Configured" + body.append([vxlan_table[key]['vlan'], supp_str, netdev]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@neigh_suppress.command('vlan') +@click.argument('vid', metavar='', required=True, type=int) +def neigh_suppress_vlan(vid): + """ Show neigh_suppress vlan""" + header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') + vlan = 'Vlan{}'.format(vid) + vxlan_keys = vxlan_table.keys() + num=0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = vxlan_table[key]['vlan'] + if(key1 == vlan): + netdev = vxlan_keys[0][0]+"-"+key1[4:] + if key1 not in suppress_table: + supp_str = "Not Configured" + else: + supp_str = "Configured" + body.append([vxlan_table[key]['vlan'], supp_str, netdev]) + click.echo(tabulate(body, header, tablefmt="grid")) + return + print(vlan + " is not configured in vxlan tunnel map table") + if __name__ == '__main__': cli() From fa139949f6d53369596450e297d31f5ad27f454c Mon Sep 17 00:00:00 2001 From: Tapash Das Date: Fri, 17 Apr 2020 09:01:24 -0700 Subject: [PATCH 05/19] LTGM Warning Fix. --- config/main.py | 1 - show/main.py | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/config/main.py b/config/main.py index 74b7791c06..d8ffad08dc 100755 --- a/config/main.py +++ b/config/main.py @@ -4061,7 +4061,6 @@ def neigh_suppress(ctx): config_db = ConfigDBConnector() config_db.connect() ctx.obj = {'db': config_db} - pass @neigh_suppress.command('enable') @click.argument('vid', metavar='', required=True, type=int) diff --git a/show/main.py b/show/main.py index c0569c9169..c2a4725298 100755 --- a/show/main.py +++ b/show/main.py @@ -1787,15 +1787,13 @@ def neigh_suppress_vlan(vid): suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') vlan = 'Vlan{}'.format(vid) vxlan_keys = vxlan_table.keys() - num=0 + if vxlan_keys is not None: for key in natsorted(vxlan_keys): key1 = vxlan_table[key]['vlan'] if(key1 == vlan): netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 not in suppress_table: - supp_str = "Not Configured" - else: + if key1 in suppress_table: supp_str = "Configured" body.append([vxlan_table[key]['vlan'], supp_str, netdev]) click.echo(tabulate(body, header, tablefmt="grid")) From 68521b6fc8cbc6ef1e9034fef19102439ac748c6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Ananthakrishnan Date: Fri, 29 May 2020 02:43:21 -0700 Subject: [PATCH 06/19] NeighSuppression Changes --- config/main.py | 38 ++++++++++++++++++++++++++++++++++++++ show/main.py | 20 ++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/config/main.py b/config/main.py index d8ffad08dc..b13a625daa 100755 --- a/config/main.py +++ b/config/main.py @@ -4085,6 +4085,44 @@ def disable_neigh_suppress(ctx, vid): ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") vlan = 'Vlan{}'.format(vid) db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) +####### +# +# 'neigh_suppress' group ('config neigh_suppress...') +# +@config.group() +@click.pass_context +def neigh_suppress(ctx): + """ Neighbour Suppress VLAN-related configuration """ + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + pass + +@neigh_suppress.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def enable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if vlan_id_is_valid(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + if len(db.get_entry('VLAN', vlan)) == 0: + click.echo("{} doesn't exist".format(vlan)) + return + fvs = {'suppress': "on"} + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + +@neigh_suppress.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def disable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if vlan_id_is_valid(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) + + if __name__ == '__main__': config() diff --git a/show/main.py b/show/main.py index c2a4725298..2ba2d55ad6 100755 --- a/show/main.py +++ b/show/main.py @@ -1560,6 +1560,26 @@ def vlanvnimap(count): if count is not None: vxlan_keys = config_db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') + vlan = 'Vlan{}'.format(vid) + vxlan_keys = vxlan_table.keys() + + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = vxlan_table[key]['vlan'] + if(key1 == vlan): + netdev = vxlan_keys[0][0]+"-"+key1[4:] + if key1 not in suppress_table: + supp_str = "Not Configured" + else: + supp_str = "Configured" + + body.append([vxlan_table[key]['vlan'], supp_str, netdev]) + click.echo(tabulate(body, header, tablefmt="grid")) + return + print(vlan + " is not configured in vxlan tunnel map table") + if not vxlan_keys: vxlan_count = 0 From 39561f0cb5ef58dd546e332144ee3d491ee47fc7 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Thu, 18 Jun 2020 05:34:15 -0700 Subject: [PATCH 07/19] 1. Updated EVPN DB names. 2. Tested on vs except for L3VXLAN+NeighSuppress+remote mac --- config/main.py | 41 ++++++++++++++++++++++---- show/main.py | 79 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/config/main.py b/config/main.py index b13a625daa..13769811a7 100755 --- a/config/main.py +++ b/config/main.py @@ -341,6 +341,37 @@ def interface_name_is_valid(config_db, interface_name): return True return False +# +# Use this method to validate unicast IPv4 address +# +def is_ip4_addr_valid(addr, display): + v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] + try: + ip = ipaddress.ip_address(unicode(addr)) + if (ip.version == 4): + if (ip.is_reserved): + if display: + click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) + return False + elif (ip.is_multicast): + if display: + click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) + return False + elif (ip in v4_invalid_list): + if display: + click.echo ("{} Not Valid.".format(addr)) + return False + else: + return True + + else: + if display: + click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) + return False + + except ValueError: + return False + def vlan_id_is_valid(vid): """Check if the vlan id is in acceptable range (between 1 and 4094) """ @@ -3791,7 +3822,7 @@ def vxlan(ctx): @click.pass_context def add_vxlan(ctx, vxlan_name, src_ip): """Add VXLAN""" - if not is_ip_addr_valid(src_ip, True): + if not is_ip4_addr_valid(src_ip, True): ctx.fail("{} invalid src ip address".format(src_ip)) db = ctx.obj['db'] @@ -3814,7 +3845,7 @@ def del_vxlan(ctx, vxlan_name): """Del VXLAN""" db = ctx.obj['db'] - vxlan_keys = db.keys('CONFIG_DB', "EVPN_NVO|*") + vxlan_keys = db.keys('CONFIG_DB', "VXLAN_EVPN_NVO|*") if not vxlan_keys: vxlan_count = 0 else: @@ -3846,7 +3877,7 @@ def vxlan_evpn_nvo(ctx): def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): """Add NVO""" db = ctx.obj['db'] - vxlan_keys = db.keys('CONFIG_DB', "EVPN_NVO|*") + vxlan_keys = db.keys('CONFIG_DB', "VXLAN_EVPN_NVO|*") if not vxlan_keys: vxlan_count = 0 else: @@ -3859,7 +3890,7 @@ def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): ctx.fail("VTEP {} not configured".format(vxlan_name)) fvs = {'source_vtep': vxlan_name} - db.set_entry('EVPN_NVO', nvo_name, fvs) + db.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs) @vxlan_evpn_nvo.command('del') @click.argument('nvo_name', metavar='', required=True) @@ -3875,7 +3906,7 @@ def del_vxlan_evpn_nvo(ctx, nvo_name): if(vxlan_count > 0): ctx.fail("Please delete all VLAN VNI mappings.") - db.set_entry('EVPN_NVO', nvo_name, None) + db.set_entry('VXLAN_EVPN_NVO', nvo_name, None) @vxlan.group('map') @click.pass_context diff --git a/show/main.py b/show/main.py index 2ba2d55ad6..f5e0212df7 100755 --- a/show/main.py +++ b/show/main.py @@ -106,6 +106,53 @@ def run_command(command, display_cmd=False, return_cmd=False): if rc != 0: sys.exit(rc) +def get_interface_mode(): + mode = os.getenv('SONIC_CLI_IFACE_MODE') + if mode is None: + mode = "default" + return mode + + +# +# Use this method to validate unicast IPv4 address +# +def is_ip4_addr_valid(addr, display): + v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] + try: + ip = ipaddress.ip_address(unicode(addr)) + if (ip.version == 4): + if (ip.is_reserved): + if display: + click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) + return False + elif (ip.is_multicast): + if display: + click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) + return False + elif (ip in v4_invalid_list): + if display: + click.echo ("{} Not Valid.".format(addr)) + return False + else: + return True + + else: + if display: + click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) + return False + + except ValueError: + return False + +def is_ip_prefix_in_key(key): + ''' + Function to check if IP address is present in the key. If it + is present, then the key would be a tuple or else, it shall be + be string + ''' + return (isinstance(key, tuple)) + + # Global class instance for SONiC interface name to alias conversion iface_alias_converter = clicommon.InterfaceAliasConverter() @@ -1490,16 +1537,6 @@ def ztp(status, verbose): cmd = cmd + " --verbose" run_command(cmd, display_cmd=verbose) - -# -# 'vxlan' group ("show vxlan ...") -# - -@cli.group(cls=AliasedGroup, default_if_no_args=False) -def vxlan(): - """Show VXLAN information""" - pass - @vxlan.command() def interface(): """Show VXLAN VTEP Information""" @@ -1526,7 +1563,7 @@ def interface(): click.echo(output) if vtep_sip is not '0.0.0.0': - vxlan_table = config_db.get_table('EVPN_NVO') + vxlan_table = config_db.get_table('VXLAN_EVPN_NVO') vxlan_keys = vxlan_table.keys() if vxlan_keys is not None: for key in natsorted(vxlan_keys): @@ -1560,26 +1597,6 @@ def vlanvnimap(count): if count is not None: vxlan_keys = config_db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') - vlan = 'Vlan{}'.format(vid) - vxlan_keys = vxlan_table.keys() - - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = vxlan_table[key]['vlan'] - if(key1 == vlan): - netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 not in suppress_table: - supp_str = "Not Configured" - else: - supp_str = "Configured" - - body.append([vxlan_table[key]['vlan'], supp_str, netdev]) - click.echo(tabulate(body, header, tablefmt="grid")) - return - print(vlan + " is not configured in vxlan tunnel map table") - if not vxlan_keys: vxlan_count = 0 From c41017513b394585b75470bc6bfe707e6d0d3518 Mon Sep 17 00:00:00 2001 From: Karthikeyan Ananthakrishnan Date: Thu, 18 Jun 2020 07:07:43 -0700 Subject: [PATCH 08/19] NeighSuppressConfigCliFix --- config/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/config/main.py b/config/main.py index 13769811a7..c11c9cb4a7 100755 --- a/config/main.py +++ b/config/main.py @@ -4127,7 +4127,6 @@ def neigh_suppress(ctx): config_db = ConfigDBConnector() config_db.connect() ctx.obj = {'db': config_db} - pass @neigh_suppress.command('enable') @click.argument('vid', metavar='', required=True, type=int) From 818009b885c9538e14f7f34502ecf330a8f96206 Mon Sep 17 00:00:00 2001 From: Tapash Das Date: Thu, 18 Jun 2020 21:39:39 -0700 Subject: [PATCH 09/19] Added UT Fix for VRF VNI MAP CLI. --- config/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/main.py b/config/main.py index c11c9cb4a7..4a99cee67c 100755 --- a/config/main.py +++ b/config/main.py @@ -2730,9 +2730,9 @@ def del_vrf(ctx, vrf_name): @click.argument('vni', metavar='', required=True) @click.pass_context def add_vrf_vni_map(ctx, vrfname, vni): - db = ctx.obj['db'] + config_db = ctx.obj['config_db'] found = 0 - if vrfname not in db.get_table('VRF').keys(): + 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)) @@ -2740,7 +2740,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): if (int(vni) < 1) or (int(vni) > 16777215): ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) - vxlan_table = db.get_table('VXLAN_TUNNEL_MAP') + 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: @@ -2752,7 +2752,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): ctx.fail(" VLAN VNI not mapped. Please create VLAN VNI map entry first ") found = 0 - vrf_table = db.get_table('VRF') + vrf_table = config_db.get_table('VRF') vrf_keys = vrf_table.keys() if vrf_keys is not None: for vrf_key in vrf_keys: @@ -2763,17 +2763,17 @@ def add_vrf_vni_map(ctx, vrfname, vni): if (found == 1): ctx.fail("VNI already mapped to vrf {}".format(vrf_key)) - db.mod_entry('VRF', vrfname, {"vni": vni}) + config_db.mod_entry('VRF', vrfname, {"vni": vni}) @vrf.command('del_vrf_vni_map') @click.argument('vrfname', metavar='', required=True, type=str) @click.pass_context def del_vrf_vni_map(ctx, vrfname): - db = ctx.obj['db'] + config_db = ctx.obj['config_db'] if vrfname not in db.get_table('VRF').keys(): ctx.fail("vrf {} doesnt exists".format(vrfname)) - db.mod_entry('VRF', vrfname, {"vni": 0}) + config_db.mod_entry('VRF', vrfname, {"vni": 0}) # # 'route' group ('config route ...') From d5071458fe4b2f7ff92e30287d8eeb883fcc33bd Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Tue, 22 Sep 2020 19:50:57 -0700 Subject: [PATCH 10/19] Incorporated Review comment regarding hardcoding of values. --- config/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/main.py b/config/main.py index 4a99cee67c..e099234c0d 100755 --- a/config/main.py +++ b/config/main.py @@ -2737,7 +2737,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): if not vni.isdigit(): ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni)) - if (int(vni) < 1) or (int(vni) > 16777215): + if 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') @@ -3926,7 +3926,6 @@ def add_vxlan_map(ctx, vxlan_name, vlan, vni): 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 (int(vni) < 1) or (int(vni) > 16777215): if vni_id_is_valid(int(vni)) is False: ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) @@ -3966,7 +3965,6 @@ def del_vxlan_map(ctx, vxlan_name, vlan, vni): 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 (int(vni) < 1) or (int(vni) > 16777215): if vni_id_is_valid(int(vni)) is False: ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) From 432e7c1ffa6ff457d2d272344ca26bf94dbfa48b Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Wed, 11 Nov 2020 09:27:42 -0800 Subject: [PATCH 11/19] Review comments incorporated. 1. Moved out vxlan related config and show utilities to a separate file. 2. Moved some utility functions to utilitie_common --- config/main.py | 477 +--------------------------------------- config/vxlan.py | 346 +++++++++++++++++++++++++++++ show/main.py | 347 ----------------------------- show/vxlan.py | 306 +++++++++++++++++++++++++- utilities_common/cli.py | 60 +++++ 5 files changed, 712 insertions(+), 824 deletions(-) create mode 100644 config/vxlan.py diff --git a/config/main.py b/config/main.py index e099234c0d..3c07f13f8a 100755 --- a/config/main.py +++ b/config/main.py @@ -23,7 +23,6 @@ import utilities_common.cli as clicommon from .utils import log - from . import aaa from . import chassis_modules from . import console @@ -34,6 +33,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', '-?']) @@ -341,76 +341,7 @@ def interface_name_is_valid(config_db, interface_name): return True return False -# -# Use this method to validate unicast IPv4 address -# -def is_ip4_addr_valid(addr, display): - v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] - try: - ip = ipaddress.ip_address(unicode(addr)) - if (ip.version == 4): - if (ip.is_reserved): - if display: - click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) - return False - elif (ip.is_multicast): - if display: - click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) - return False - elif (ip in v4_invalid_list): - if display: - click.echo ("{} Not Valid.".format(addr)) - return False - else: - return True - - else: - if display: - click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) - return False - - except ValueError: - return False - -def vlan_id_is_valid(vid): - """Check if the vlan id is in acceptable range (between 1 and 4094) - """ - - if vid<1 or vid>4094: - return False - - return True - -def vni_id_is_valid(vni): - """Check if the vni id is in acceptable range (between 1 and 2^24) - """ - - if (vni < 1) or (vni > 16777215): - return False - - return True - -def is_vni_vrf_mapped(ctx, vni): - """Check if the vni is mapped to vrf - """ - - found = 0 - db = ctx.obj['db'] - vrf_table = 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): - print "VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key) - return False - - return True - -def interface_name_to_alias(interface_name): +def interface_name_to_alias(config_db, interface_name): """Return alias interface name if default name is given as argument """ # If the input parameter config_db is None, derive it from interface. @@ -953,6 +884,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, @@ -2737,7 +2669,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): if not vni.isdigit(): ctx.fail("Invalid VNI {}. Only valid VNI is accepted".format(vni)) - if vni_id_is_valid(int(vni)) is False: + 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') @@ -3752,406 +3684,5 @@ def delete(ctx): sflow_tbl['global'].pop('agent_id') config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) -# -# 'feature' command ('config feature name state') -# -@config.command('feature') -@click.argument('name', metavar='', required=True) -@click.argument('state', metavar='', required=True, type=click.Choice(["enabled", "disabled"])) -def feature_status(name, state): - """ Configure status of feature""" - config_db = ConfigDBConnector() - config_db.connect() - status_data = config_db.get_entry('FEATURE', name) - - if not status_data: - click.echo(" Feature '{}' doesn't exist".format(name)) - return - - config_db.mod_entry('FEATURE', name, {'status': state}) - -# -# 'container' group ('config container ...') -# -@config.group(cls=AbbreviationGroup, name='container', invoke_without_command=False) -def container(): - """Modify configuration of containers""" - pass - -# -# 'feature' group ('config container feature ...') -# -@container.group(cls=AbbreviationGroup, name='feature', invoke_without_command=False) -def feature(): - """Modify configuration of container features""" - pass - -# -# 'autorestart' subcommand ('config container feature autorestart ...') -# -@feature.command(name='autorestart', short_help="Configure the status of autorestart feature for specific container") -@click.argument('container_name', metavar='', required=True) -@click.argument('autorestart_status', metavar='', required=True, type=click.Choice(["enabled", "disabled"])) -def autorestart(container_name, autorestart_status): - config_db = ConfigDBConnector() - config_db.connect() - container_feature_table = config_db.get_table('CONTAINER_FEATURE') - if not container_feature_table: - click.echo("Unable to retrieve container feature table from Config DB.") - return - - if not container_feature_table.has_key(container_name): - click.echo("Unable to retrieve features for container '{}'".format(container_name)) - return - - config_db.mod_entry('CONTAINER_FEATURE', container_name, {'auto_restart': autorestart_status}) - -# -# 'vxlan' group ('config vxlan ...') -# -@config.group() -@click.pass_context -def vxlan(ctx): - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} - -@vxlan.command('add') -@click.argument('vxlan_name', metavar='', required=True) -@click.argument('src_ip', metavar='', required=True) -@click.pass_context -def add_vxlan(ctx, vxlan_name, src_ip): - """Add VXLAN""" - if not is_ip4_addr_valid(src_ip, True): - ctx.fail("{} invalid src ip address".format(src_ip)) - db = ctx.obj['db'] - - vxlan_keys = db.keys('CONFIG_DB', "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.set_entry('VXLAN_TUNNEL', vxlan_name, fvs) - -@vxlan.command('del') -@click.argument('vxlan_name', metavar='', required=True) -@click.pass_context -def del_vxlan(ctx, vxlan_name): - """Del VXLAN""" - db = ctx.obj['db'] - - vxlan_keys = db.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("Please delete the EVPN NVO configuration.") - - vxlan_keys = db.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.set_entry('VXLAN_TUNNEL', vxlan_name, None) - -@vxlan.group('evpn_nvo') -@click.pass_context -def vxlan_evpn_nvo(ctx): - pass - -@vxlan_evpn_nvo.command('add') -@click.argument('nvo_name', metavar='', required=True) -@click.argument('vxlan_name', metavar='', required=True) -@click.pass_context -def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): - """Add NVO""" - db = ctx.obj['db'] - vxlan_keys = db.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.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: - ctx.fail("VTEP {} not configured".format(vxlan_name)) - - fvs = {'source_vtep': vxlan_name} - db.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs) - -@vxlan_evpn_nvo.command('del') -@click.argument('nvo_name', metavar='', required=True) -@click.pass_context -def del_vxlan_evpn_nvo(ctx, nvo_name): - """Del NVO""" - db = ctx.obj['db'] - vxlan_keys = db.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.set_entry('VXLAN_EVPN_NVO', nvo_name, None) - -@vxlan.group('map') -@click.pass_context -def vxlan_map(ctx): - pass - -@vxlan_map.command('add') -@click.argument('vxlan_name', metavar='', required=True) -@click.argument('vlan', metavar='', required=True) -@click.argument('vni', metavar='', required=True) -@click.pass_context -def add_vxlan_map(ctx, vxlan_name, vlan, vni): - """Add VLAN-VNI map entry""" - if not vlan.isdigit(): - ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni)) - if vlan_id_is_valid(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 vni_id_is_valid(int(vni)) is False: - ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) - - db = ctx.obj['db'] - vlan_name = "Vlan" + vlan - - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: - ctx.fail("VTEP {} not configured".format(vxlan_name)) - - if len(db.get_entry('VLAN', vlan_name)) == 0: - ctx.fail("{} not configured".format(vlan_name)) - - vxlan_table = 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]['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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) - -@vxlan_map.command('del') -@click.argument('vxlan_name', metavar='', required=True) -@click.argument('vlan', metavar='', required=True) -@click.argument('vni', metavar='', required=True) -@click.pass_context -def del_vxlan_map(ctx, vxlan_name, vlan, vni): - """Del VLAN-VNI map entry""" - if not vlan.isdigit(): - ctx.fail("Invalid vlan {}. Only valid vlan is accepted".format(vni)) - if vlan_id_is_valid(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 vni_id_is_valid(int(vni)) is False: - ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) - - db = ctx.obj['db'] - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: - ctx.fail("VTEP {} not configured".format(vxlan_name)) - found = 0 - vrf_table = 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 mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key)) - - mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan - db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) - mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan - db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) - -@vxlan.group('map_range') -@click.pass_context -def vxlan_map_range(ctx): - pass - -@vxlan_map_range.command('add') -@click.argument('vxlan_name', metavar='', required=True) -@click.argument('vlan_start', metavar='', required=True, type=int) -@click.argument('vlan_end', metavar='', required=True, type=int) -@click.argument('vni_start', metavar='', required=True, type=int) -@click.pass_context -def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): - """Add Range of vlan-vni mappings""" - if vlan_id_is_valid(vlan_start) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - if vlan_id_is_valid(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 vni_id_is_valid(vni_start) is False: - ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start)) - if 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)) - - db = ctx.obj['db'] - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: - ctx.fail("VTEP {} not configured".format(vxlan_name)) - vlan_end = vlan_end + 1 - vxlan_table = db.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.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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) - -@vxlan_map_range.command('del') -@click.argument('vxlan_name', metavar='', required=True) -@click.argument('vlan_start', metavar='', required=True, type=int) -@click.argument('vlan_end', metavar='', required=True, type=int) -@click.argument('vni_start', metavar='', required=True, type=int) -@click.pass_context -def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): - """Del Range of vlan-vni mappings""" - if vlan_id_is_valid(vlan_start) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - if vlan_id_is_valid(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 vni_id_is_valid(vni_start) is False: - ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni_start)) - if 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)) - - db = ctx.obj['db'] - if len(db.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 is_vni_vrf_mapped(ctx, 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.set_entry('VXLAN_TUNNEL_MAP', mapname, None) - -####### -# -# 'neigh_suppress' group ('config neigh_suppress...') -# -@config.group() -@click.pass_context -def neigh_suppress(ctx): - """ Neighbour Suppress VLAN-related configuration """ - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} - -@neigh_suppress.command('enable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def enable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if vlan_id_is_valid(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - if len(db.get_entry('VLAN', vlan)) == 0: - click.echo("{} doesn't exist".format(vlan)) - return - fvs = {'suppress': "on"} - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) - -@neigh_suppress.command('disable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def disable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if vlan_id_is_valid(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) -####### -# -# 'neigh_suppress' group ('config neigh_suppress...') -# -@config.group() -@click.pass_context -def neigh_suppress(ctx): - """ Neighbour Suppress VLAN-related configuration """ - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} - -@neigh_suppress.command('enable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def enable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if vlan_id_is_valid(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - if len(db.get_entry('VLAN', vlan)) == 0: - click.echo("{} doesn't exist".format(vlan)) - return - fvs = {'suppress': "on"} - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) - -@neigh_suppress.command('disable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def disable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if vlan_id_is_valid(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) - - - if __name__ == '__main__': config() - diff --git a/config/vxlan.py b/config/vxlan.py new file mode 100644 index 0000000000..03a05636c1 --- /dev/null +++ b/config/vxlan.py @@ -0,0 +1,346 @@ +import click +import utilities_common.cli as clicommon + +# +# 'vxlan' group ('config vxlan ...') +# +@config.group() +@click.pass_context +def vxlan(ctx): + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + +@vxlan.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('src_ip', metavar='', required=True) +@click.pass_context +def add_vxlan(ctx, vxlan_name, src_ip): + """Add VXLAN""" + if not clicommon.is_ip4_addr_valid(src_ip, True): + ctx.fail("{} invalid src ip address".format(src_ip)) + db = ctx.obj['db'] + + vxlan_keys = db.keys('CONFIG_DB', "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.set_entry('VXLAN_TUNNEL', vxlan_name, fvs) + +@vxlan.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.pass_context +def del_vxlan(ctx, vxlan_name): + """Del VXLAN""" + db = ctx.obj['db'] + + vxlan_keys = db.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("Please delete the EVPN NVO configuration.") + + vxlan_keys = db.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.set_entry('VXLAN_TUNNEL', vxlan_name, None) + +@vxlan.group('evpn_nvo') +@click.pass_context +def vxlan_evpn_nvo(ctx): + pass + +@vxlan_evpn_nvo.command('add') +@click.argument('nvo_name', metavar='', required=True) +@click.argument('vxlan_name', metavar='', required=True) +@click.pass_context +def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): + """Add NVO""" + db = ctx.obj['db'] + vxlan_keys = db.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.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + + fvs = {'source_vtep': vxlan_name} + db.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs) + +@vxlan_evpn_nvo.command('del') +@click.argument('nvo_name', metavar='', required=True) +@click.pass_context +def del_vxlan_evpn_nvo(ctx, nvo_name): + """Del NVO""" + db = ctx.obj['db'] + vxlan_keys = db.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.set_entry('VXLAN_EVPN_NVO', nvo_name, None) + +@vxlan.group('map') +@click.pass_context +def vxlan_map(ctx): + pass + +@vxlan_map.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan', metavar='', required=True) +@click.argument('vni', metavar='', required=True) +@click.pass_context +def add_vxlan_map(ctx, vxlan_name, vlan, vni): + """Add VLAN-VNI map entry""" + 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)) + + db = ctx.obj['db'] + vlan_name = "Vlan" + vlan + + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + + if len(db.get_entry('VLAN', vlan_name)) == 0: + ctx.fail("{} not configured".format(vlan_name)) + + vxlan_table = 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]['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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + +@vxlan_map.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan', metavar='', required=True) +@click.argument('vni', metavar='', required=True) +@click.pass_context +def del_vxlan_map(ctx, vxlan_name, vlan, vni): + """Del VLAN-VNI map entry""" + 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)) + + db = ctx.obj['db'] + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + found = 0 + vrf_table = 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 mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key)) + + mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan + db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan + db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + +@vxlan.group('map_range') +@click.pass_context +def vxlan_map_range(ctx): + pass + +@vxlan_map_range.command('add') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan_start', metavar='', required=True, type=int) +@click.argument('vlan_end', metavar='', required=True, type=int) +@click.argument('vni_start', metavar='', required=True, type=int) +@click.pass_context +def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): + """Add Range of vlan-vni mappings""" + 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)) + + db = ctx.obj['db'] + if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + ctx.fail("VTEP {} not configured".format(vxlan_name)) + vlan_end = vlan_end + 1 + vxlan_table = db.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.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.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + +@vxlan_map_range.command('del') +@click.argument('vxlan_name', metavar='', required=True) +@click.argument('vlan_start', metavar='', required=True, type=int) +@click.argument('vlan_end', metavar='', required=True, type=int) +@click.argument('vni_start', metavar='', required=True, type=int) +@click.pass_context +def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): + """Del Range of vlan-vni mappings""" + 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)) + + db = ctx.obj['db'] + if len(db.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(ctx, 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.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + +####### +# +# 'neigh_suppress' group ('config neigh_suppress...') +# +@config.group() +@click.pass_context +def neigh_suppress(ctx): + """ Neighbour Suppress VLAN-related configuration """ + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + +@neigh_suppress.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def enable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if clicommon.is_vlanid_in_range(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + if len(db.get_entry('VLAN', vlan)) == 0: + click.echo("{} doesn't exist".format(vlan)) + return + fvs = {'suppress': "on"} + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + +@neigh_suppress.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def disable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if clicommon.is_vlanid_in_range(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) +####### +# +# 'neigh_suppress' group ('config neigh_suppress...') +# +@config.group() +@click.pass_context +def neigh_suppress(ctx): + """ Neighbour Suppress VLAN-related configuration """ + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + +@neigh_suppress.command('enable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def enable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if clicommon.is_vlanid_in_range(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + if len(db.get_entry('VLAN', vlan)) == 0: + click.echo("{} doesn't exist".format(vlan)) + return + fvs = {'suppress': "on"} + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + +@neigh_suppress.command('disable') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def disable_neigh_suppress(ctx, vid): + db = ctx.obj['db'] + if clicommon.is_vlanid_in_range(vid) is False: + ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") + vlan = 'Vlan{}'.format(vid) + db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) diff --git a/show/main.py b/show/main.py index f5e0212df7..1e24989b91 100755 --- a/show/main.py +++ b/show/main.py @@ -106,53 +106,6 @@ def run_command(command, display_cmd=False, return_cmd=False): if rc != 0: sys.exit(rc) -def get_interface_mode(): - mode = os.getenv('SONIC_CLI_IFACE_MODE') - if mode is None: - mode = "default" - return mode - - -# -# Use this method to validate unicast IPv4 address -# -def is_ip4_addr_valid(addr, display): - v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] - try: - ip = ipaddress.ip_address(unicode(addr)) - if (ip.version == 4): - if (ip.is_reserved): - if display: - click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) - return False - elif (ip.is_multicast): - if display: - click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) - return False - elif (ip in v4_invalid_list): - if display: - click.echo ("{} Not Valid.".format(addr)) - return False - else: - return True - - else: - if display: - click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) - return False - - except ValueError: - return False - -def is_ip_prefix_in_key(key): - ''' - Function to check if IP address is present in the key. If it - is present, then the key would be a tuple or else, it shall be - be string - ''' - return (isinstance(key, tuple)) - - # Global class instance for SONiC interface name to alias conversion iface_alias_converter = clicommon.InterfaceAliasConverter() @@ -1537,305 +1490,5 @@ def ztp(status, verbose): cmd = cmd + " --verbose" run_command(cmd, display_cmd=verbose) -@vxlan.command() -def interface(): - """Show VXLAN VTEP Information""" - - config_db = ConfigDBConnector() - config_db.connect() - - # Fetching VTEP keys from config DB - click.secho('VTEP Information:\n', bold=True, underline=True) - vxlan_table = config_db.get_table('VXLAN_TUNNEL') - vxlan_keys = vxlan_table.keys() - vtep_sip = '0.0.0.0' - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = key.split('|',1) - vtepname = key1.pop(); - if 'src_ip' in vxlan_table[key]: - vtep_sip = vxlan_table[key]['src_ip'] - if vtep_sip is not '0.0.0.0': - output = '\tVTEP Name : ' + vtepname + ', SIP : ' + vxlan_table[key]['src_ip'] - else: - output = '\tVTEP Name : ' + vtepname - - click.echo(output) - - if vtep_sip is not '0.0.0.0': - vxlan_table = config_db.get_table('VXLAN_EVPN_NVO') - vxlan_keys = vxlan_table.keys() - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = key.split('|',1) - vtepname = key1.pop(); - output = '\tNVO Name : ' + vtepname + ', VTEP : ' + vxlan_table[key]['source_vtep'] - click.echo(output) - - vxlan_keys = config_db.keys('CONFIG_DB', "LOOPBACK_INTERFACE|*") - loopback = 'Not Configured' - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = key.split('|',2) - if len(key1) == 3 and key1[2] == vtep_sip+'/32': - loopback = key1[1] - break - output = '\tSource interface : ' + loopback - if vtep_sip != '0.0.0.0': - click.echo(output) - -@vxlan.command() -@click.argument('count', required=False) -def vlanvnimap(count): - """Show VLAN VNI Mapping Information""" - - header = ['VLAN', 'VNI'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - if count is not None: - vxlan_keys = config_db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") - - if not vxlan_keys: - vxlan_count = 0 - else: - vxlan_count = len(vxlan_keys) - - output = 'Total mapping count:' - output += ('%s \n' % (str(vxlan_count))) - click.echo(output) - else: - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - vxlan_keys = vxlan_table.keys() - num=0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - body.append([vxlan_table[key]['vlan'], vxlan_table[key]['vni']]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@vxlan.command() -def vrfvnimap(): - """Show VRF VNI Mapping Information""" - - header = ['VRF', 'VNI'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - vrf_table = config_db.get_table('VRF') - vrf_keys = vrf_table.keys() - num=0 - if vrf_keys is not None: - for key in natsorted(vrf_keys): - if ('vni' in vrf_table[key]): - body.append([key, vrf_table[key]['vni']]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@vxlan.command() -@click.argument('count', required=False) -def tunnel(count): - """Show All VXLAN Tunnels Information""" - - if (count is not None) and (count != 'count'): - click.echo("Unacceptable argument {}".format(count)) - return - - header = ['SIP', 'DIP', 'Creation Source', 'OperStatus'] - body = [] - db = SonicV2Connector(host='127.0.0.1') - db.connect(db.STATE_DB) - - vxlan_keys = db.keys(db.STATE_DB, 'VXLAN_TUNNEL_TABLE|*') - - if vxlan_keys is not None: - vxlan_count = len(vxlan_keys) - else: - vxlan_count = 0 - - if (count is not None): - output = 'Total mapping count:' - output += ('%s \n' % (str(vxlan_count))) - click.echo(output) - else: - num = 0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - vxlan_table = db.get_all(db.STATE_DB, key); - if vxlan_table is None: - continue - body.append([vxlan_table['src_ip'], vxlan_table['dst_ip'], vxlan_table['tnl_src'], 'oper_' + vxlan_table['operstatus']]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@vxlan.command() -@click.argument('remote_vtep_ip', required=True) -@click.argument('count', required=False) -def remote_vni(remote_vtep_ip, count): - """Show Vlans extended to the remote VTEP""" - - if (remote_vtep_ip != 'all') and (is_ip4_addr_valid(remote_vtep_ip, True) is False): - click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) - return - - header = ['VLAN', 'RemoteVTEP', 'VNI'] - body = [] - db = SonicV2Connector(host='127.0.0.1') - db.connect(db.APPL_DB) - - if(remote_vtep_ip == 'all'): - vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*') - else: - vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*' + remote_vtep_ip + '*') - - if count is not None: - if not vxlan_keys: - vxlan_count = 0 - else: - vxlan_count = len(vxlan_keys) - - output = 'Total mapping count:' - output += ('%s \n' % (str(vxlan_count))) - click.echo(output) - else: - num = 0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = key.split(':') - rmtip = key1.pop(); - #if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: - # continue - vxlan_table = db.get_all(db.APPL_DB, key); - if vxlan_table is None: - continue - body.append([key1.pop(), rmtip, vxlan_table['vni']]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@vxlan.command() -@click.argument('remote_vtep_ip', required=True) -@click.argument('count', required=False) -def remote_mac(remote_vtep_ip, count): - """Show MACs pointing to the remote VTEP""" - - if (remote_vtep_ip != 'all') and (is_ip4_addr_valid(remote_vtep_ip, True) is False): - click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) - return - - header = ['VLAN', 'MAC', 'RemoteVTEP', 'VNI', 'Type'] - body = [] - db = SonicV2Connector(host='127.0.0.1') - db.connect(db.APPL_DB) - - vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_FDB_TABLE:*') - - if ((count is not None) and (remote_vtep_ip == 'all')): - if not vxlan_keys: - vxlan_count = 0 - else: - vxlan_count = len(vxlan_keys) - - output = 'Total count:' - output += ('%s \n' % (str(vxlan_count))) - click.echo(output) - else: - num = 0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = key.split(':',2) - mac = key1.pop(); - vlan = key1.pop(); - vxlan_table = db.get_all(db.APPL_DB, key); - if vxlan_table is None: - continue - rmtip = vxlan_table['remote_vtep'] - if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: - continue - if count is None: - body.append([vlan, mac, rmtip, vxlan_table['vni'], vxlan_table['type']]) - num += 1 - if count is None: - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -#Neigh Suppress -@cli.group('neigh-suppress') -def neigh_suppress(): - """ show neigh_suppress """ - pass -@neigh_suppress.command('all') -def neigh_suppress_all(): - """ Show neigh_suppress all """ - - header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') - vxlan_keys = vxlan_table.keys() - num=0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = vxlan_table[key]['vlan'] - netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 not in suppress_table: - supp_str = "Not Configured" - else: - supp_str = "Configured" - body.append([vxlan_table[key]['vlan'], supp_str, netdev]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@neigh_suppress.command('vlan') -@click.argument('vid', metavar='', required=True, type=int) -def neigh_suppress_vlan(vid): - """ Show neigh_suppress vlan""" - header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') - vlan = 'Vlan{}'.format(vid) - vxlan_keys = vxlan_table.keys() - - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = vxlan_table[key]['vlan'] - if(key1 == vlan): - netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 in suppress_table: - supp_str = "Configured" - body.append([vxlan_table[key]['vlan'], supp_str, netdev]) - click.echo(tabulate(body, header, tablefmt="grid")) - return - print(vlan + " is not configured in vxlan tunnel map table") - if __name__ == '__main__': cli() diff --git a/show/vxlan.py b/show/vxlan.py index d9b0de85f0..8eb903b556 100644 --- a/show/vxlan.py +++ b/show/vxlan.py @@ -2,9 +2,9 @@ import utilities_common.cli as clicommon from natsort import natsorted from swsssdk import ConfigDBConnector +from swsssdk import SonicV2Connector from tabulate import tabulate - # # 'vxlan' command ("show vxlan") # @@ -13,7 +13,6 @@ def vxlan(): """Show vxlan related information""" pass - @vxlan.command() @click.argument('vxlan_name', required=True) def name(vxlan_name): @@ -41,9 +40,8 @@ def name(vxlan_name): click.echo(tabulate(table, header)) - @vxlan.command() -def tunnel(): +def tunnel_cfg(): """Show vxlan tunnel information""" config_db = ConfigDBConnector() config_db.connect() @@ -68,3 +66,303 @@ def tunnel(): table.append(r) click.echo(tabulate(table, header)) + +@vxlan.command() +def interface(): + """Show VXLAN VTEP Information""" + + config_db = ConfigDBConnector() + config_db.connect() + + # Fetching VTEP keys from config DB + click.secho('VTEP Information:\n', bold=True, underline=True) + vxlan_table = config_db.get_table('VXLAN_TUNNEL') + vxlan_keys = vxlan_table.keys() + vtep_sip = '0.0.0.0' + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',1) + vtepname = key1.pop(); + if 'src_ip' in vxlan_table[key]: + vtep_sip = vxlan_table[key]['src_ip'] + if vtep_sip is not '0.0.0.0': + output = '\tVTEP Name : ' + vtepname + ', SIP : ' + vxlan_table[key]['src_ip'] + else: + output = '\tVTEP Name : ' + vtepname + + click.echo(output) + + if vtep_sip is not '0.0.0.0': + vxlan_table = config_db.get_table('VXLAN_EVPN_NVO') + vxlan_keys = vxlan_table.keys() + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',1) + vtepname = key1.pop(); + output = '\tNVO Name : ' + vtepname + ', VTEP : ' + vxlan_table[key]['source_vtep'] + click.echo(output) + + vxlan_keys = config_db.keys('CONFIG_DB', "LOOPBACK_INTERFACE|*") + loopback = 'Not Configured' + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split('|',2) + if len(key1) == 3 and key1[2] == vtep_sip+'/32': + loopback = key1[1] + break + output = '\tSource interface : ' + loopback + if vtep_sip != '0.0.0.0': + click.echo(output) + +@vxlan.command() +@click.argument('count', required=False) +def vlanvnimap(count): + """Show VLAN VNI Mapping Information""" + + header = ['VLAN', 'VNI'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + if count is not None: + vxlan_keys = config_db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") + + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total count : ' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + vxlan_keys = vxlan_table.keys() + num=0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + body.append([vxlan_table[key]['vlan'], vxlan_table[key]['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +def vrfvnimap(): + """Show VRF VNI Mapping Information""" + + header = ['VRF', 'VNI'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vrf_table = config_db.get_table('VRF') + vrf_keys = vrf_table.keys() + num=0 + if vrf_keys is not None: + for key in natsorted(vrf_keys): + if ('vni' in vrf_table[key]): + body.append([key, vrf_table[key]['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('count', required=False) +def tunnel(count): + """Show All VXLAN Tunnels Information""" + + if (count is not None) and (count != 'count'): + click.echo("Unacceptable argument {}".format(count)) + return + + header = ['SIP', 'DIP', 'Creation Source', 'OperStatus'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB) + + vxlan_keys = db.keys(db.STATE_DB, 'VXLAN_TUNNEL_TABLE|*') + + if vxlan_keys is not None: + vxlan_count = len(vxlan_keys) + else: + vxlan_count = 0 + + if (count is not None): + output = 'Total count : ' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + vxlan_table = db.get_all(db.STATE_DB, key); + if vxlan_table is None: + continue + body.append([vxlan_table['src_ip'], vxlan_table['dst_ip'], vxlan_table['tnl_src'], 'oper_' + vxlan_table['operstatus']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('remote_vtep_ip', required=True) +@click.argument('count', required=False) +def remote_vni(remote_vtep_ip, count): + """Show Vlans extended to the remote VTEP""" + + if (remote_vtep_ip != 'all') and (clicommon.is_ipaddress(remote_vtep_ip ) is False): + click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) + return + + header = ['VLAN', 'RemoteVTEP', 'VNI'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.APPL_DB) + + if(remote_vtep_ip == 'all'): + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*') + else: + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_REMOTE_VNI_TABLE:*' + remote_vtep_ip + '*') + + if count is not None: + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total count : ' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split(':') + rmtip = key1.pop(); + #if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: + # continue + vxlan_table = db.get_all(db.APPL_DB, key); + if vxlan_table is None: + continue + body.append([key1.pop(), rmtip, vxlan_table['vni']]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@vxlan.command() +@click.argument('remote_vtep_ip', required=True) +@click.argument('count', required=False) +def remote_mac(remote_vtep_ip, count): + """Show MACs pointing to the remote VTEP""" + + if (remote_vtep_ip != 'all') and (clicommon.is_ipaddress(remote_vtep_ip ) is False): + click.echo("Remote VTEP IP {} invalid format".format(remote_vtep_ip)) + return + + header = ['VLAN', 'MAC', 'RemoteVTEP', 'VNI', 'Type'] + body = [] + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.APPL_DB) + + vxlan_keys = db.keys(db.APPL_DB, 'VXLAN_FDB_TABLE:*') + + if ((count is not None) and (remote_vtep_ip == 'all')): + if not vxlan_keys: + vxlan_count = 0 + else: + vxlan_count = len(vxlan_keys) + + output = 'Total count : ' + output += ('%s \n' % (str(vxlan_count))) + click.echo(output) + else: + num = 0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = key.split(':',2) + mac = key1.pop(); + vlan = key1.pop(); + vxlan_table = db.get_all(db.APPL_DB, key); + if vxlan_table is None: + continue + rmtip = vxlan_table['remote_vtep'] + if remote_vtep_ip != 'all' and rmtip != remote_vtep_ip: + continue + if count is None: + body.append([vlan, mac, rmtip, vxlan_table['vni'], vxlan_table['type']]) + num += 1 + if count is None: + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +#Neigh Suppress +@click.group('neigh-suppress') +def neigh_suppress(): + """ show neigh_suppress """ + pass +@neigh_suppress.command('all') +def neigh_suppress_all(): + """ Show neigh_suppress all """ + + header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') + vxlan_keys = vxlan_table.keys() + num=0 + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = vxlan_table[key]['vlan'] + netdev = vxlan_keys[0][0]+"-"+key1[4:] + if key1 not in suppress_table: + supp_str = "Not Configured" + else: + supp_str = "Configured" + body.append([vxlan_table[key]['vlan'], supp_str, netdev]) + num += 1 + click.echo(tabulate(body, header, tablefmt="grid")) + output = 'Total count : ' + output += ('%s \n' % (str(num))) + click.echo(output) + +@neigh_suppress.command('vlan') +@click.argument('vid', metavar='', required=True, type=int) +def neigh_suppress_vlan(vid): + """ Show neigh_suppress vlan""" + header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] + body = [] + + config_db = ConfigDBConnector() + config_db.connect() + + vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') + suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') + vlan = 'Vlan{}'.format(vid) + vxlan_keys = vxlan_table.keys() + + if vxlan_keys is not None: + for key in natsorted(vxlan_keys): + key1 = vxlan_table[key]['vlan'] + if(key1 == vlan): + netdev = vxlan_keys[0][0]+"-"+key1[4:] + if key1 in suppress_table: + supp_str = "Configured" + body.append([vxlan_table[key]['vlan'], supp_str, netdev]) + click.echo(tabulate(body, header, tablefmt="grid")) + return + print(vlan + " is not configured in vxlan tunnel map table") diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 2379b4a603..751823b17e 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -301,6 +301,66 @@ def is_port_mirror_dst_port(config_db, port): return False +# +# Use this method to validate unicast IPv4 address +# +def is_ip4_addr_valid(addr, display): + v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] + try: + ip = ipaddress.ip_address(unicode(addr)) + if (ip.version == 4): + if (ip.is_reserved): + if display: + click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) + return False + elif (ip.is_multicast): + if display: + click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) + return False + elif (ip in v4_invalid_list): + if display: + click.echo ("{} Not Valid.".format(addr)) + return False + else: + return True + + else: + if display: + click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) + return False + + except ValueError: + return False + +def vni_id_is_valid(vni): + """Check if the vni id is in acceptable range (between 1 and 2^24) + """ + + if (vni < 1) or (vni > 16777215): + return False + + return True + +def is_vni_vrf_mapped(ctx, vni): + """Check if the vni is mapped to vrf + """ + + found = 0 + db = ctx.obj['db'] + vrf_table = 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): + print "VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key) + return False + + return True + def interface_has_mirror_config(mirror_table, interface_name): """Check if port is already configured with mirror config """ for _,v in mirror_table.items(): From 7221a175cdd6c580ee99dbc978c0c8cfebba5296 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Fri, 11 Dec 2020 02:21:49 -0800 Subject: [PATCH 12/19] Added tests for VXLAN CLI changes to vxlan config utils --- config/vxlan.py | 182 ++++++++++++------------------- tests/mock_tables/appl_db.json | 9 ++ tests/mock_tables/config_db.json | 22 ++++ tests/mock_tables/state_db.json | 18 +++ tests/vxlan_test.py | 164 ++++++++++++++++++++++++++++ utilities_common/cli.py | 2 +- 6 files changed, 284 insertions(+), 113 deletions(-) create mode 100644 tests/vxlan_test.py diff --git a/config/vxlan.py b/config/vxlan.py index 03a05636c1..6dc76a008e 100644 --- a/config/vxlan.py +++ b/config/vxlan.py @@ -4,24 +4,22 @@ # # 'vxlan' group ('config vxlan ...') # -@config.group() -@click.pass_context -def vxlan(ctx): - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} +@click.group() +def vxlan(): + pass @vxlan.command('add') @click.argument('vxlan_name', metavar='', required=True) @click.argument('src_ip', metavar='', required=True) -@click.pass_context -def add_vxlan(ctx, vxlan_name, src_ip): +@clicommon.pass_db +def add_vxlan(db, vxlan_name, src_ip): """Add VXLAN""" - if not clicommon.is_ip4_addr_valid(src_ip, True): + ctx = click.get_current_context() + + if not clicommon.is_ipaddress(src_ip): ctx.fail("{} invalid src ip address".format(src_ip)) - db = ctx.obj['db'] - vxlan_keys = db.keys('CONFIG_DB', "VXLAN_TUNNEL|*") + vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL') if not vxlan_keys: vxlan_count = 0 else: @@ -31,16 +29,16 @@ def add_vxlan(ctx, vxlan_name, src_ip): ctx.fail("VTEP already configured.") fvs = {'src_ip': src_ip} - db.set_entry('VXLAN_TUNNEL', vxlan_name, fvs) + db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, fvs) @vxlan.command('del') @click.argument('vxlan_name', metavar='', required=True) -@click.pass_context -def del_vxlan(ctx, vxlan_name): +@clicommon.pass_db +def del_vxlan(db, vxlan_name): """Del VXLAN""" - db = ctx.obj['db'] + ctx = click.get_current_context() - vxlan_keys = db.keys('CONFIG_DB', "VXLAN_EVPN_NVO|*") + vxlan_keys = db.cfgdb.get_keys('VXLAN_EVPN_NVO') if not vxlan_keys: vxlan_count = 0 else: @@ -49,7 +47,7 @@ def del_vxlan(ctx, vxlan_name): if(vxlan_count > 0): ctx.fail("Please delete the EVPN NVO configuration.") - vxlan_keys = db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") + vxlan_keys = db.cfgdb.get_keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") if not vxlan_keys: vxlan_count = 0 else: @@ -58,21 +56,20 @@ def del_vxlan(ctx, vxlan_name): if(vxlan_count > 0): ctx.fail("Please delete all VLAN VNI mappings.") - db.set_entry('VXLAN_TUNNEL', vxlan_name, None) + db.cfgdb.set_entry('VXLAN_TUNNEL', vxlan_name, None) @vxlan.group('evpn_nvo') -@click.pass_context -def vxlan_evpn_nvo(ctx): +def vxlan_evpn_nvo(): pass @vxlan_evpn_nvo.command('add') @click.argument('nvo_name', metavar='', required=True) @click.argument('vxlan_name', metavar='', required=True) -@click.pass_context -def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): +@clicommon.pass_db +def add_vxlan_evpn_nvo(db, nvo_name, vxlan_name): """Add NVO""" - db = ctx.obj['db'] - vxlan_keys = db.keys('CONFIG_DB', "VXLAN_EVPN_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: @@ -81,19 +78,19 @@ def add_vxlan_evpn_nvo(ctx, nvo_name, vxlan_name): if(vxlan_count > 0): ctx.fail("EVPN NVO already configured") - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + 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.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs) + db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, fvs) @vxlan_evpn_nvo.command('del') @click.argument('nvo_name', metavar='', required=True) -@click.pass_context -def del_vxlan_evpn_nvo(ctx, nvo_name): +@clicommon.pass_db +def del_vxlan_evpn_nvo(db, nvo_name): """Del NVO""" - db = ctx.obj['db'] - vxlan_keys = db.keys('CONFIG_DB', "VXLAN_TUNNEL_MAP|*") + ctx = click.get_current_context() + vxlan_keys = db.cfgdb.get_keys('VXLAN_TUNNEL_MAP') if not vxlan_keys: vxlan_count = 0 else: @@ -101,20 +98,21 @@ def del_vxlan_evpn_nvo(ctx, nvo_name): if(vxlan_count > 0): ctx.fail("Please delete all VLAN VNI mappings.") - db.set_entry('VXLAN_EVPN_NVO', nvo_name, None) + db.cfgdb.set_entry('VXLAN_EVPN_NVO', nvo_name, None) @vxlan.group('map') -@click.pass_context -def vxlan_map(ctx): +def vxlan_map(): pass @vxlan_map.command('add') @click.argument('vxlan_name', metavar='', required=True) @click.argument('vlan', metavar='', required=True) @click.argument('vni', metavar='', required=True) -@click.pass_context -def add_vxlan_map(ctx, vxlan_name, vlan, vni): +@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: @@ -124,16 +122,15 @@ def add_vxlan_map(ctx, vxlan_name, vlan, vni): if clicommon.vni_id_is_valid(int(vni)) is False: ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) - db = ctx.obj['db'] vlan_name = "Vlan" + vlan - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: ctx.fail("VTEP {} not configured".format(vxlan_name)) - if len(db.get_entry('VLAN', vlan_name)) == 0: + if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0: ctx.fail("{} not configured".format(vlan_name)) - vxlan_table = db.get_table('VXLAN_TUNNEL_MAP') + 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: @@ -145,15 +142,17 @@ def add_vxlan_map(ctx, vxlan_name, vlan, vni): fvs = {'vni': vni, 'vlan' : vlan_name} mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan_name - db.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) @vxlan_map.command('del') @click.argument('vxlan_name', metavar='', required=True) @click.argument('vlan', metavar='', required=True) @click.argument('vni', metavar='', required=True) -@click.pass_context -def del_vxlan_map(ctx, vxlan_name, vlan, vni): +@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: @@ -163,11 +162,10 @@ def del_vxlan_map(ctx, vxlan_name, vlan, vni): if clicommon.vni_id_is_valid(int(vni)) is False: ctx.fail("Invalid VNI {}. Valid range [1 to 16777215].".format(vni)) - db = ctx.obj['db'] - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: ctx.fail("VTEP {} not configured".format(vxlan_name)) found = 0 - vrf_table = db.get_table('VRF') + vrf_table = db.cfgdb.get_table('VRF') vrf_keys = vrf_table.keys() if vrf_keys is not None: for vrf_key in vrf_keys: @@ -179,13 +177,12 @@ def del_vxlan_map(ctx, vxlan_name, vlan, vni): ctx.fail("VNI mapped to vrf {}, Please remove VRF VNI mapping".format(vrf_key)) mapname = vxlan_name + '|' + 'map_' + vni + '_' + vlan - db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None) mapname = vxlan_name + '|' + 'map_' + vni + '_Vlan' + vlan - db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) + db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None) @vxlan.group('map_range') -@click.pass_context -def vxlan_map_range(ctx): +def vxlan_map_range(): pass @vxlan_map_range.command('add') @@ -193,9 +190,10 @@ def vxlan_map_range(ctx): @click.argument('vlan_start', metavar='', required=True, type=int) @click.argument('vlan_end', metavar='', required=True, type=int) @click.argument('vni_start', metavar='', required=True, type=int) -@click.pass_context -def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): +@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: @@ -207,11 +205,10 @@ def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, 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)) - db = ctx.obj['db'] - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + 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.get_table('VXLAN_TUNNEL_MAP') + vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP') vxlan_keys = vxlan_table.keys() for vid in range (vlan_start, vlan_end): @@ -219,7 +216,7 @@ def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): vnid = vni_start+vid-vlan_start vni_name = '{}'.format(vnid) match_found = 'no' - if len(db.get_entry('VLAN', vlan_name)) == 0: + if len(db.cfgdb.get_entry('VLAN', vlan_name)) == 0: click.echo("{} not configured".format(vlan_name)) continue if vxlan_keys is not None: @@ -237,16 +234,17 @@ def add_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): fvs = {'vni': vni_name, 'vlan' : vlan_name} mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name - db.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) + db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, fvs) @vxlan_map_range.command('del') @click.argument('vxlan_name', metavar='', required=True) @click.argument('vlan_start', metavar='', required=True, type=int) @click.argument('vlan_end', metavar='', required=True, type=int) @click.argument('vni_start', metavar='', required=True, type=int) -@click.pass_context -def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): +@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: @@ -258,8 +256,7 @@ def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, 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)) - db = ctx.obj['db'] - if len(db.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: + if len(db.cfgdb.get_entry('VXLAN_TUNNEL', vxlan_name)) == 0: ctx.fail("VTEP {} not configured".format(vxlan_name)) vlan_end = vlan_end + 1 @@ -268,79 +265,40 @@ def del_vxlan_map_range(ctx, vxlan_name, vlan_start, vlan_end, vni_start): vnid = vni_start+vid-vlan_start vni_name = '{}'.format(vnid) if clicommon.is_vni_vrf_mapped(ctx, vni_name) is False: - print "Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name) + print("Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name)) continue mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name - db.set_entry('VXLAN_TUNNEL_MAP', mapname, None) - -####### -# -# 'neigh_suppress' group ('config neigh_suppress...') -# -@config.group() -@click.pass_context -def neigh_suppress(ctx): - """ Neighbour Suppress VLAN-related configuration """ - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} - -@neigh_suppress.command('enable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def enable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if clicommon.is_vlanid_in_range(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - if len(db.get_entry('VLAN', vlan)) == 0: - click.echo("{} doesn't exist".format(vlan)) - return - fvs = {'suppress': "on"} - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None) -@neigh_suppress.command('disable') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def disable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] - if clicommon.is_vlanid_in_range(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) ####### # # 'neigh_suppress' group ('config neigh_suppress...') # -@config.group() -@click.pass_context -def neigh_suppress(ctx): +@click.group() +def neigh_suppress(): """ Neighbour Suppress VLAN-related configuration """ - config_db = ConfigDBConnector() - config_db.connect() - ctx.obj = {'db': config_db} @neigh_suppress.command('enable') @click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def enable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] +@clicommon.pass_db +def enable_neigh_suppress(db, vid): + ctx = click.get_current_context() if clicommon.is_vlanid_in_range(vid) is False: ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") vlan = 'Vlan{}'.format(vid) - if len(db.get_entry('VLAN', vlan)) == 0: + if len(db.cfgdb.get_entry('VLAN', vlan)) == 0: click.echo("{} doesn't exist".format(vlan)) return fvs = {'suppress': "on"} - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) + db.cfgdb.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) @neigh_suppress.command('disable') @click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def disable_neigh_suppress(ctx, vid): - db = ctx.obj['db'] +@clicommon.pass_db +def disable_neigh_suppress(db, vid): + ctx = click.get_current_context() if clicommon.is_vlanid_in_range(vid) is False: ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") vlan = 'Vlan{}'.format(vid) - db.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) + db.cfgdb.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index 5cdb65a180..803473b9c4 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -169,5 +169,14 @@ }, "SWITCH_TABLE:switch": { "vxlan_port": "13550" + }, + "VXLAN_REMOTE_VNI_TABLE:Vlan200:25.25.25.25": { + "vni": "200" + }, + "VXLAN_REMOTE_VNI_TABLE:Vlan200:25.25.25.26": { + "vni": "200" + }, + "VXLAN_REMOTE_VNI_TABLE:Vlan200:25.25.25.27": { + "vni": "200" } } diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 4a3c21e29f..3501daead6 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1332,5 +1332,27 @@ }, "PEER_SWITCH|sonic-switch" : { "address_ipv4": "10.2.2.2" + }, + "VXLAN_TUNNEL|vtep1": { + "src_ip": "1.1.1.1" + }, + "VXLAN_EVPN_NVO|nvo1": { + "source_vtep": "vtep1" + }, + "VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan100": { + "vni" : "100", + "vlan": "Vlan100" + }, + "VXLAN_TUNNEL_MAP|vtep1|map_101_Vlan101": { + "vni" : "101", + "vlan": "Vlan101" + }, + "VXLAN_TUNNEL_MAP|vtep1|map_102_Vlan102": { + "vni" : "102", + "vlan": "Vlan102" + }, + "VXLAN_TUNNEL_MAP|vtep1|map_200_Vlan200": { + "vni" : "200", + "vlan": "Vlan200" } } diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index b8cd11315e..5d225386ad 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -327,5 +327,23 @@ }, "MUX_CABLE_TABLE|Ethernet12": { "state": "unknown" + }, + "VXLAN_TUNNEL_TABLE|EVPN_25.25.25.25": { + "src_ip": "1.1.1.1", + "dst_ip": "25.25.25.25", + "tnl_src": "EVPN", + "operstatus": "down" + }, + "VXLAN_TUNNEL_TABLE|EVPN_25.25.25.26": { + "src_ip": "1.1.1.1", + "dst_ip": "25.25.25.26", + "tnl_src": "EVPN", + "operstatus": "down" + }, + "VXLAN_TUNNEL_TABLE|EVPN_25.25.25.27": { + "src_ip": "1.1.1.1", + "dst_ip": "25.25.25.27", + "tnl_src": "EVPN", + "operstatus": "down" } } diff --git a/tests/vxlan_test.py b/tests/vxlan_test.py new file mode 100644 index 0000000000..f4f70a997c --- /dev/null +++ b/tests/vxlan_test.py @@ -0,0 +1,164 @@ +import os +import traceback +from unittest import mock + +from click.testing import CliRunner + +import config.main as config +import show.main as show +from utilities_common.db import Db + +show_vxlan_interface_output="""\ +VTEP Information: + + VTEP Name : vtep1, SIP : 1.1.1.1 + NVO Name : nvo1, VTEP : vtep1 +""" + +show_vxlan_vlanvnimap_output="""\ ++---------+-------+ +| VLAN | VNI | ++=========+=======+ +| Vlan100 | 100 | ++---------+-------+ +| Vlan101 | 101 | ++---------+-------+ +| Vlan102 | 102 | ++---------+-------+ +| Vlan200 | 200 | ++---------+-------+ +Total count : 4 +""" + +show_vxlan_tunnel_output="""\ ++---------+-------------+-------------------+--------------+ +| SIP | DIP | Creation Source | OperStatus | ++=========+=============+===================+==============+ +| 1.1.1.1 | 25.25.25.25 | EVPN | oper_down | ++---------+-------------+-------------------+--------------+ +| 1.1.1.1 | 25.25.25.26 | EVPN | oper_down | ++---------+-------------+-------------------+--------------+ +| 1.1.1.1 | 25.25.25.27 | EVPN | oper_down | ++---------+-------------+-------------------+--------------+ +Total count : 3 +""" + +show_vxlan_remotevni_output="""\ ++---------+--------------+-------+ +| VLAN | RemoteVTEP | VNI | ++=========+==============+=======+ +| Vlan200 | 25.25.25.25 | 2000 | ++---------+--------------+-------+ +| Vlan200 | 25.25.25.26 | 2000 | ++---------+--------------+-------+ +| Vlan200 | 25.25.25.27 | 2000 | ++---------+--------------+-------+ +Total count : 3 +""" + +show_vxlan_remotevni_specific_output="""\ ++---------+--------------+-------+ +| VLAN | RemoteVTEP | VNI | ++=========+==============+=======+ +| Vlan200 | 25.25.25.27 | 2000 | ++---------+--------------+-------+ +Total count : 1 +""" +show_vxlan_vlanvnimap_cnt_output="""\ +Total count : 4 +""" + +show_vxlan_tunnel_cnt_output="""\ +Total count : 3 +""" + +show_vxlan_remotevni_cnt_output="""\ +Total count : 3 +""" + +show_vxlan_remotevni_specific_cnt_output="""\ +Total count : 1 +""" + +class TestVxlan(object): + @classmethod + def setup_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "1" + print("SETUP") + + def test_show_vxlan_interface(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["interface"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_interface_output + + def test_show_vxlan_vlanvnimap(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_vlanvnimap_output + + def test_show_vxlan_tunnel(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_tunnel_output + + def test_show_vxlan_remotevni(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["all"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_remotevni_output + + def test_show_vxlan_remotevni_specific(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["25.25.25.27"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_remotevni_specific_output + + def test_show_vxlan_vlanvnimap_cnt(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap"].commands["count"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_vlanvnimap_cnt_output + + def test_show_vxlan_tunnel_cnt(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"].commands["count"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_tunnel_cnt_output + + def test_show_vxlan_remotevni_cnt(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["all"].commands["count"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_remotevni_cnt_output + + def test_show_vxlan_remotevni_specific_cnt(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["25.25.25.27"].commands["count"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_remotevni_specific_cnt_output + + @classmethod + def teardown_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "0" + print("TEARDOWN") diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 751823b17e..39a7814901 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -356,7 +356,7 @@ def is_vni_vrf_mapped(ctx, vni): break if (found == 1): - print "VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key) + print("VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key)) return False return True From c696f694e13129b2be0e287d84629528e83165d2 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Fri, 11 Dec 2020 02:46:12 -0800 Subject: [PATCH 13/19] Changes to test script to add newline in output. --- tests/vxlan_test.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/vxlan_test.py b/tests/vxlan_test.py index f4f70a997c..f9cc2e5174 100644 --- a/tests/vxlan_test.py +++ b/tests/vxlan_test.py @@ -28,6 +28,7 @@ | Vlan200 | 200 | +---------+-------+ Total count : 4 + """ show_vxlan_tunnel_output="""\ @@ -41,6 +42,7 @@ | 1.1.1.1 | 25.25.25.27 | EVPN | oper_down | +---------+-------------+-------------------+--------------+ Total count : 3 + """ show_vxlan_remotevni_output="""\ @@ -54,6 +56,7 @@ | Vlan200 | 25.25.25.27 | 2000 | +---------+--------------+-------+ Total count : 3 + """ show_vxlan_remotevni_specific_output="""\ @@ -63,21 +66,26 @@ | Vlan200 | 25.25.25.27 | 2000 | +---------+--------------+-------+ Total count : 1 + """ show_vxlan_vlanvnimap_cnt_output="""\ Total count : 4 + """ show_vxlan_tunnel_cnt_output="""\ Total count : 3 + """ show_vxlan_remotevni_cnt_output="""\ Total count : 3 + """ show_vxlan_remotevni_specific_cnt_output="""\ Total count : 1 + """ class TestVxlan(object): @@ -112,7 +120,7 @@ def test_show_vxlan_tunnel(self): def test_show_vxlan_remotevni(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["all"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni all"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -120,7 +128,7 @@ def test_show_vxlan_remotevni(self): def test_show_vxlan_remotevni_specific(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["25.25.25.27"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni 25.25.25.27"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -128,7 +136,7 @@ def test_show_vxlan_remotevni_specific(self): def test_show_vxlan_vlanvnimap_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap"].commands["count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap count"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -136,7 +144,7 @@ def test_show_vxlan_vlanvnimap_cnt(self): def test_show_vxlan_tunnel_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"].commands["count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel count"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -144,7 +152,7 @@ def test_show_vxlan_tunnel_cnt(self): def test_show_vxlan_remotevni_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["all"].commands["count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni all count"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -152,7 +160,7 @@ def test_show_vxlan_remotevni_cnt(self): def test_show_vxlan_remotevni_specific_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni"].commands["25.25.25.27"].commands["count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni 25.25.25.27 count"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 From 403d07e015d19a08d14e2d60b79e73188792a8f2 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Sun, 13 Dec 2020 06:07:26 -0800 Subject: [PATCH 14/19] test scripts for vxlan config and show utilities added. --- config/vxlan.py | 2 +- show/vxlan.py | 4 +- tests/vxlan_test.py | 97 ++++++++++++++++++++++++++++++++--------- utilities_common/cli.py | 5 +-- 4 files changed, 82 insertions(+), 26 deletions(-) diff --git a/config/vxlan.py b/config/vxlan.py index 6dc76a008e..4448b3b52b 100644 --- a/config/vxlan.py +++ b/config/vxlan.py @@ -264,7 +264,7 @@ def del_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start): vlan_name = 'Vlan{}'.format(vid) vnid = vni_start+vid-vlan_start vni_name = '{}'.format(vnid) - if clicommon.is_vni_vrf_mapped(ctx, vni_name) is False: + if clicommon.is_vni_vrf_mapped(db, vni_name) is False: print("Skipping Vlan {} VNI {} mapped delete. ".format(vlan_name, vni_name)) continue diff --git a/show/vxlan.py b/show/vxlan.py index 8eb903b556..b218cf90ee 100644 --- a/show/vxlan.py +++ b/show/vxlan.py @@ -214,7 +214,7 @@ def tunnel(count): @vxlan.command() @click.argument('remote_vtep_ip', required=True) @click.argument('count', required=False) -def remote_vni(remote_vtep_ip, count): +def remotevni(remote_vtep_ip, count): """Show Vlans extended to the remote VTEP""" if (remote_vtep_ip != 'all') and (clicommon.is_ipaddress(remote_vtep_ip ) is False): @@ -261,7 +261,7 @@ def remote_vni(remote_vtep_ip, count): @vxlan.command() @click.argument('remote_vtep_ip', required=True) @click.argument('count', required=False) -def remote_mac(remote_vtep_ip, count): +def remotemac(remote_vtep_ip, count): """Show MACs pointing to the remote VTEP""" if (remote_vtep_ip != 'all') and (clicommon.is_ipaddress(remote_vtep_ip ) is False): diff --git a/tests/vxlan_test.py b/tests/vxlan_test.py index f9cc2e5174..2a74ee27df 100644 --- a/tests/vxlan_test.py +++ b/tests/vxlan_test.py @@ -11,8 +11,8 @@ show_vxlan_interface_output="""\ VTEP Information: - VTEP Name : vtep1, SIP : 1.1.1.1 - NVO Name : nvo1, VTEP : vtep1 + VTEP Name : vtep1, SIP : 1.1.1.1 + NVO Name : nvo1, VTEP : vtep1 """ show_vxlan_vlanvnimap_output="""\ @@ -27,7 +27,7 @@ +---------+-------+ | Vlan200 | 200 | +---------+-------+ -Total count : 4 +Total count : 4 """ @@ -41,7 +41,7 @@ +---------+-------------+-------------------+--------------+ | 1.1.1.1 | 25.25.25.27 | EVPN | oper_down | +---------+-------------+-------------------+--------------+ -Total count : 3 +Total count : 3 """ @@ -49,13 +49,13 @@ +---------+--------------+-------+ | VLAN | RemoteVTEP | VNI | +=========+==============+=======+ -| Vlan200 | 25.25.25.25 | 2000 | +| Vlan200 | 25.25.25.25 | 200 | +---------+--------------+-------+ -| Vlan200 | 25.25.25.26 | 2000 | +| Vlan200 | 25.25.25.26 | 200 | +---------+--------------+-------+ -| Vlan200 | 25.25.25.27 | 2000 | +| Vlan200 | 25.25.25.27 | 200 | +---------+--------------+-------+ -Total count : 3 +Total count : 3 """ @@ -63,28 +63,28 @@ +---------+--------------+-------+ | VLAN | RemoteVTEP | VNI | +=========+==============+=======+ -| Vlan200 | 25.25.25.27 | 2000 | +| Vlan200 | 25.25.25.27 | 200 | +---------+--------------+-------+ -Total count : 1 +Total count : 1 """ show_vxlan_vlanvnimap_cnt_output="""\ -Total count : 4 +Total count : 4 """ show_vxlan_tunnel_cnt_output="""\ -Total count : 3 +Total count : 3 """ show_vxlan_remotevni_cnt_output="""\ -Total count : 3 +Total count : 3 """ show_vxlan_remotevni_specific_cnt_output="""\ -Total count : 1 +Total count : 1 """ @@ -120,7 +120,8 @@ def test_show_vxlan_tunnel(self): def test_show_vxlan_remotevni(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni all"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevni"], ["all"]) + #result = runner.invoke(show.cli.commands["vxlan"].commands["remotevni"].commands["all"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -128,7 +129,7 @@ def test_show_vxlan_remotevni(self): def test_show_vxlan_remotevni_specific(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni 25.25.25.27"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevni"],["25.25.25.27"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -136,7 +137,7 @@ def test_show_vxlan_remotevni_specific(self): def test_show_vxlan_vlanvnimap_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap"],["count"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -144,7 +145,7 @@ def test_show_vxlan_vlanvnimap_cnt(self): def test_show_vxlan_tunnel_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"], ["count"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -152,7 +153,7 @@ def test_show_vxlan_tunnel_cnt(self): def test_show_vxlan_remotevni_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni all count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevni"], ["all", "count"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -160,12 +161,68 @@ def test_show_vxlan_remotevni_cnt(self): def test_show_vxlan_remotevni_specific_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["remote-vni 25.25.25.27 count"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevni"], ["25.25.25.25", "count"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 assert result.output == show_vxlan_remotevni_specific_cnt_output + def test_config_vxlan_add(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["vxlan"].commands["map"].commands["del"], ["vtep1", "200", "200"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(config.config.commands["vxlan"].commands["map_range"].commands["del"], ["vtep1", "100", "102", "100"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(config.config.commands["vxlan"].commands["evpn_nvo"].commands["del"], ["nvo1"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(config.config.commands["vxlan"].commands["del"], ["vtep1"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(config.config.commands["vxlan"].commands["add"], ["vtep1", "1.1.1.1"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(show.cli.commands["vxlan"].commands["interface"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_interface_output + + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["100"], obj=db) + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["101"], obj=db) + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["102"], obj=db) + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["200"], obj=db) + + result = runner.invoke(config.config.commands["vxlan"].commands["map"].commands["add"], ["vtep1", "200", "200"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(config.config.commands["vxlan"].commands["map_range"].commands["add"], ["vtep1", "100", "102", "100"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + result = runner.invoke(show.cli.commands["vxlan"].commands["vlanvnimap"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_vlanvnimap_output + @classmethod def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0" diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 39a7814901..a5a67af076 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -341,13 +341,12 @@ def vni_id_is_valid(vni): return True -def is_vni_vrf_mapped(ctx, vni): +def is_vni_vrf_mapped(db, vni): """Check if the vni is mapped to vrf """ found = 0 - db = ctx.obj['db'] - vrf_table = db.get_table('VRF') + vrf_table = db.cfgdb.get_table('VRF') vrf_keys = vrf_table.keys() if vrf_keys is not None: for vrf_key in vrf_keys: From b95be1b593326735fcc8637304f9db6090809d97 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Sun, 13 Dec 2020 19:54:24 -0800 Subject: [PATCH 15/19] Removing ARP/ND suppression CLIs on behalf of karthikeyan --- config/vxlan.py | 31 ------------------------- show/vxlan.py | 60 ------------------------------------------------- 2 files changed, 91 deletions(-) diff --git a/config/vxlan.py b/config/vxlan.py index 4448b3b52b..382ab72815 100644 --- a/config/vxlan.py +++ b/config/vxlan.py @@ -271,34 +271,3 @@ def del_vxlan_map_range(db, vxlan_name, vlan_start, vlan_end, vni_start): mapname = vxlan_name + '|' + 'map_' + vni_name + '_' + vlan_name db.cfgdb.set_entry('VXLAN_TUNNEL_MAP', mapname, None) -####### -# -# 'neigh_suppress' group ('config neigh_suppress...') -# -@click.group() -def neigh_suppress(): - """ Neighbour Suppress VLAN-related configuration """ - -@neigh_suppress.command('enable') -@click.argument('vid', metavar='', required=True, type=int) -@clicommon.pass_db -def enable_neigh_suppress(db, vid): - ctx = click.get_current_context() - if clicommon.is_vlanid_in_range(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - if len(db.cfgdb.get_entry('VLAN', vlan)) == 0: - click.echo("{} doesn't exist".format(vlan)) - return - fvs = {'suppress': "on"} - db.cfgdb.set_entry('SUPPRESS_VLAN_NEIGH', vlan, fvs) - -@neigh_suppress.command('disable') -@click.argument('vid', metavar='', required=True, type=int) -@clicommon.pass_db -def disable_neigh_suppress(db, vid): - ctx = click.get_current_context() - if clicommon.is_vlanid_in_range(vid) is False: - ctx.fail(" Invalid Vlan Id , Valid Range : 1 to 4094 ") - vlan = 'Vlan{}'.format(vid) - db.cfgdb.set_entry('SUPPRESS_VLAN_NEIGH', vlan, None) diff --git a/show/vxlan.py b/show/vxlan.py index b218cf90ee..ececd50324 100644 --- a/show/vxlan.py +++ b/show/vxlan.py @@ -306,63 +306,3 @@ def remotemac(remote_vtep_ip, count): output += ('%s \n' % (str(num))) click.echo(output) -#Neigh Suppress -@click.group('neigh-suppress') -def neigh_suppress(): - """ show neigh_suppress """ - pass -@neigh_suppress.command('all') -def neigh_suppress_all(): - """ Show neigh_suppress all """ - - header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') - vxlan_keys = vxlan_table.keys() - num=0 - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = vxlan_table[key]['vlan'] - netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 not in suppress_table: - supp_str = "Not Configured" - else: - supp_str = "Configured" - body.append([vxlan_table[key]['vlan'], supp_str, netdev]) - num += 1 - click.echo(tabulate(body, header, tablefmt="grid")) - output = 'Total count : ' - output += ('%s \n' % (str(num))) - click.echo(output) - -@neigh_suppress.command('vlan') -@click.argument('vid', metavar='', required=True, type=int) -def neigh_suppress_vlan(vid): - """ Show neigh_suppress vlan""" - header = ['VLAN', 'STATUS', 'ASSOCIATED_NETDEV'] - body = [] - - config_db = ConfigDBConnector() - config_db.connect() - - vxlan_table = config_db.get_table('VXLAN_TUNNEL_MAP') - suppress_table = config_db.get_table('SUPPRESS_VLAN_NEIGH') - vlan = 'Vlan{}'.format(vid) - vxlan_keys = vxlan_table.keys() - - if vxlan_keys is not None: - for key in natsorted(vxlan_keys): - key1 = vxlan_table[key]['vlan'] - if(key1 == vlan): - netdev = vxlan_keys[0][0]+"-"+key1[4:] - if key1 in suppress_table: - supp_str = "Configured" - body.append([vxlan_table[key]['vlan'], supp_str, netdev]) - click.echo(tabulate(body, header, tablefmt="grid")) - return - print(vlan + " is not configured in vxlan tunnel map table") From 95dc9b94f1035e4d31c180f952879443fea92b79 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran <48232228+srj102@users.noreply.github.com> Date: Wed, 16 Dec 2020 10:17:19 +0530 Subject: [PATCH 16/19] Update main.py --- config/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index 75b30f6dc0..141a97e1c1 100644 --- a/config/main.py +++ b/config/main.py @@ -2976,7 +2976,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): @click.pass_context def del_vrf_vni_map(ctx, vrfname): config_db = ctx.obj['config_db'] - if vrfname not in db.get_table('VRF').keys(): + 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}) From d9e9df6c70f9f035bc169812ee706b3ec663925c Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran Date: Wed, 16 Dec 2020 09:15:49 -0800 Subject: [PATCH 17/19] Renamed show vxlan tunnel to show vxlan remotevtep. show vxlan tunnel_cfg (which reads from cfg db) which was originally show vxlan tunnel has been reverted back to show vxlan tunnel. --- show/vxlan.py | 6 +++--- tests/vxlan_test.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/show/vxlan.py b/show/vxlan.py index ececd50324..a9f18759e3 100644 --- a/show/vxlan.py +++ b/show/vxlan.py @@ -41,7 +41,7 @@ def name(vxlan_name): click.echo(tabulate(table, header)) @vxlan.command() -def tunnel_cfg(): +def tunnel(): """Show vxlan tunnel information""" config_db = ConfigDBConnector() config_db.connect() @@ -174,8 +174,8 @@ def vrfvnimap(): @vxlan.command() @click.argument('count', required=False) -def tunnel(count): - """Show All VXLAN Tunnels Information""" +def remotevtep(count): + """Show All Remote VTEP Information""" if (count is not None) and (count != 'count'): click.echo("Unacceptable argument {}".format(count)) diff --git a/tests/vxlan_test.py b/tests/vxlan_test.py index 2a74ee27df..881ea85174 100644 --- a/tests/vxlan_test.py +++ b/tests/vxlan_test.py @@ -112,7 +112,7 @@ def test_show_vxlan_vlanvnimap(self): def test_show_vxlan_tunnel(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"], []) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevtep"], []) print(result.exit_code) print(result.output) assert result.exit_code == 0 @@ -145,7 +145,7 @@ def test_show_vxlan_vlanvnimap_cnt(self): def test_show_vxlan_tunnel_cnt(self): runner = CliRunner() - result = runner.invoke(show.cli.commands["vxlan"].commands["tunnel"], ["count"]) + result = runner.invoke(show.cli.commands["vxlan"].commands["remotevtep"], ["count"]) print(result.exit_code) print(result.output) assert result.exit_code == 0 From 24958ff0e3dbaae6d31dfbca8486e6564f85b4ce Mon Sep 17 00:00:00 2001 From: Tapash Das Date: Thu, 17 Dec 2020 08:26:23 -0800 Subject: [PATCH 18/19] Updated review comments. --- tests/mock_tables/config_db.json | 4 ++++ tests/vxlan_test.py | 19 +++++++++++++++++++ utilities_common/cli.py | 31 ------------------------------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 39b520ebf6..06e1f65f80 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1355,6 +1355,10 @@ "vni" : "200", "vlan": "Vlan200" }, + "VRF|Vrf1": { + "fallback": "false", + "vni" : "1000" + }, "BUFFER_POOL|egress_lossless_pool": { "mode": "dynamic", "size": "13945824", diff --git a/tests/vxlan_test.py b/tests/vxlan_test.py index 881ea85174..74819f9f61 100644 --- a/tests/vxlan_test.py +++ b/tests/vxlan_test.py @@ -31,6 +31,17 @@ """ +show_vxlan_vrfvnimap_output="""\ ++-------+-------+ +| VRF | VNI | ++=======+=======+ +| Vrf1 | 1000 | ++-------+-------+ +Total count : 1 + +""" + + show_vxlan_tunnel_output="""\ +---------+-------------+-------------------+--------------+ | SIP | DIP | Creation Source | OperStatus | @@ -110,6 +121,14 @@ def test_show_vxlan_vlanvnimap(self): assert result.exit_code == 0 assert result.output == show_vxlan_vlanvnimap_output + def test_show_vxlan_vrfvnimap(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vxlan"].commands["vrfvnimap"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vxlan_vrfvnimap_output + def test_show_vxlan_tunnel(self): runner = CliRunner() result = runner.invoke(show.cli.commands["vxlan"].commands["remotevtep"], []) diff --git a/utilities_common/cli.py b/utilities_common/cli.py index a5a67af076..3644f71b10 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -301,37 +301,6 @@ def is_port_mirror_dst_port(config_db, port): return False -# -# Use this method to validate unicast IPv4 address -# -def is_ip4_addr_valid(addr, display): - v4_invalid_list = [ipaddress.IPv4Address(unicode('0.0.0.0')), ipaddress.IPv4Address(unicode('255.255.255.255'))] - try: - ip = ipaddress.ip_address(unicode(addr)) - if (ip.version == 4): - if (ip.is_reserved): - if display: - click.echo ("{} Not Valid, Reason: IPv4 reserved address range.".format(addr)) - return False - elif (ip.is_multicast): - if display: - click.echo ("{} Not Valid, Reason: IPv4 Multicast address range.".format(addr)) - return False - elif (ip in v4_invalid_list): - if display: - click.echo ("{} Not Valid.".format(addr)) - return False - else: - return True - - else: - if display: - click.echo ("{} Not Valid, Reason: Not an IPv4 address".format(addr)) - return False - - except ValueError: - return False - def vni_id_is_valid(vni): """Check if the vni id is in acceptable range (between 1 and 2^24) """ From 074ca0d492745504ee7719bffa38c0a71a3530c8 Mon Sep 17 00:00:00 2001 From: Tapash Das <48195098+tapashdas@users.noreply.github.com> Date: Fri, 18 Dec 2020 11:51:03 +0530 Subject: [PATCH 19/19] Update main.py Removed space as per review comment. --- config/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index 141a97e1c1..8af7abb3b7 100644 --- a/config/main.py +++ b/config/main.py @@ -2955,7 +2955,7 @@ def add_vrf_vni_map(ctx, vrfname, vni): break if (found == 0): - ctx.fail(" VLAN VNI not mapped. Please create VLAN VNI map entry first ") + ctx.fail("VLAN VNI not mapped. Please create VLAN VNI map entry first") found = 0 vrf_table = config_db.get_table('VRF')