diff --git a/acl_loader/main.py b/acl_loader/main.py index 362f1f75ea..f9201846b3 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -6,11 +6,13 @@ import syslog import tabulate from natsort import natsorted +import sonic_device_util import openconfig_acl import pyangbind.lib.pybindJSON as pybindJSON from swsssdk import ConfigDBConnector from swsssdk import SonicV2Connector +from swsssdk import SonicDBConfig def info(msg): @@ -114,12 +116,39 @@ def __init__(self): self.tables_db_info = {} self.rules_db_info = {} self.rules_info = {} + # Load global db config. This call is no-op in single npu platforms + SonicDBConfig.load_sonic_global_db_config() self.sessions_db_info = {} self.configdb = ConfigDBConnector() self.configdb.connect() self.statedb = SonicV2Connector(host="127.0.0.1") self.statedb.connect(self.statedb.STATE_DB) + # For multi-npu architecture we will have both global and per front asic namespace. + # Global namespace will be used for Control plane ACL which are via IPTables. + # Per ASIC namespace will be used for Data and Everflow ACL's. + # Global Configdb will have all ACL information for both Ctrl and Data/Evereflow ACL's + # and will be used as souurce of truth for ACL modification to config DB which will be done to both Global DB and + # front asic namespace + + self.per_npu_configdb = {} + + # State DB are used for to get mirror Session monitor port. + # For multi-npu platforms each asic namespace can have different monitor port + # dependinding on which route to session destination ip. So for multi-npu + # platforms we get state db for all front asic namespace in addition to + + self.per_npu_statedb = {} + + # Getting all front asic namespace and correspding config and state DB connector + + namespaces = sonic_device_util.get_all_namespaces() + for front_asic_namespaces in namespaces['front_ns']: + self.per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) + self.per_npu_configdb[front_asic_namespaces].connect() + self.per_npu_statedb[front_asic_namespaces] = SonicV2Connector(use_unix_socket_path=True, namespace=front_asic_namespaces) + self.per_npu_statedb[front_asic_namespaces].connect(self.per_npu_statedb[front_asic_namespaces].STATE_DB) + self.read_tables_info() self.read_rules_info() self.read_sessions_info() @@ -150,7 +179,14 @@ def read_policers_info(self): Read POLICER table from configuration database :return: """ - self.policers_db_info = self.configdb.get_table(self.POLICER) + + # For multi-npu platforms we will read from any one of front asic namespace + # config db as the information should be same across all config db + if self.per_npu_configdb: + namespace_configdb = (self.per_npu_configdb.values())[0] + self.policers_db_info = namespace_configdb.get_table(self.POLICER) + else: + self.policers_db_info = self.configdb.get_table(self.POLICER) def get_policers_db_info(self): return self.policers_db_info @@ -160,17 +196,30 @@ def read_sessions_info(self): Read MIRROR_SESSION table from configuration database :return: """ - self.sessions_db_info = self.configdb.get_table(self.CFG_MIRROR_SESSION_TABLE) + + # For multi-npu platforms we will read from any one of front asic namespace + # config db as the information should be same across all config db + if self.per_npu_configdb: + namespace_configdb = (self.per_npu_configdb.values())[0] + self.sessions_db_info = namespace_configdb.get_table(self.CFG_MIRROR_SESSION_TABLE) + else: + self.sessions_db_info = self.configdb.get_table(self.CFG_MIRROR_SESSION_TABLE) for key in self.sessions_db_info.keys(): - state_db_info = self.statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key)) - monitor_port = "" - if state_db_info: - status = state_db_info.get("status", "inactive") - monitor_port = state_db_info.get("monitor_port", "") + if self.per_npu_statedb: + # For multi-npu platforms we will read from all front asic name space + # statedb as the monitor port will be differnt for each asic + # and it's status also might be different (ideally should not happen) + # We will store them as dict of 'asic' : value + self.sessions_db_info[key]["status"] = {} + self.sessions_db_info[key]["monitor_port"] = {} + for namespace_key, namespace_statedb in self.per_npu_statedb.iteritems(): + state_db_info = namespace_statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key)) + self.sessions_db_info[key]["status"][namespace_key] = state_db_info.get("status", "inactive") if state_db_info else "error" + self.sessions_db_info[key]["monitor_port"][namespace_key] = state_db_info.get("monitor_port", "") if state_db_info else "" else: - status = "error" - self.sessions_db_info[key]["status"] = status - self.sessions_db_info[key]["monitor_port"] = monitor_port + state_db_info = self.statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key)) + self.sessions_db_info[key]["status"] = state_db_info.get("status", "inactive") if state_db_info else "error" + self.sessions_db_info[key]["monitor_port"] = state_db_info.get("monitor_port", "") if state_db_info else "" def get_sessions_db_info(self): return self.sessions_db_info @@ -309,7 +358,17 @@ def validate_actions(self, table_name, action_props): raise AclLoaderException("Table {} does not exist".format(table_name)) stage = self.tables_db_info[table_name].get("stage", Stage.INGRESS) - capability = self.statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE)) + + # check if per npu state db is there then read using first state db + # else read from global statedb + if self.per_npu_statedb: + # For multi-npu we will read using anyone statedb connector for front asic namespace. + # Same information should be there in all state DB's + # as it is static information about switch capability + namespace_statedb = (self.per_npu_statedb.values())[0] + capability = namespace_statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE)) + else: + capability = self.statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE)) for action_key in dict(action_props): key = "{}|{}".format(self.ACL_ACTIONS_CAPABILITY_FIELD, stage.upper()) if key not in capability: @@ -518,9 +577,16 @@ def full_update(self): """ for key in self.rules_db_info.keys(): if self.current_table is None or self.current_table == key[0]: - self.configdb.mod_entry(self.ACL_RULE, key, None) + self.configdb.mod_entry(self.ACL_RULE, key, None) + # Program for per front asic namespace also if present + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_entry(self.ACL_RULE, key, None) + self.configdb.mod_config({self.ACL_RULE: self.rules_info}) + # Program for per front asic namespace also if present + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_config({self.ACL_RULE: self.rules_info}) def incremental_update(self): """ @@ -559,10 +625,17 @@ def incremental_update(self): # Remove all existing dataplane rules for key in current_dataplane_rules: self.configdb.mod_entry(self.ACL_RULE, key, None) + # Program for per-asic namespace also if present + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_entry(self.ACL_RULE, key, None) + # Add all new dataplane rules for key in new_dataplane_rules: self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) + # Program for per-asic namespace corresponding to front asic also if present. + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) added_controlplane_rules = new_controlplane_rules.difference(current_controlplane_rules) removed_controlplane_rules = current_controlplane_rules.difference(new_controlplane_rules) @@ -570,14 +643,25 @@ def incremental_update(self): for key in added_controlplane_rules: self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) + # Program for per-asic namespace corresponding to front asic also if present. + # For control plane ACL it's not needed but to keep all db in sync program everywhere + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) for key in removed_controlplane_rules: self.configdb.mod_entry(self.ACL_RULE, key, None) + # Program for per-asic namespace corresponding to front asic also if present. + # For control plane ACL it's not needed but to keep all db in sync program everywhere + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.mod_entry(self.ACL_RULE, key, None) for key in existing_controlplane_rules: if cmp(self.rules_info[key], self.rules_db_info[key]) != 0: self.configdb.set_entry(self.ACL_RULE, key, self.rules_info[key]) - + # Program for per-asic namespace corresponding to front asic also if present. + # For control plane ACL it's not needed but to keep all db in sync program everywhere + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.set_entry(self.ACL_RULE, key, self.rules_info[key]) def delete(self, table=None, rule=None): """ @@ -589,8 +673,10 @@ def delete(self, table=None, rule=None): if not table or table == key[0]: if not rule or rule == key[1]: self.configdb.set_entry(self.ACL_RULE, key, None) - - + # Program for per-asic namespace corresponding to front asic also if present. + for namespace_configdb in self.per_npu_configdb.values(): + namespace_configdb.set_entry(self.ACL_RULE, key, None) + def show_table(self, table_name): """ Show ACL table configuration. @@ -626,7 +712,6 @@ def show_table(self, table_name): print(tabulate.tabulate(data, headers=header, tablefmt="simple", missingval="")) - def show_session(self, session_name): """ Show mirror session configuration. @@ -639,7 +724,8 @@ def show_session(self, session_name): for key, val in self.get_sessions_db_info().iteritems(): if session_name and key != session_name: continue - + # For multi-mpu platform status and monitor port will be dict() + # of 'asic-x':value data.append([key, val["status"], val["src_ip"], val["dst_ip"], val.get("gre_type", ""), val.get("dscp", ""), val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), diff --git a/config/main.py b/config/main.py index 3778145392..70a74d1d56 100755 --- a/config/main.py +++ b/config/main.py @@ -50,6 +50,12 @@ DEFAULT_NAMESPACE = '' +CFG_LOOPBACK_PREFIX = "Loopback" +CFG_LOOPBACK_PREFIX_LEN = len(CFG_LOOPBACK_PREFIX) +CFG_LOOPBACK_NAME_TOTAL_LEN_MAX = 11 +CFG_LOOPBACK_ID_MAX_VAL = 999 +CFG_LOOPBACK_NO="<0-999>" + # ========================== Syslog wrappers ========================== def log_debug(msg): @@ -265,7 +271,7 @@ def breakout_Ports(cm, delPorts=list(), addPorts=list(), portJson=dict(), \ # Execute action on list of systemd services def execute_systemctl(list_of_services, action): - num_asic = _get_num_asic() + num_asic = sonic_device_util.get_num_npus() generated_services_list, generated_multi_instance_services = _get_sonic_generated_services(num_asic) if ((generated_services_list == []) and (generated_multi_instance_services == [])): @@ -304,44 +310,12 @@ def run_command(command, display_cmd=False, ignore_error=False): if proc.returncode != 0 and not ignore_error: sys.exit(proc.returncode) -# API to check if this is a multi-asic device or not. -def is_multi_asic(): - num_asics = _get_num_asic() - - if num_asics > 1: - return True - else: - return False - -"""In case of Multi-Asic platform, Each ASIC will have a linux network namespace created. - So we loop through the databases in different namespaces and depending on the sub_role - decide whether this is a front end ASIC/namespace or a back end one. -""" -def get_all_namespaces(): - front_ns = [] - back_ns = [] - num_asics = _get_num_asic() - - if is_multi_asic(): - for asic in range(num_asics): - namespace = "{}{}".format(NAMESPACE_PREFIX, asic) - config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) - config_db.connect() - - metadata = config_db.get_table('DEVICE_METADATA') - if metadata['localhost']['sub_role'] == 'FrontEnd': - front_ns.append(namespace) - elif metadata['localhost']['sub_role'] == 'BackEnd': - back_ns.append(namespace) - - return {'front_ns': front_ns, 'back_ns': back_ns} - # Validate whether a given namespace name is valid in the device. def validate_namespace(namespace): - if not is_multi_asic(): + if not sonic_device_util.is_multi_npu(): return True - namespaces = get_all_namespaces() + namespaces = sonic_device_util.get_all_namespaces() if namespace in namespaces['front_ns'] + namespaces['back_ns']: return True else: @@ -531,88 +505,95 @@ def get_interface_naming_mode(): mode = "default" return mode -def _is_neighbor_ipaddress(ipaddress): +# Get the local BGP ASN from DEVICE_METADATA +def get_local_bgp_asn(config_db): + metadata = config_db.get_table('DEVICE_METADATA') + return metadata['localhost']['bgp_asn'] + +def _is_neighbor_ipaddress(config_db, ipaddress): """Returns True if a neighbor has the IP address , False if not """ - config_db = ConfigDBConnector() - config_db.connect() entry = config_db.get_entry('BGP_NEIGHBOR', ipaddress) return True if entry else False -def _get_all_neighbor_ipaddresses(): +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 BGP neighbor AS number is same as local BGP AS number, if so ignore that neigbor. """ - config_db = ConfigDBConnector() - config_db.connect() - return config_db.get_table('BGP_NEIGHBOR').keys() + addrs = [] + bgp_sessions = config_db.get_table('BGP_NEIGHBOR') + local_as = get_local_bgp_asn(config_db) + for addr, session in bgp_sessions.iteritems(): + if not ignore_local_hosts or (ignore_local_hosts and local_as != session['asn']): + addrs.append(addr) + return addrs -def _get_neighbor_ipaddress_list_by_hostname(hostname): +def _get_neighbor_ipaddress_list_by_hostname(config_db, hostname): """Returns list of strings, each containing an IP address of neighbor with hostname . Returns empty list if not a neighbor """ addrs = [] - config_db = ConfigDBConnector() - config_db.connect() bgp_sessions = config_db.get_table('BGP_NEIGHBOR') for addr, session in bgp_sessions.iteritems(): if session.has_key('name') and session['name'] == hostname: addrs.append(addr) return addrs -def _change_bgp_session_status_by_addr(ipaddress, status, verbose): +def _change_bgp_session_status_by_addr(config_db, ipaddress, status, verbose): """Start up or shut down BGP session by IP address """ verb = 'Starting' if status == 'up' else 'Shutting' click.echo("{} {} BGP session with neighbor {}...".format(verb, status, ipaddress)) - config_db = ConfigDBConnector() - config_db.connect() config_db.mod_entry('bgp_neighbor', ipaddress, {'admin_status': status}) -def _change_bgp_session_status(ipaddr_or_hostname, status, verbose): +def _change_bgp_session_status(config_db, ipaddr_or_hostname, status, verbose): """Start up or shut down BGP session by IP address or hostname """ ip_addrs = [] # If we were passed an IP address, convert it to lowercase because IPv6 addresses were # stored in ConfigDB with all lowercase alphabet characters during minigraph parsing - if _is_neighbor_ipaddress(ipaddr_or_hostname.lower()): + if _is_neighbor_ipaddress(config_db, ipaddr_or_hostname.lower()): ip_addrs.append(ipaddr_or_hostname.lower()) else: # If is not the IP address of a neighbor, check to see if it's a hostname - ip_addrs = _get_neighbor_ipaddress_list_by_hostname(ipaddr_or_hostname) + ip_addrs = _get_neighbor_ipaddress_list_by_hostname(config_db, ipaddr_or_hostname) if not ip_addrs: - click.get_current_context().fail("Could not locate neighbor '{}'".format(ipaddr_or_hostname)) + return False for ip_addr in ip_addrs: - _change_bgp_session_status_by_addr(ip_addr, status, verbose) + _change_bgp_session_status_by_addr(config_db, ip_addr, status, verbose) -def _validate_bgp_neighbor(neighbor_ip_or_hostname): + return True + +def _validate_bgp_neighbor(config_db, neighbor_ip_or_hostname): """validates whether the given ip or host name is a BGP neighbor """ ip_addrs = [] - if _is_neighbor_ipaddress(neighbor_ip_or_hostname.lower()): + if _is_neighbor_ipaddress(config_db, neighbor_ip_or_hostname.lower()): ip_addrs.append(neighbor_ip_or_hostname.lower()) else: - ip_addrs = _get_neighbor_ipaddress_list_by_hostname(neighbor_ip_or_hostname.upper()) - - if not ip_addrs: - click.get_current_context().fail("Could not locate neighbor '{}'".format(neighbor_ip_or_hostname)) + ip_addrs = _get_neighbor_ipaddress_list_by_hostname(config_db, neighbor_ip_or_hostname.upper()) return ip_addrs -def _remove_bgp_neighbor_config(neighbor_ip_or_hostname): +def _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname): """Removes BGP configuration of the given neighbor """ - ip_addrs = _validate_bgp_neighbor(neighbor_ip_or_hostname) - config_db = ConfigDBConnector() - config_db.connect() + ip_addrs = _validate_bgp_neighbor(config_db, neighbor_ip_or_hostname) + + if not ip_addrs: + return False for ip_addr in ip_addrs: config_db.mod_entry('bgp_neighbor', ip_addr, None) click.echo("Removed configuration of BGP neighbor {}".format(ip_addr)) + return True + def _change_hostname(hostname): current_hostname = os.uname()[1] if current_hostname != hostname: @@ -642,32 +623,6 @@ def _clear_qos(): for qos_table in QOS_TABLE_NAMES: config_db.delete_table(qos_table) -def _get_hwsku(): - config_db = ConfigDBConnector() - config_db.connect() - metadata = config_db.get_table('DEVICE_METADATA') - return metadata['localhost']['hwsku'] - -def _get_platform(): - with open('/host/machine.conf') as machine_conf: - for line in machine_conf: - tokens = line.split('=') - if tokens[0].strip() == 'onie_platform' or tokens[0].strip() == 'aboot_platform': - return tokens[1].strip() - return '' - -def _get_num_asic(): - platform = _get_platform() - num_asic = 1 - asic_conf_file = os.path.join('/usr/share/sonic/device/', platform, ASIC_CONF_FILENAME) - if os.path.isfile(asic_conf_file): - with open(asic_conf_file) as conf_file: - for line in conf_file: - line_info = line.split('=') - if line_info[0].lower() == "num_asic": - num_asic = int(line_info[1]) - return num_asic - def _get_sonic_generated_services(num_asic): if not os.path.isfile(SONIC_GENERATED_SERVICE_PATH): return None @@ -792,11 +747,11 @@ def save(filename): """Export current config DB to a file on disk.\n : Names of configuration file(s) to save, separated by comma with no spaces in between """ - num_asic = _get_num_asic() + num_asic = sonic_device_util.get_num_npus() cfg_files = [] num_cfg_file = 1 - if is_multi_asic(): + if sonic_device_util.is_multi_npu(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -849,11 +804,11 @@ def load(filename, yes): if not yes: click.confirm(message, abort=True) - num_asic = _get_num_asic() + num_asic = sonic_device_util.get_num_npus() cfg_files = [] num_cfg_file = 1 - if is_multi_asic(): + if sonic_device_util.is_multi_npu(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -900,8 +855,9 @@ def load(filename, yes): @config.command() @click.option('-y', '--yes', is_flag=True) @click.option('-l', '--load-sysinfo', is_flag=True, help='load system default information (mac, portmap etc) first.') +@click.option('-n', '--no_service_restart', default=False, is_flag=True, help='Do not restart docker services') @click.argument('filename', required=False) -def reload(filename, yes, load_sysinfo): +def reload(filename, yes, load_sysinfo, no_service_restart): """Clear current configuration and import a previous saved config DB dump file. : Names of configuration file(s) to load, separated by comma with no spaces in between """ @@ -915,11 +871,11 @@ def reload(filename, yes, load_sysinfo): log_info("'reload' executing...") - num_asic = _get_num_asic() + num_asic = sonic_device_util.get_num_npus() cfg_files = [] num_cfg_file = 1 - if is_multi_asic(): + if sonic_device_util.is_multi_npu(): num_cfg_file += num_asic # If the user give the filename[s], extract the file names. @@ -941,8 +897,9 @@ def reload(filename, yes, load_sysinfo): cfg_hwsku = cfg_hwsku.strip() #Stop services before config push - log_info("'reload' stopping services...") - _stop_services() + if not no_service_restart: + log_info("'reload' stopping services...") + _stop_services() """ In Single AISC platforms we have single DB service. In multi-ASIC platforms we have a global DB service running in the host + DB services running in each ASIC namespace created per ASIC. @@ -1014,9 +971,10 @@ def reload(filename, yes, load_sysinfo): # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them - _reset_failed_services() - log_info("'reload' restarting services...") - _restart_services() + if not no_service_restart: + _reset_failed_services() + log_info("'reload' restarting services...") + _restart_services() @config.command("load_mgmt_config") @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, @@ -1046,7 +1004,8 @@ def load_mgmt_config(filename): @config.command("load_minigraph") @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, expose_value=False, prompt='Reload config from minigraph?') -def load_minigraph(): +@click.option('-n', '--no_service_restart', default=False, is_flag=True, help='Do not restart docker services') +def load_minigraph(no_service_restart): """Reconfigure based on minigraph.""" log_info("'load_minigraph' executing...") @@ -1061,8 +1020,9 @@ def load_minigraph(): device_type = device_type.strip() #Stop services before config push - log_info("'load_minigraph' stopping services...") - _stop_services() + if not no_service_restart: + log_info("'load_minigraph' stopping services...") + _stop_services() # For Single Asic platform the namespace list has the empty string # for mulit Asic platform the empty string to generate the config @@ -1107,10 +1067,11 @@ def load_minigraph(): # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them - _reset_failed_services() - #FIXME: After config DB daemon is implemented, we'll no longer need to restart every service. - log_info("'load_minigraph' restarting services...") - _restart_services() + if not no_service_restart: + _reset_failed_services() + #FIXME: After config DB daemon is implemented, we'll no longer need to restart every service. + log_info("'load_minigraph' restarting services...") + _restart_services() click.echo("Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`.") @@ -1213,9 +1174,6 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): """ Add mirror session """ - config_db = ConfigDBConnector() - config_db.connect() - session_info = { "src_ip": src_ip, "dst_ip": dst_ip, @@ -1231,8 +1189,21 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): if queue is not None: session_info['queue'] = queue - - config_db.set_entry("MIRROR_SESSION", session_name, session_info) + + """ + For multi-npu platforms we need to program all front asic namespaces + """ + namespaces = sonic_device_util.get_all_namespaces() + if not namespaces['front_ns']: + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry("MIRROR_SESSION", session_name, session_info) + else: + per_npu_configdb = {} + for front_asic_namespaces in namespaces['front_ns']: + per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) + per_npu_configdb[front_asic_namespaces].connect() + per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) @mirror_session.command() @click.argument('session_name', metavar='', required=True) @@ -1240,10 +1211,21 @@ def remove(session_name): """ Delete mirror session """ - config_db = ConfigDBConnector() - config_db.connect() - config_db.set_entry("MIRROR_SESSION", session_name, None) + """ + For multi-npu platforms we need to program all front asic namespaces + """ + namespaces = sonic_device_util.get_all_namespaces() + if not namespaces['front_ns']: + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry("MIRROR_SESSION", session_name, None) + else: + per_npu_configdb = {} + for front_asic_namespaces in namespaces['front_ns']: + per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) + per_npu_configdb[front_asic_namespaces].connect() + per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, None) # # 'pfcwd' group ('config pfcwd ...') # @@ -1350,8 +1332,8 @@ def reload(): """Reload QoS configuration""" log_info("'qos reload' executing...") _clear_qos() - platform = _get_platform() - hwsku = _get_hwsku() + platform = sonic_device_util.get_platform() + hwsku = sonic_device_util.get_hwsku() buffer_template_file = os.path.join('/usr/share/sonic/device/', platform, hwsku, 'buffers.json.j2') if os.path.isfile(buffer_template_file): command = "{} -d -t {} >/tmp/buffers.json".format(SONIC_CFGGEN_PATH, buffer_template_file) @@ -1838,20 +1820,53 @@ def num_dumps(kdump_num_dumps): @shutdown.command() @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def all(verbose): - """Shut down all BGP sessions""" + """Shut down all BGP sessions + In the case of Multi-Asic platform, we shut only the EBGP sessions with external neighbors. + """ log_info("'bgp shutdown all' executing...") - bgp_neighbor_ip_list = _get_all_neighbor_ipaddresses() - for ipaddress in bgp_neighbor_ip_list: - _change_bgp_session_status_by_addr(ipaddress, 'down', verbose) + namespaces = [DEFAULT_NAMESPACE] + ignore_local_hosts = False + + if sonic_device_util.is_multi_npu(): + ns_list = sonic_device_util.get_all_namespaces() + namespaces = ns_list['front_ns'] + ignore_local_hosts = True + + # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the + # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) + for namespace in namespaces: + 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: + _change_bgp_session_status_by_addr(config_db, ipaddress, 'down', verbose) # 'neighbor' subcommand @shutdown.command() @click.argument('ipaddr_or_hostname', metavar='', required=True) @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def neighbor(ipaddr_or_hostname, verbose): - """Shut down BGP session by neighbor IP address or hostname""" + """Shut down BGP session by neighbor IP address or hostname. + User can specify either internal or external BGP neighbor to shutdown + """ log_info("'bgp shutdown neighbor {}' executing...".format(ipaddr_or_hostname)) - _change_bgp_session_status(ipaddr_or_hostname, 'down', verbose) + namespaces = [DEFAULT_NAMESPACE] + found_neighbor = False + + if sonic_device_util.is_multi_npu(): + ns_list = sonic_device_util.get_all_namespaces() + namespaces = ns_list['front_ns'] + ns_list['back_ns'] + + # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the + # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) + for namespace in namespaces: + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() + if _change_bgp_session_status(config_db, ipaddr_or_hostname, 'down', verbose): + found_neighbor = True + + if not found_neighbor: + click.get_current_context().fail("Could not locate neighbor '{}'".format(ipaddr_or_hostname)) @bgp.group(cls=AbbreviationGroup) def startup(): @@ -1862,20 +1877,53 @@ def startup(): @startup.command() @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def all(verbose): - """Start up all BGP sessions""" + """Start up all BGP sessions + In the case of Multi-Asic platform, we startup only the EBGP sessions with external neighbors. + """ log_info("'bgp startup all' executing...") - bgp_neighbor_ip_list = _get_all_neighbor_ipaddresses() - for ipaddress in bgp_neighbor_ip_list: - _change_bgp_session_status(ipaddress, 'up', verbose) + namespaces = [DEFAULT_NAMESPACE] + ignore_local_hosts = False + + if sonic_device_util.is_multi_npu(): + ns_list = sonic_device_util.get_all_namespaces() + namespaces = ns_list['front_ns'] + ignore_local_hosts = True + + # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the + # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) + for namespace in namespaces: + 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: + _change_bgp_session_status_by_addr(config_db, ipaddress, 'up', verbose) # 'neighbor' subcommand @startup.command() @click.argument('ipaddr_or_hostname', metavar='', required=True) @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def neighbor(ipaddr_or_hostname, verbose): - """Start up BGP session by neighbor IP address or hostname""" log_info("'bgp startup neighbor {}' executing...".format(ipaddr_or_hostname)) - _change_bgp_session_status(ipaddr_or_hostname, 'up', verbose) + """Start up BGP session by neighbor IP address or hostname. + User can specify either internal or external BGP neighbor to startup + """ + namespaces = [DEFAULT_NAMESPACE] + found_neighbor = False + + if sonic_device_util.is_multi_npu(): + ns_list = sonic_device_util.get_all_namespaces() + namespaces = ns_list['front_ns'] + ns_list['back_ns'] + + # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the + # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) + for namespace in namespaces: + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() + if _change_bgp_session_status(config_db, ipaddr_or_hostname, 'up', verbose): + found_neighbor = True + + if not found_neighbor: + click.get_current_context().fail("Could not locate neighbor '{}'".format(ipaddr_or_hostname)) # # 'remove' subgroup ('config bgp remove ...') @@ -1889,8 +1937,26 @@ def remove(): @remove.command('neighbor') @click.argument('neighbor_ip_or_hostname', metavar='', required=True) def remove_neighbor(neighbor_ip_or_hostname): - """Deletes BGP neighbor configuration of given hostname or ip from devices""" - _remove_bgp_neighbor_config(neighbor_ip_or_hostname) + """Deletes BGP neighbor configuration of given hostname or ip from devices + User can specify either internal or external BGP neighbor to remove + """ + namespaces = [DEFAULT_NAMESPACE] + removed_neighbor = False + + if sonic_device_util.is_multi_npu(): + ns_list = sonic_device_util.get_all_namespaces() + namespaces = ns_list['front_ns'] + ns_list['back_ns'] + + # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the + # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) + for namespace in namespaces: + config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + config_db.connect() + if _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname): + removed_neighbor = True + + if not removed_neighbor: + click.get_current_context().fail("Could not locate neighbor '{}'".format(neighbor_ip_or_hostname)) # # 'interface' group ('config interface ...') @@ -2916,6 +2982,71 @@ def naming_mode_alias(): """Set CLI interface naming mode to ALIAS (Vendor port alias)""" set_interface_naming_mode('alias') +@config.group() +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 + int(loopback_name[CFG_LOOPBACK_PREFIX_LEN:]) > CFG_LOOPBACK_ID_MAX_VAL) : + return False + if len(loopback_name) > CFG_LOOPBACK_NAME_TOTAL_LEN_MAX: + return False + return True + +# +# 'loopback' group ('config loopback ...') +# +@config.group() +@click.pass_context +@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') +def loopback(ctx, redis_unix_socket_path): + """Loopback-related configuration tasks""" + kwargs = {} + if redis_unix_socket_path: + kwargs['unix_socket_path'] = redis_unix_socket_path + config_db = ConfigDBConnector(**kwargs) + config_db.connect(wait_for_init=False) + ctx.obj = {'db': config_db} + +@loopback.command('add') +@click.argument('loopback_name', metavar='', required=True) +@click.pass_context +def add_loopback(ctx, loopback_name): + config_db = ctx.obj['db'] + if is_loopback_name_valid(loopback_name) is False: + ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " + .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) + + lo_intfs = [k for k,v in config_db.get_table('LOOPBACK_INTERFACE').iteritems() if type(k) != tuple] + if loopback_name in lo_intfs: + ctx.fail("{} already exists".format(loopback_name)) + + config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, {"NULL" : "NULL"}) + +@loopback.command('del') +@click.argument('loopback_name', metavar='', required=True) +@click.pass_context +def del_loopback(ctx, loopback_name): + config_db = ctx.obj['db'] + if is_loopback_name_valid(loopback_name) is False: + ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " + .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) + + lo_config_db = config_db.get_table('LOOPBACK_INTERFACE') + lo_intfs = [k for k,v in lo_config_db.iteritems() if type(k) != tuple] + if loopback_name not in lo_intfs: + ctx.fail("{} does not exists".format(loopback_name)) + + ips = [ k[1] for k in lo_config_db if type(k) == tuple and k[0] == loopback_name ] + for ip in ips: + config_db.set_entry('LOOPBACK_INTERFACE', (loopback_name, ip), None) + + config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, None) + + @config.group(cls=AbbreviationGroup) def ztp(): """ Configure Zero Touch Provisioning """ diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 384c1f357e..a6598b717e 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -49,6 +49,9 @@ * [Interface Naming Mode](#interface-naming-mode) * [Interface naming mode show commands](#interface-naming-mode-show-commands) * [Interface naming mode config commands](#interface-naming-mode-config-commands) + * [Interface Vrf binding](#interface-vrf-binding) + * [Interface vrf bind & unbind config commands](#interface-vrf-bind-&-unbind-config-commands) + * [Interface vrf binding show commands](#interface-vrf-binding-show-commands) * [IP / IPv6](#ip--ipv6) * [IP show commands](#ip-show-commands) * [IPv6 show commands](#ipv6-show-commands) @@ -60,6 +63,11 @@ * [Reloading Configuration](#reloading-configuration) * [Loading Management Configuration](#loading-management-configuration) * [Saving Configuration to a File for Persistence](saving-configuration-to-a-file-for-persistence) + * [Loopback Interfaces](#loopback-interfaces) + * [Loopback config commands](#loopback-config-commands) +* [VRF Configuration](#vrf-configuration) + * [VRF show commands](#vrf-show-commands) + * [VRF config commands](#vrf-config-commands) * [Management VRF](#Management-VRF) * [Management VRF Show commands](#management-vrf-show-commands) * [Management VRF Config commands](#management-vrf-config-commands) @@ -271,15 +279,18 @@ This command lists all the possible configuration commands at the top level. load Import a previous saved config DB dump file. load_mgmt_config Reconfigure hostname and mgmt interface based... load_minigraph Reconfigure based on minigraph. + loopback Loopback-related configuration tasks. mirror_session nat NAT-related configuration tasks platform Platform-related configuration tasks portchannel qos reload Clear current configuration and import a... + route route-related configuration tasks save Export current config DB to a file on disk. tacacs TACACS+ server configuration vlan VLAN-related configuration tasks + vrf VRF-related configuration tasks warm_restart warm_restart-related configuration tasks watermark Configure watermark container Modify configuration of containers @@ -342,6 +353,7 @@ This command displays the full list of show commands available in the software; users Show users version Show version information vlan Show VLAN information + vrf Show vrf config warm_restart Show warm restart configuration and state watermark Show details of watermark container Show details of container @@ -2440,7 +2452,6 @@ This command displays the key fields of the interfaces such as Operational Statu Ethernet4 down up hundredGigE1/2 T0-2:hundredGigE1/30 ``` - **show interfaces naming_mode** Refer sub-section [Interface-Naming-Mode](#Interface-Naming-Mode) @@ -2544,7 +2555,7 @@ NOTE: In older versions of SONiC until 201811 release, the command syntax was `c **config interface ip add (Versions <= 201811)** This command is used for adding the IP address for an interface. -IP address for either physical interface or for portchannel or for VLAN interface can be configured using this command. +IP address for either physical interface or for portchannel or for VLAN interface or for Loopback interface can be configured using this command. While configuring the IP address for the management interface "eth0", users can provide the default gateway IP address as an optional parameter from release 201911. @@ -2900,6 +2911,35 @@ The user must log out and log back in for changes to take effect. Note that the Go Back To [Beginning of the document](#) or [Beginning of this section](#interface-naming-mode) +## Interface Vrf binding + +### Interface vrf bind & unbind config commands + +**config interface vrf bind** + +This command is used to bind a interface to a vrf. +By default, all L3 interfaces will be in default vrf. Above vrf bind command will let users bind interface to a vrf. + +- Usage: + ``` + config interface vrf bind + ``` + +**config interface vrf unbind** + +This command is used to ubind a interface from a vrf. +This will move the interface to default vrf. + +- Usage: + ``` + config interface vrf unbind + ``` + + ### Interface vrf binding show commands + + To display interface vrf binding information, user can use show vrf command. Please refer sub-section [Vrf-show-command](#vrf-show-commands). + +Go Back To [Beginning of the document](#) or [Beginning of this section](#interface-vrf-binding) ## IP / IPv6 @@ -2918,7 +2958,7 @@ This command displays either all the route entries from the routing table or a s - Usage: ``` - show ip route [] + show ip route [] [] ``` - Example: @@ -2929,12 +2969,9 @@ This command displays either all the route entries from the routing table or a s > - selected route, * - FIB route S>* 0.0.0.0/0 [200/0] via 10.11.162.254, eth0 C>* 1.1.0.0/16 is directly connected, Vlan100 - C>* 10.1.0.1/32 is directly connected, lo - C>* 10.1.0.32/32 is directly connected, lo C>* 10.1.1.0/31 is directly connected, Ethernet112 C>* 10.1.1.2/31 is directly connected, Ethernet116 C>* 10.11.162.0/24 is directly connected, eth0 - C>* 10.12.0.102/32 is directly connected, lo C>* 127.0.0.0/8 is directly connected, lo C>* 240.127.1.0/24 is directly connected, docker0 ``` @@ -2949,6 +2986,27 @@ This command displays either all the route entries from the routing table or a s * directly connected, Ethernet112 ``` + - Vrf-name can also be specified to get IPv4 routes programmed in the vrf. + + - Example: + ``` + admin@sonic:~$ show ip route vrf Vrf-red + Codes: K - kernel route, C - connected, S - static, R - RIP, + O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP, + T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP, + F - PBR, f - OpenFabric, + > - selected route, * - FIB route + VRF Vrf-red: + C>* 11.1.1.1/32 is directly connected, Loopback11, 21:50:47 + C>* 100.1.1.0/24 is directly connected, Vlan100, 03w1d06h + + admin@sonic:~$ show ip route vrf Vrf-red 11.1.1.1/32 + Routing entry for 11.1.1.1/32 + Known via "connected", distance 0, metric 0, vrf Vrf-red, best + Last update 21:57:53 ago + * directly connected, Loopback11 + ``` + **show ip interfaces** This command displays the details about all the Layer3 IP interfaces in the device for which IP address has been assigned. @@ -2968,16 +3026,20 @@ The type of interfaces include the following. - Example: ``` admin@sonic:~$ show ip interfaces - Interface IPv4 address/mask Admin/Oper BGP Neighbor Neighbor IP - ------------- ------------------- ------------ -------------- ------------- - PortChannel01 10.0.0.56/31 up/down DEVICE1 10.0.0.57 - PortChannel02 10.0.0.58/31 up/down DEVICE2 10.0.0.59 - PortChannel03 10.0.0.60/31 up/down DEVICE3 10.0.0.61 - PortChannel04 10.0.0.62/31 up/down DEVICE4 10.0.0.63 - Vlan1000 192.168.0.1/27 up/up N/A N/A - docker0 240.127.1.1/24 up/down N/A N/A - eth0 10.3.147.252/23 up/up N/A N/A - lo 127.0.0.1/8 up/up N/A N/A + Interface Master IPv4 address/mask Admin/Oper BGP Neighbor Neighbor IP Flags + ------------- ------------ ------------------ -------------- ------------- ------------- ------- + Loopback0 1.0.0.1/32 up/up N/A N/A + Loopback11 Vrf-red 11.1.1.1/32 up/up N/A N/A + Loopback100 Vrf-blue 100.0.0.1/32 up/up N/A N/A + PortChannel01 10.0.0.56/31 up/down DEVICE1 10.0.0.57 + PortChannel02 10.0.0.58/31 up/down DEVICE2 10.0.0.59 + PortChannel03 10.0.0.60/31 up/down DEVICE3 10.0.0.61 + PortChannel04 10.0.0.62/31 up/down DEVICE4 10.0.0.63 + Vlan100 Vrf-red 1001.1.1/24 up/up N/A N/A + Vlan1000 192.168.0.1/27 up/up N/A N/A + docker0 240.127.1.1/24 up/down N/A N/A + eth0 10.3.147.252/23 up/up N/A N/A + lo 127.0.0.1/8 up/up N/A N/A ``` **show ip protocol** @@ -3026,7 +3088,7 @@ This command displays either all the IPv6 route entries from the routing table o - Usage: ``` - show ipv6 route [] + show ipv6 route [] [] ``` - Example: @@ -3060,6 +3122,29 @@ This command displays either all the IPv6 route entries from the routing table o * directly connected, lo ``` + Vrf-name can also be specified to get IPv6 routes programmed in the vrf. + + - Example: + ``` + admin@sonic:~$ show ipv6 route vrf Vrf-red + Codes: K - kernel route, C - connected, S - static, R - RIP, + O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP, + T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP, + F - PBR, f - OpenFabric, + > - selected route, * - FIB route + VRF Vrf-red: + C>* 1100::1/128 is directly connected, Loopback11, 21:50:47 + C>* 100::/112 is directly connected, Vlan100, 03w1d06h + C>* fe80::/64 is directly connected, Loopback11, 21:50:47 + C>* fe80::/64 is directly connected, Vlan100, 03w1d06h + + admin@sonic:~$ show ipv6 route vrf Vrf-red 1100::1/128 + Routing entry for 1100::1/128 + Known via "connected", distance 0, metric 0, vrf Vrf-red, best + Last update 21:57:53 ago + * directly connected, Loopback11 + ``` + **show ipv6 interfaces** This command displays the details about all the Layer3 IPv6 interfaces in the device for which IPv6 address has been assigned. @@ -3078,16 +3163,18 @@ The type of interfaces include the following. - Example: ``` admin@sonic:~$ show ipv6 interfaces - Interface IPv6 address/mask Admin/Oper BGP Neighbor Neighbor IP - ------------- ---------------------------------------- ------------ -------------- ------------- - Bridge fe80::7c45:1dff:fe08:cdd%Bridge/64 up/up N/A N/A - PortChannel01 fc00::71/126 up/down DEVICE1 fc00::72 - PortChannel02 fc00::75/126 up/down DEVICE2 fc00::76 - PortChannel03 fc00::79/126 up/down DEVICE3 fc00::7a - PortChannel04 fc00::7d/126 up/down DEVICE4 fc00::7e - Vlan100 fe80::eef4:bbff:fefe:880a%Vlan100/64 up/up N/A N/A - eth0 fe80::eef4:bbff:fefe:880a%eth0/64 up/up N/A N/A - lo fc00:1::32/128 up/up N/A N/A + Interface Master IPv6 address/mask Admin/Oper BGP Neighbor Neighbor IP + ----------- -------- ---------------------------------------- ------------ -------------- ------------- + Bridge fe80::7c45:1dff:fe08:cdd%Bridge/64 up/up N/A N/A + Loopback11 Vrf-red 1100::1/128 up/up + PortChannel01 fc00::71/126 up/down DEVICE1 fc00::72 + PortChannel02 fc00::75/126 up/down DEVICE2 fc00::76 + PortChannel03 fc00::79/126 up/down DEVICE3 fc00::7a + PortChannel04 fc00::7d/126 up/down DEVICE4 fc00::7e + Vlan100 Vrf-red 100::1/112 up/up N/A N/A + fe80::eef4:bbff:fefe:880a%Vlan100/64 + eth0 fe80::eef4:bbff:fefe:880a%eth0/64 up/up N/A N/A + lo fc00:1::32/128 up/up N/A N/A ``` **show ipv6 protocol** @@ -3269,9 +3356,13 @@ NOTE: Management interface IP address and default route (or specific route) may When user specifies the optional argument "-y" or "--yes", this command forces the loading without prompting the user for confirmation. If the argument is not specified, it prompts the user to confirm whether user really wants to load this configuration file. +When user specifies the optional argument "-n" or "--no-service-restart", this command loads the configuration without restarting dependent services +running on the device. One use case for this option is during boot time when config-setup service loads minigraph configuration and there is no services +running on the device. + - Usage: ``` - config load_minigraph [-y|--yes] + config load_minigraph [-y|--yes] [-n|--no-service-restart] ``` - Example: @@ -3304,9 +3395,13 @@ NOTE: Management interface IP address and default route (or specific route) may When user specifies the optional argument "-y" or "--yes", this command forces the loading without prompting the user for confirmation. If the argument is not specified, it prompts the user to confirm whether user really wants to load this configuration file. +When user specifies the optional argument "-n" or "--no-service-restart", this command clear and loads the configuration without restarting dependent services +running on the device. One use case for this option is during boot time when config-setup service loads existing old configuration and there is no services +running on the device. + - Usage: ``` - config reload [-y|--yes] [-l|--load-sysinfo] [] + config reload [-y|--yes] [-l|--load-sysinfo] [] [-n|--no-service-restart] ``` - Example: @@ -3378,6 +3473,73 @@ Saved file can be transferred to remote machines for debugging. If users wants t Go Back To [Beginning of the document](#) or [Beginning of this section](#loading-reloading-and-saving-configuration) +## Loopback Interfaces + +### Loopback Config commands + +This sub-section explains how to create and delete loopback interfaces. + +**config interface loopback** + +This command is used to add or delete loopback interfaces. +It is recommended to use loopback names in the format "Loopbackxxx", where "xxx" is number of 1 to 3 digits. Ex: "Loopback11". + +- Usage: + ``` + config loopback (add | del) + ``` + +- Example (Create the loopback with name "Loopback11"): + ``` + admin@sonic:~$ sudo config loopback add Loopback11 + ``` + +## VRF Configuration + +### VRF show commands + +**show vrf** + +This command displays all vrfs configured on the system along with interface binding to the vrf. +If vrf-name is also provided as part of the command, if the vrf is created it will display all interfaces binding to the vrf, if vrf is not created nothing will be displayed. + +- Usage: + ``` + show vrf [] + ``` + +- Example: + ```` + admin@sonic:~$ show vrf + VRF Interfaces + ------- ------------ + default Vlan20 + Vrf-red Vlan100 + Loopback11 + Vrf-blue Loopback100 + Loopback102 + ```` + +### VRF config commands + +**config vrf add ** + +This command creates vrf in SONiC system with provided vrf-name. + +- Usage: + ``` +config vrf add +``` +Note: vrf-name should always start with keyword "Vrf" + +**config vrf del ** + +This command deletes vrf with name vrf-name. + +- Usage: + ``` +config vrf del +``` ## Management VRF diff --git a/show/main.py b/show/main.py index 363895fce1..de57cf3229 100755 --- a/show/main.py +++ b/show/main.py @@ -636,16 +636,19 @@ def is_mgmt_vrf_enabled(ctx): cmd = 'sonic-cfggen -d --var-json "MGMT_VRF_CONFIG"' p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout = p.communicate()[0] - if p.returncode == 0: - mvrf_dict = json.loads(stdout) - - # if the mgmtVrfEnabled attribute is configured, check the value - # and return True accordingly. - if 'mgmtVrfEnabled' in mvrf_dict['vrf_global']: - if (mvrf_dict['vrf_global']['mgmtVrfEnabled'] == "true"): - #ManagementVRF is enabled. Return True. - return True + try : + mvrf_dict = json.loads(p.stdout.read()) + except ValueError: + print("MGMT_VRF_CONFIG is not present.") + return False + + # if the mgmtVrfEnabled attribute is configured, check the value + # and return True accordingly. + if 'mgmtVrfEnabled' in mvrf_dict['vrf_global']: + if (mvrf_dict['vrf_global']['mgmtVrfEnabled'] == "true"): + #ManagementVRF is enabled. Return True. + return True + return False #