diff --git a/config/main.py b/config/main.py index fe573e85bf..d8c5b03892 100755 --- a/config/main.py +++ b/config/main.py @@ -399,6 +399,45 @@ def interface_name_is_valid(interface_name): return True 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): """Return alias interface name if default name is given as argument """ @@ -538,7 +577,7 @@ def _is_neighbor_ipaddress(config_db, ipaddress): def _get_all_neighbor_ipaddresses(config_db, ignore_local_hosts=False): """Returns list of strings containing IP addresses of all BGP neighbors - if the flag ignore_local_hosts is set to True, additional check to see if + if the flag ignore_local_hosts is set to True, additional check to see if if the BGP neighbor AS number is same as local BGP AS number, if so ignore that neigbor. """ addrs = [] @@ -907,7 +946,7 @@ def load(filename, yes): # if any of the config files in linux host OR namespace is not present, return if not os.path.isfile(file): click.echo("The config_db file {} doesn't exist".format(file)) - return + return if namespace is None: command = "{} -j {} --write-to-db".format(SONIC_CFGGEN_PATH, file) @@ -1125,7 +1164,7 @@ def load_minigraph(no_service_restart): if os.path.isfile('/etc/sonic/acl.json'): run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True) - + # Write latest db version string into db db_migrator='/usr/bin/db_migrator.py' if os.path.isfile(db_migrator) and os.access(db_migrator, os.X_OK): @@ -1135,7 +1174,7 @@ def load_minigraph(no_service_restart): else: cfggen_namespace_option = " -n {}".format(namespace) run_command(db_migrator + ' -o set_version' + cfggen_namespace_option) - + # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them if not no_service_restart: @@ -1260,7 +1299,7 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): if queue is not None: session_info['queue'] = queue - + """ For multi-npu platforms we need to program all front asic namespaces """ @@ -1585,7 +1624,7 @@ def add_vlan_member(ctx, vid, interface_name, untagged): for entry in interface_table: if (interface_name == entry[0]): ctx.fail("{} is a L3 interface!".format(interface_name)) - + members.append(interface_name) vlan['members'] = members db.set_entry('VLAN', vlan_name, vlan) @@ -1683,7 +1722,7 @@ def add_snmp_agent_address(ctx, agentip, port, vrf): #Construct SNMP_AGENT_ADDRESS_CONFIG table key in the format ip|| key = agentip+'|' if port: - key = key+port + key = key+port key = key+'|' if vrf: key = key+vrf @@ -1704,7 +1743,7 @@ def del_snmp_agent_address(ctx, agentip, port, vrf): key = agentip+'|' if port: - key = key+port + key = key+port key = key+'|' if vrf: key = key+vrf @@ -1966,7 +2005,7 @@ def all(verbose): config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) config_db.connect() bgp_neighbor_ip_list = _get_all_neighbor_ipaddresses(config_db, ignore_local_hosts) - for ipaddress in bgp_neighbor_ip_list: + for ipaddress in bgp_neighbor_ip_list: _change_bgp_session_status_by_addr(config_db, ipaddress, 'up', verbose) # 'neighbor' subcommand @@ -2261,7 +2300,7 @@ def breakout(ctx, interface_name, mode, verbose, force_remove_dependencies, load sys.exit(0) def _get_all_mgmtinterface_keys(): - """Returns list of strings containing mgmt interface keys + """Returns list of strings containing mgmt interface keys """ config_db = ConfigDBConnector() config_db.connect() @@ -2595,6 +2634,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['config_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['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}) + # # 'route' group ('config route ...') # @@ -2985,9 +3074,9 @@ def priority(ctx, interface_name, priority, status): interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - + run_command("pfc config priority {0} {1} {2}".format(status, interface_name, priority)) - + # # 'platform' group ('config platform ...') # @@ -3086,10 +3175,10 @@ def naming_mode_alias(): def is_loopback_name_valid(loopback_name): """Loopback name validation """ - + if loopback_name[:CFG_LOOPBACK_PREFIX_LEN] != CFG_LOOPBACK_PREFIX : return False - if (loopback_name[CFG_LOOPBACK_PREFIX_LEN:].isdigit() is False or + if (loopback_name[CFG_LOOPBACK_PREFIX_LEN:].isdigit() is False or int(loopback_name[CFG_LOOPBACK_PREFIX_LEN:]) > CFG_LOOPBACK_ID_MAX_VAL) : return False if len(loopback_name) > CFG_LOOPBACK_NAME_TOTAL_LEN_MAX: @@ -3256,7 +3345,7 @@ def add_ntp_server(ctx, ntp_ip_address): if ntp_ip_address in ntp_servers: click.echo("NTP server {} is already configured".format(ntp_ip_address)) return - else: + else: db.set_entry('NTP_SERVER', ntp_ip_address, {'NULL': 'NULL'}) click.echo("NTP server {} added to configuration".format(ntp_ip_address)) try: @@ -3277,7 +3366,7 @@ def del_ntp_server(ctx, ntp_ip_address): if ntp_ip_address in ntp_servers: db.set_entry('NTP_SERVER', '{}'.format(ntp_ip_address), None) click.echo("NTP server {} removed from configuration".format(ntp_ip_address)) - else: + else: ctx.fail("NTP server {} is not configured.".format(ntp_ip_address)) try: click.echo("Restarting ntp-config service...") @@ -3565,7 +3654,7 @@ def delete(ctx): # # '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"])) @@ -3617,5 +3706,317 @@ def autorestart(container_name, autorestart_status): 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_ipaddress(src_ip): + 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 (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 + + 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) + if __name__ == '__main__': config() + diff --git a/scripts/fast-reboot b/scripts/fast-reboot index f368d06480..3768d33a86 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -240,6 +240,7 @@ function backup_database() if not string.match(k, 'FDB_TABLE|') and not string.match(k, 'WARM_RESTART_TABLE|') \ and not string.match(k, 'MIRROR_SESSION_TABLE|') \ and not string.match(k, 'PIM_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 b3e24d0532..3066daa278 100755 --- a/show/main.py +++ b/show/main.py @@ -38,6 +38,15 @@ # noinspection PyUnresolvedReferences import configparser +def is_ipaddress(val): + """ Validate if an entry is a valid IP """ + if not val: + return False + try: + netaddr.IPAddress(str(val)) + except: + return False + return True # This is from the aliases example: # https://github.com/pallets/click/blob/57c6f09611fc47ca80db0bd010f05998b3c0aa95/examples/aliases/aliases.py @@ -1179,8 +1188,8 @@ def priority(interface): cmd = 'pfc show priority' if interface is not None and get_interface_mode() == "alias": interface = iface_alias_converter.alias_to_name(interface) - - if interface is not None: + + if interface is not None: cmd += ' {0}'.format(interface) run_command(cmd) @@ -1192,8 +1201,8 @@ def asymmetric(interface): cmd = 'pfc show asymmetric' if interface is not None and get_interface_mode() == "alias": interface = iface_alias_converter.alias_to_name(interface) - - if interface is not None: + + if interface is not None: cmd += ' {0}'.format(interface) run_command(cmd) @@ -1727,7 +1736,7 @@ def protocol(verbose): from .bgp_quagga_v6 import bgp ipv6.add_command(bgp) elif routing_stack == "frr": - from .bgp_frr_v4 import bgp + from .bgp_frr_v4 import bgp ip.add_command(bgp) from .bgp_frr_v6 import bgp ipv6.add_command(bgp) @@ -2173,7 +2182,7 @@ def ntp(ctx, verbose): ntpstat_cmd = "ntpstat" ntpcmd = "ntpq -p -n" if is_mgmt_vrf_enabled(ctx) is True: - #ManagementVRF is enabled. Call ntpq using "ip vrf exec" or cgexec based on linux version + #ManagementVRF is enabled. Call ntpq using "ip vrf exec" or cgexec based on linux version os_info = os.uname() release = os_info[2].split('-') if parse_version(release[0]) > parse_version("4.9.0"): @@ -2971,7 +2980,7 @@ def pool(verbose): # Define GEARBOX commands only if GEARBOX is configured app_db = SonicV2Connector(host='127.0.0.1') -app_db.connect(app_db.APPL_DB) +app_db.connect(app_db.APPL_DB) if app_db.keys(app_db.APPL_DB, '_GEARBOX_TABLE:phy:*'): @cli.group(cls=AliasedGroup) @@ -3438,5 +3447,315 @@ def status(slot_index, verbose): run_command(cmd, display_cmd=verbose) + +# +# 'vxlan' group ("show vxlan ...") +# + +@cli.group(cls=AliasedGroup) +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_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 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_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 +@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()