diff --git a/CLI/actioner/show_config_bfd.py b/CLI/actioner/show_config_bfd.py
index 7366a200ad..33aa43ac4b 100644
--- a/CLI/actioner/show_config_bfd.py
+++ b/CLI/actioner/show_config_bfd.py
@@ -374,6 +374,11 @@ def show_bfd_config(render_tables):
cmd_str += "bfd" + cmd_end
+ for profile_rec in profile_record_list:
+ cmd_str += bfd_generate_profile_config(profile_rec)
+ cmd_str += " !"
+ cmd_str += cmd_end
+
for shop_rec in shop_record_list:
cmd_str += bfd_generate_shop_config(shop_rec)
cmd_str += " !"
@@ -384,11 +389,6 @@ def show_bfd_config(render_tables):
cmd_str += " !"
cmd_str += cmd_end
- for profile_rec in profile_record_list:
- cmd_str += bfd_generate_profile_config(profile_rec)
- cmd_str += " !"
- cmd_str += cmd_end
-
# cmd_str += cmd_end
return "CB_SUCCESS", cmd_str
diff --git a/CLI/actioner/show_config_data.py b/CLI/actioner/show_config_data.py
index c3eb5319f5..31145f3562 100644
--- a/CLI/actioner/show_config_data.py
+++ b/CLI/actioner/show_config_data.py
@@ -146,7 +146,6 @@
"configure-mirror",
"configure-ipsla",
"configure-bfd",
- "configure-bfd-profile",
"renderCfg_ntp",
"renderCfg_ipdns",
"renderCfg_tacacs",
diff --git a/CLI/actioner/show_config_interface.py b/CLI/actioner/show_config_interface.py
index 58c2cd4793..b8b4b602d5 100644
--- a/CLI/actioner/show_config_interface.py
+++ b/CLI/actioner/show_config_interface.py
@@ -20,6 +20,7 @@
from itertools import groupby
from operator import itemgetter
+from sonic_cli_if_autoneg import getPortValidSpeeds
def show_if_channel_group_cmd(render_tables):
cmd_str = ""
@@ -279,13 +280,21 @@ def show_if_autoneg(render_tables):
continue
if port.get("autoneg") != "on":
continue
- if "adv_speeds" in port:
- if port["adv_speeds"] in [None, '', 'all', 'none']:
+ adv_speeds = port.get('adv_speeds')
+ if adv_speeds in [None, '', 'all', 'none']:
+ cmd_str = "speed auto"
+ else:
+ valid_speeds = getPortValidSpeeds(ifname_key)
+ admin_speeds = adv_speeds.split(',')
+ all = True
+ for spd in valid_speeds:
+ if spd not in admin_speeds:
+ all = False
+ break
+ if all:
cmd_str = "speed auto"
else:
- cmd_str = "speed auto {}".format(port["adv_speeds"])
- else:
- cmd_str = "speed auto"
+ cmd_str = "speed auto {}".format(adv_speeds)
return "CB_SUCCESS", cmd_str
diff --git a/CLI/actioner/show_config_ospfv2.py b/CLI/actioner/show_config_ospfv2.py
index 14a2f9ee8d..408ea3946b 100644
--- a/CLI/actioner/show_config_ospfv2.py
+++ b/CLI/actioner/show_config_ospfv2.py
@@ -68,8 +68,8 @@ def show_router_ospf_config(render_tables):
# ospf_debug_log_disable()
# ospf_debug_log_enable()
- ospf_debug_print(
- "show_router_ospf_config: render_tables {}".format(render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_config: render_tables {}".format(render_tables))
present, _, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -127,6 +127,14 @@ def show_router_ospf_config(render_tables):
match_value=match_value,
)
+ match_value = {True: ""}
+ cmd_str += ospf_generate_command(
+ tbl_rec,
+ "opaque-lsa-capability",
+ " capability opaque",
+ match_value=match_value,
+ )
+
cmd_str += ospf_generate_command(tbl_rec, "default-metric",
" default-metric")
@@ -240,6 +248,41 @@ def show_router_ospf_config(render_tables):
cmd_str += ospf_generate_command(tbl_rec, "write-multiplier",
" write-multiplier")
+ gr_p, _, gr_v = ospf_get_key_value(
+ tbl_rec, "gr-enabled")
+ grgp_p, _, grgp_v = ospf_get_key_value(
+ tbl_rec, "gr-grace-period")
+ if gr_p == True:
+ cmd_str += " graceful-restart"
+ if grgp_p == True:
+ cmd_str += " grace-period {}".format(grgp_v)
+ cmd_str += cmd_end
+
+ match_value = {True: ""}
+ cmd_str += ospf_generate_command(tbl_rec, "gr-helper-only",
+ " graceful-restart helper enable",
+ match_value=match_value)
+
+ grhelperlsa_p, _, grhelperlsa_v = ospf_get_key_value(
+ tbl_rec, "gr-helper-strict-lsa-checking")
+ if grhelperlsa_p == True and grhelperlsa_v == False:
+ match_value = {False: ""}
+ cmd_str += ospf_generate_command(tbl_rec, "gr-helper-strict-lsa-checking",
+ " no graceful-restart helper strict-lsa-checking",
+ match_value=match_value)
+
+ match_value = {True: ""}
+ cmd_str += ospf_generate_command(tbl_rec, "gr-helper-planned-only",
+ " graceful-restart helper planned-only",
+ match_value=match_value)
+
+ cmd_str += ospf_generate_command(tbl_rec, "gr-helper-supported-grace-time",
+ " graceful-restart helper supported-grace-time")
+
+ status, gr_nbr_cmd = show_router_ospf_gr_helper_neighbor_config(
+ render_tables)
+ if status == "CB_SUCCESS":
+ cmd_str += gr_nbr_cmd
status, sub_cmd_str = show_router_ospf_distribute_route_config(
render_tables)
@@ -256,9 +299,9 @@ def show_router_ospf_passive_interface_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_passive_interface_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_passive_interface_config: render_tables {}".format(
+ # render_tables))
present, _, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -326,8 +369,8 @@ def show_router_ospf_area_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_area_config: render_tables {}".format(render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_area_config: render_tables {}".format(render_tables))
present, _, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -444,9 +487,9 @@ def show_router_ospf_area_network_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_area_network_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_area_network_config: render_tables {}".format(
+ # render_tables))
present, _, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -508,9 +551,9 @@ def show_router_ospf_area_vlink_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_area_vlink_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_area_vlink_config: render_tables {}".format(
+ # render_tables))
present, _, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -635,9 +678,9 @@ def show_router_ospf_area_vlmd_auth_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_area_vlmd_auth_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_area_vlmd_auth_config: render_tables {}".format(
+ # render_tables))
present, name, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -719,9 +762,9 @@ def show_router_ospf_area_addr_range_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_area_addr_range_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_area_addr_range_config: render_tables {}".format(
+ # render_tables))
present, name, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -807,6 +850,67 @@ def show_router_ospf_area_addr_range_config(render_tables):
"show_router_ospf_area_addr_range_config: cmd_str {}".format(cmd_str))
return "CB_SUCCESS", cmd_str
+def show_router_ospf_gr_helper_neighbor_config(render_tables):
+ cmd_str = ""
+ cmd_end = " ;"
+
+ #ospf_debug_log_enable()
+ #ospf_debug_log_disable()
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: render_tables {}".format(
+ render_tables))
+
+ present, name, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
+ if present == False:
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: vrf_name not present")
+ return "CB_SUCCESS", cmd_str
+
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: vrf_name {} present".format(
+ vrf_name))
+ tbl_path = "sonic-ospfv2:sonic-ospfv2/OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER/OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER_LIST"
+ present, tbl_path, tbl_rec_list = ospf_get_key_value(
+ render_tables, tbl_path)
+ if present == False:
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER not present"
+ )
+ return "CB_SUCCESS", cmd_str
+
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER {}".format(
+ tbl_rec_list))
+
+ if not isinstance(tbl_rec_list, list):
+ tbl_rec_list = [tbl_rec_list]
+
+ for tbl_rec in tbl_rec_list:
+ vrf_p, _, vrf_v = ospf_get_key_value(tbl_rec, "vrf_name")
+ if vrf_p == False:
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config : vrf_name field not present"
+ )
+ continue
+
+ if vrf_v != vrf_name:
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config : vrf names {} != {}".
+ format(vrf_v, vrf_name))
+ continue
+
+ nbr_p, _, nbr_v = ospf_get_key_value(tbl_rec, "neighbour-id")
+ if nbr_p == False:
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: neighbour-id field not present"
+ )
+ continue
+ cmd_str += " graceful-restart helper enable {}".format(nbr_v)
+ cmd_str += cmd_end
+
+ ospf_debug_print(
+ "show_router_ospf_gr_helper_neighbor_config: cmd_str {}".format(cmd_str))
+ return "CB_SUCCESS", cmd_str
def show_router_ospf_distribute_route_config(render_tables):
cmd_str = ""
@@ -814,9 +918,9 @@ def show_router_ospf_distribute_route_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_router_ospf_distribute_route_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_router_ospf_distribute_route_config: render_tables {}".format(
+ # render_tables))
present, name, vrf_name = ospf_get_key_value(render_tables, "vrf-name")
if present == False:
@@ -914,8 +1018,8 @@ def show_interface_ip_ospf_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print("show_interface_ip_ospf_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print("show_interface_ip_ospf_config: render_tables {}".format(
+ # render_tables))
present, name, intf_name = ospf_get_key_value(render_tables, "name")
if present == False:
@@ -1091,9 +1195,9 @@ def show_interface_ip_ospf_md_auth_config(render_tables):
# ospf_debug_log_enable()
# ospf_debug_log_disable()
- ospf_debug_print(
- "show_interface_ip_ospf_md_auth_config: render_tables {}".format(
- render_tables))
+ #ospf_debug_print(
+ # "show_interface_ip_ospf_md_auth_config: render_tables {}".format(
+ # render_tables))
present, name, intf_name = ospf_get_key_value(render_tables, "name")
if present == False:
diff --git a/CLI/actioner/sonic_cli_acl.py b/CLI/actioner/sonic_cli_acl.py
index eb5a515ad7..aac2dcd35b 100644
--- a/CLI/actioner/sonic_cli_acl.py
+++ b/CLI/actioner/sonic_cli_acl.py
@@ -683,6 +683,12 @@ def handle_get_acl_details_request(args):
if counter_mode == "AGGREGATE_ONLY":
keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets')
response = acl_client.get(keypath, depth=None, ignore404=False)
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/openconfig-acl-ext:dynamic-acl-sets/dynamic-acl-set={acl_type}', acl_type=args[0])
+ response2 = acl_client.get(keypath, depth=None, ignore404=False)
+ if response.ok() and response2.ok():
+ response.content.update(response2.content)
+ elif response2.ok():
+ response = response2
else:
keypath = cc.Path('/restconf/data/sonic-acl:sonic-acl/ACL_TABLE/ACL_TABLE_LIST')
response = acl_client.get(keypath, depth=None, ignore404=False)
@@ -696,6 +702,9 @@ def handle_get_acl_details_request(args):
if counter_mode == "AGGREGATE_ONLY":
keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{acl_type}', name=args[1], acl_type=args[0])
response = acl_client.get(keypath, depth=None, ignore404=False)
+ if not response.ok() and response.status_code == 404:
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/openconfig-acl-ext:dynamic-acl-sets/dynamic-acl-set={acl_type}', acl_type=args[0])
+ response = acl_client.get(keypath, depth=None, ignore404=False)
else:
keypath = cc.Path('/restconf/data/sonic-acl:sonic-acl/ACL_TABLE/ACL_TABLE_LIST={aclname}', aclname=args[1])
response = acl_client.get(keypath, depth=None, ignore404=False)
@@ -1054,7 +1063,11 @@ def __convert_l2_rule_to_user_fmt(acl_entry, rule_data):
def __parse_acl_entry(data, acl_entry, acl_type):
- seq_id = acl_entry['sequence-id']
+ if 'sequence-id' in acl_entry:
+ seq_id = acl_entry['sequence-id']
+ else:
+ seq_id = acl_entry['state']['sequence-id']
+
data[seq_id] = dict()
log.log_debug("Parse {} rule {}".format(acl_type, str(acl_entry)))
@@ -1090,8 +1103,14 @@ def __parse_acl_entry(data, acl_entry, acl_type):
def __convert_oc_acl_set_to_user_fmt(acl_set, data):
- acl_name = acl_set['name']
- acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['type'])
+ if 'name' in acl_set:
+ acl_name = acl_set['name']
+ else:
+ acl_name = acl_set['state']['name']
+ if 'type' in acl_set:
+ acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['type'])
+ else:
+ acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['state']['type'])
if not data.get(acl_type, None):
data[acl_type] = OrderedDict()
data[acl_type][acl_name] = OrderedDict()
@@ -1123,14 +1142,29 @@ def __handle_get_acl_details_aggregate_mode_response(resp_content, args):
acl_type = __convert_oc_acl_type_to_user_fmt(args[0])
data[acl_type] = OrderedDict()
- for acl_set in resp_content["openconfig-acl:acl-sets"]["acl-set"]:
- if not acl_set['type'].endswith(args[0]):
- continue
-
- __convert_oc_acl_set_to_user_fmt(acl_set, data)
+ if "openconfig-acl:acl-sets" in resp_content:
+ resp_content_data = resp_content["openconfig-acl:acl-sets"]
+ if "acl-set" in resp_content_data:
+ for acl_set in resp_content_data["acl-set"]:
+ if not acl_set['type'].endswith(args[0]):
+ continue
+ __convert_oc_acl_set_to_user_fmt(acl_set, data)
+
+ if 'openconfig-acl-ext:dynamic-acl-set' in resp_content:
+ resp_content_data = resp_content['openconfig-acl-ext:dynamic-acl-set']
+ if 'acl-sets' in resp_content_data[0]:
+ for acl_set in resp_content_data[0]['acl-sets']['acl-set']:
+ __convert_oc_acl_set_to_user_fmt(acl_set, data)
else:
log.log_debug('Get details for ACL Type {}::{}'.format(args[0], args[1]))
- __convert_oc_acl_set_to_user_fmt(resp_content['openconfig-acl:acl-set'][0], data)
+ if 'openconfig-acl-ext:dynamic-acl-set' in resp_content:
+ resp_content_data = resp_content['openconfig-acl-ext:dynamic-acl-set']
+ if 'acl-sets' in resp_content_data[0]:
+ for acl_set in resp_content_data[0]['acl-sets']['acl-set']:
+ if acl_set['state']['name'] == args[1]:
+ __convert_oc_acl_set_to_user_fmt(acl_set, data)
+ else:
+ __convert_oc_acl_set_to_user_fmt(resp_content['openconfig-acl:acl-set'][0], data)
log.log_debug(str(data))
show_cli_output('show_access_list.j2', data)
@@ -1158,37 +1192,85 @@ def __get_and_show_acl_counters_by_name_and_intf(acl_name, acl_type, intf_name,
if response.ok():
log.log_debug(response.content)
__convert_oc_acl_set_to_user_fmt(response.content['openconfig-acl:acl-set'][0], output)
- temp = OrderedDict()
- __deep_copy(temp, output)
- if cache:
- cache[acl_name] = temp
- else:
+ elif not response.ok() and response.status_code == 404:
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/openconfig-acl-ext:dynamic-acl-sets/dynamic-acl-set={acl_type}', acl_type=acl_type)
+ response = acl_client.get(keypath, depth=None, ignore404=False)
+ if response.ok():
+ log.log_debug(response.content)
+ if 'acl-sets' in response.content['openconfig-acl-ext:dynamic-acl-set'][0]:
+ for acl_set in response.content['openconfig-acl-ext:dynamic-acl-set'][0]['acl-sets']['acl-set']:
+ if acl_set['state']['name'] == acl_name:
+ __convert_oc_acl_set_to_user_fmt(acl_set, output)
+ if not response.ok():
log.log_error("Error pulling ACL config for {}:{}".format(acl_name, acl_type))
raise SonicAclCLIError("{}".format(response.error_message()))
+
+ temp = OrderedDict()
+ __deep_copy(temp, output)
+ if cache:
+ cache[acl_name] = temp
else:
log.log_debug("Cache present")
__deep_copy(output, cache[acl_name])
user_acl_type = __convert_oc_acl_type_to_user_fmt(acl_type)
output[user_acl_type][acl_name]['stage'] = stage.capitalize()
+ foundStaticBinding = False
if intf_name != "CtrlPlane":
keypath = cc.Path('/restconf/data/openconfig-acl:acl/interfaces/interface={id}/{stage}-acl-sets/{stage}-acl-set={setname},{acltype}',
id=intf_name, stage=stage.lower(), setname=acl_name, acltype=acl_type)
response = acl_client.get(keypath, depth=None, ignore404=False)
- if not response.ok():
+
+ log.log_debug(response.content)
+ if response.ok():
+ acl_set = response.content['openconfig-acl:{}-acl-set'.format(stage.lower())][0]
+ user_acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['type'])
+ output[user_acl_type][acl_name]['stage'] = stage.capitalize()
+ foundStaticBinding = True
+ for acl_entry in acl_set['acl-entries']['acl-entry']:
+ try:
+ # Its possible that the counter may not be present for user configured ACLs when dynamic ACLs are active
+ output[user_acl_type][acl_name]['rules'][acl_entry['sequence-id']]['packets'] = acl_entry['state']['matched-packets']
+ output[user_acl_type][acl_name]['rules'][acl_entry['sequence-id']]['octets'] = acl_entry['state']['matched-octets']
+ except KeyError:
+ pass
+ elif response.status_code != 404:
raise SonicAclCLIError("{}".format(response.error_message()))
+ if intf_name != "CtrlPlane" and intf_name != "Switch":
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/interfaces/interface={id}/openconfig-acl-ext:dynamic-{stage}-acl-sets/dynamic-{stage}-acl-set={acltype}',
+ id=intf_name, stage=stage.lower(), acltype=acl_type)
+ response = acl_client.get(keypath, depth=None, ignore404=False)
log.log_debug(response.content)
- acl_set = response.content['openconfig-acl:{}-acl-set'.format(stage.lower())][0]
- user_acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['type'])
- output[user_acl_type][acl_name]['stage'] = stage.capitalize()
- for acl_entry in acl_set['acl-entries']['acl-entry']:
- try:
- # Its possible that the counter may not be present for user configured ACLs when dynamic ACLs are active
- output[user_acl_type][acl_name]['rules'][acl_entry['sequence-id']]['packets'] = acl_entry['state']['matched-packets']
- output[user_acl_type][acl_name]['rules'][acl_entry['sequence-id']]['octets'] = acl_entry['state']['matched-octets']
- except KeyError:
- pass
+ if response.ok():
+ acl_stage_set = response.content['openconfig-acl-ext:dynamic-{}-acl-set'.format(stage.lower())][0]
+ acl_set = {}
+ if '{}-acl-sets'.format(stage.lower()) in acl_stage_set:
+ for acl_stage_set_data in acl_stage_set['{}-acl-sets'.format(stage.lower())]['{}-acl-set'.format(stage.lower())]:
+ if acl_stage_set_data['state']['set-name'] == acl_name:
+ acl_set = acl_stage_set_data
+ break
+ if 'type' in acl_set:
+ user_acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['type'])
+ elif 'state' in acl_set:
+ user_acl_type = __convert_oc_acl_type_to_user_fmt(acl_set['state']['type'])
+ output[user_acl_type][acl_name]['stage'] = stage.capitalize()
+ if 'acl-entries' in acl_set:
+ for acl_entry in acl_set['acl-entries']['acl-entry']:
+ sequence_id = acl_entry['state']['sequence-id']
+ try:
+ if 'packets' not in output[user_acl_type][acl_name]['rules'][sequence_id]:
+ output[user_acl_type][acl_name]['rules'][sequence_id]['packets'] = acl_entry['state']['matched-packets']
+ else:
+ output[user_acl_type][acl_name]['rules'][sequence_id]['packets'] = str(int(output[user_acl_type][acl_name]['rules'][sequence_id]['packets']) + int(acl_entry['state']['matched-packets']))
+ if 'octets' not in output[user_acl_type][acl_name]['rules'][sequence_id]:
+ output[user_acl_type][acl_name]['rules'][sequence_id]['octets'] = acl_entry['state']['matched-octets']
+ else:
+ output[user_acl_type][acl_name]['rules'][sequence_id]['octets'] = str(int(output[user_acl_type][acl_name]['rules'][sequence_id]['octets']) + int(acl_entry['state']['matched-octets']))
+ except KeyError:
+ pass
+ elif not foundStaticBinding:
+ raise SonicAclCLIError("{}".format(response.error_message()))
render_dict = dict()
if intf_name != "Switch":
@@ -1236,11 +1318,11 @@ def __process_acl_counters_request_by_name_and_inf(response, args):
portBindingData = __get_port_binding_table_data(acl_type)
stage = acl_data.get("stage", "INGRESS")
- ports = []
+ ports = set()
if "ports" in acl_data:
- ports = acl_data["ports"]
- elif acl_data["aclname"] in portBindingData:
- ports = portBindingData[acl_data["aclname"]]
+ ports.update(acl_data["ports"])
+ if acl_data["aclname"] in portBindingData:
+ ports.update(portBindingData[acl_data["aclname"]])
intf_name = args[2] if len(args) == 3 else "".join(args[3:])
if intf_name in ports:
@@ -1271,11 +1353,11 @@ def __process_acl_counters_request_by_type_and_name(response, args):
portBindingData = __get_port_binding_table_data(acl_type)
cache = dict()
stage = acl_data.get("stage", "INGRESS")
- ports = []
+ ports = set()
if "ports" in acl_data:
- ports = acl_data["ports"]
- elif acl_data["aclname"] in portBindingData:
- ports = portBindingData[acl_data["aclname"]]
+ ports.update(acl_data["ports"])
+ if acl_data["aclname"] in portBindingData:
+ ports.update(portBindingData[acl_data["aclname"]])
if len(ports) != 0:
# Sort and show
@@ -1289,6 +1371,9 @@ def __process_acl_counters_request_by_type_and_name(response, args):
log.log_debug("ACL {} Type {} has ZERO ports. Show only ACL configuration.".format(acl_data["aclname"], acl_type))
keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{acl_type}', name=args[1], acl_type=args[0])
response = acl_client.get(keypath, depth=None, ignore404=False)
+ if not response.ok() and response.status_code == 404:
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/openconfig-acl-ext:dynamic-acl-sets/dynamic-acl-set={acl_type}', acl_type=args[0])
+ response = acl_client.get(keypath, depth=None, ignore404=False)
if response.ok():
__handle_get_acl_details_aggregate_mode_response(response.content, args)
else:
@@ -1318,11 +1403,11 @@ def __process_acl_counters_request_by_type(response, args):
cache = dict()
for acl in filtered_acls:
stage = acl.get("stage", "INGRESS")
- ports = []
+ ports = set()
if "ports" in acl:
- ports = acl["ports"]
- elif acl["aclname"] in portBindingData:
- ports = portBindingData[acl["aclname"]]
+ ports.update(acl["ports"])
+ if acl["aclname"] in portBindingData:
+ ports.update(portBindingData[acl["aclname"]])
if len(ports) != 0:
# Sort and show
@@ -1338,6 +1423,9 @@ def __process_acl_counters_request_by_type(response, args):
acl_name = acl["aclname"].replace("_" + args[0], "")
keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{acl_type}', name=acl_name, acl_type=args[0])
response = acl_client.get(keypath, depth=None, ignore404=False)
+ if not response.ok() and response.status_code == 404:
+ keypath = cc.Path('/restconf/data/openconfig-acl:acl/openconfig-acl-ext:dynamic-acl-sets/dynamic-acl-set={acl_type}', acl_type=args[0])
+ response = acl_client.get(keypath, depth=None, ignore404=False)
if response.ok():
__handle_get_acl_details_aggregate_mode_response(response.content, [args[0], acl_name])
else:
@@ -1358,7 +1446,7 @@ def handle_get_acl_details_response(response, op_str, args):
if response.ok():
if response.counter_mode == 'AGGREGATE_ONLY':
__handle_get_acl_details_aggregate_mode_response(response.content, args)
- else:
+ else:
if bool(response.content):
__handle_get_acl_details_interface_mode_response(response.content, args)
elif len(args) > 1:
diff --git a/CLI/actioner/sonic_cli_authmgr.py b/CLI/actioner/sonic_cli_authmgr.py
index c0e0ed7aeb..717355569e 100644
--- a/CLI/actioner/sonic_cli_authmgr.py
+++ b/CLI/actioner/sonic_cli_authmgr.py
@@ -89,6 +89,14 @@ def invoke_api(func, args):
portCtlMode = 'FORCE_AUTHORIZED'
elif args[0] == 'force-unauthorized':
portCtlMode = 'FORCE_UNAUTHORIZED'
+ if portCtlMode == 'AUTO' or portCtlMode == 'FORCE_UNAUTHORIZED':
+ func = "get_switchport_access_trunk_mode"
+ response = {}
+ response = invoke_api(func, args[1])
+ if response and response.ok() and (response.content is not None) and ('sonic-port:PORT_LIST' in response.content):
+ if "access_vlan" in response.content['sonic-port:PORT_LIST'][0] or "tagged_vlans" in response.content['sonic-port:PORT_LIST'][0]:
+ print("%Info: Enabling PAC on the port will revert all switchport configurations on the port,\n\
+ if port control mode is auto/force-unauthorized and port pae role is authenticator.")
path = cc.Path('/restconf/data/openconfig-authmgr:authmgr/authmgr-port-config/interface')
body = collections.defaultdict(dict)
body["interface"] = [{
@@ -497,7 +505,8 @@ def invoke_api(func, args):
response = invoke_api(func, args[1])
if response and response.ok() and (response.content is not None) and ('sonic-port:PORT_LIST' in response.content):
if "access_vlan" in response.content['sonic-port:PORT_LIST'][0] or "tagged_vlans" in response.content['sonic-port:PORT_LIST'][0]:
- print("%Info: Enabling PAC on the port will revert all switchport configurations on the port")
+ print("%Info: Enabling PAC on the port will revert all switchport configurations on the port,\n\
+ if port control mode is auto/force-unauthorized and port pae role is authenticator.")
elif args[0] == 'none':
dot1xPaeRole = 'NONE'
path = cc.Path('/restconf/data/openconfig-authmgr:authmgr/authmgr-port-config/interface')
diff --git a/CLI/actioner/sonic_cli_breakout.py b/CLI/actioner/sonic_cli_breakout.py
index 7c4647182c..c574cc0c4e 100644
--- a/CLI/actioner/sonic_cli_breakout.py
+++ b/CLI/actioner/sonic_cli_breakout.py
@@ -82,21 +82,14 @@ def invoke(func, args):
"num-physical-channels": 0
}
}
- '''body = {
- "openconfig-platform:components": {
- "component": [{
- "name": interface,
- "port": {
- "openconfig-platform-port:breakout-mode": {
- "config": {
- "num-channels": int(args[1][0]),
- "channel-speed": speed_map.get(args[1]),
- }
- }
- },
- }]
- }
- }'''
+ get_resp = aa.get(path)
+ if get_resp.ok() and get_resp.content:
+ if 'openconfig-platform-port:config' in get_resp.content:
+ cfg = get_resp.content['openconfig-platform-port:config']
+ if 'breakout-speed' in cfg:
+ if speed_map.get(args[1]) in cfg['breakout-speed'] and int(args[1][0]) is cfg['num-breakouts']:
+ print("No change in port breakout mode")
+ return None
return aa.patch(path, body)
elif (func ==
@@ -110,11 +103,9 @@ def invoke(func, args):
get_resp = aa.get(path)
if get_resp.ok() and get_resp.content:
return aa.delete(path)
- elif get_resp.error_message():
- return get_resp
else:
- return aa._make_error_response(
- "%Error: No change in port breakout mode")
+ print("No change in port breakout mode")
+ return None
else:
path = cc.Path(
"/restconf/data/sonic-port-breakout:sonic-port-breakout")
@@ -166,25 +157,26 @@ def invoke(func, args):
def run(func, args):
try:
api_response = invoke(func, args)
- if api_response.ok():
- if (func.find(
+ if api_response is not None:
+ if api_response.ok():
+ if (func.find(
"openconfig_platform_port_components_component_port_breakout_mode_config"
- ) != -1):
- print((
- "Dynamic Port Breakout in-progress, use 'show interface breakout port {}' to check status."
- .format(args[0])))
- if api_response.content is not None:
- if func == "dependencies":
- temp = args[1]
- elif func == "modes":
- temp = args[1]
- elif len(args) > 3:
- temp = args[3]
- else:
- temp = args[1]
- show_cli_output(temp, api_response.content)
- elif api_response.error_message():
- print((api_response.error_message()))
+ ) != -1):
+ print((
+ "Dynamic Port Breakout in-progress, use 'show interface breakout port {}' to check status."
+ .format(args[0])))
+ if api_response.content is not None:
+ if func == "dependencies":
+ temp = args[1]
+ elif func == "modes":
+ temp = args[1]
+ elif len(args) > 3:
+ temp = args[3]
+ else:
+ temp = args[1]
+ show_cli_output(temp, api_response.content)
+ elif api_response.error_message():
+ print((api_response.error_message()))
except Exception as ex:
print(ex)
diff --git a/CLI/actioner/sonic_cli_dropcounter.py b/CLI/actioner/sonic_cli_dropcounter.py
index f159d077f3..fa3dc3c3ff 100755
--- a/CLI/actioner/sonic_cli_dropcounter.py
+++ b/CLI/actioner/sonic_cli_dropcounter.py
@@ -143,7 +143,7 @@ def invoke(func, args):
if func == 'add_reason_config':
reasons = deserialize_reason_list(args[1])
- keypath = cc.Path('/restconf/data/sonic-debugcounter:sonic-debugcounter/DEBUG_COUNTER/DEBUG_COUNTER_LIST={name}/type', name=args[0])
+ keypath = cc.Path('/restconf/data/sonic-debugcounter:sonic-debugcounter/DEBUG_COUNTER/DEBUG_COUNTER_LIST={name}/reasons', name=args[0])
body = {"sonic-debugcounter:reasons" : [args[1]]}
return aa.patch(keypath, body)
diff --git a/CLI/actioner/sonic_cli_if_autoneg.py b/CLI/actioner/sonic_cli_if_autoneg.py
index 4f1bdf0713..3a56f3d320 100644
--- a/CLI/actioner/sonic_cli_if_autoneg.py
+++ b/CLI/actioner/sonic_cli_if_autoneg.py
@@ -358,7 +358,13 @@ def run(func, args):
body = {"openconfig-interfaces:interfaces": {"interface": info}}
path = cc.Path("/restconf/data/openconfig-interfaces:interfaces")
- aa.patch(path, body)
+ response = {}
+ response = aa.patch(path, body)
+ if response.ok():
+ return
+ else:
+ print((response.error_message()))
+ return
elif func == "patch_ocif_eth_link_training":
if if_name is not None:
diff --git a/CLI/actioner/sonic_cli_in_memory_logging.py b/CLI/actioner/sonic_cli_in_memory_logging.py
index 74a47050bc..e045bfc522 100755
--- a/CLI/actioner/sonic_cli_in_memory_logging.py
+++ b/CLI/actioner/sonic_cli_in_memory_logging.py
@@ -20,6 +20,9 @@
import cli_client as cc
from scripts.render_cli import show_cli_output
+import os
+from rpipe_utils import pipestr
+import scripts.render_cli as cli
def get_sonic_inmemory_logging(args):
@@ -30,14 +33,20 @@ def get_sonic_inmemory_logging(args):
body = {"openconfig-infra-logging:input": {"num-lines": int(args[1])}}
else:
body = {"openconfig-infra-logging:input": {"num-lines": 0}}
- templ = args[0]
- api_response = aa.post(keypath, body)
+ api_response = aa.post(keypath, body, response_type='string')
try:
if api_response.ok():
- response = api_response.content
- if response is not None and "openconfig-infra-logging:output" in response:
- show_cli_output(templ, response)
+ response = api_response.content
+ if response is not None and 'openconfig-infra-logging:output' in response:
+ full_cmd = os.getenv('USER_COMMAND', None)
+ if full_cmd is not None:
+ pipestr().write(full_cmd.split())
+ data = response.split("\"status-detail\":[\"")
+ if len(data) > 1:
+ cli.write(data[1].split("\"]}}")[0])
+ else:
+ print("%Error:{}".format(data))
except Exception as e:
raise e
except KeyboardInterrupt:
diff --git a/CLI/actioner/sonic_cli_logging.py b/CLI/actioner/sonic_cli_logging.py
index c3738fa22d..0d4b9f632b 100755
--- a/CLI/actioner/sonic_cli_logging.py
+++ b/CLI/actioner/sonic_cli_logging.py
@@ -20,6 +20,9 @@
import cli_client as cc
from scripts.render_cli import show_cli_output
+import os
+from rpipe_utils import pipestr
+import scripts.render_cli as cli
SYSTEM = "/restconf/data/openconfig-system:system/"
LOG_SERVERS = SYSTEM + "logging/remote-servers"
@@ -202,21 +205,27 @@ def get_sonic_logging(args):
body = {
"openconfig-infra-logging:input": {
"num-lines": num,
- "logtype": "null",
+ "logtype": "syslog",
"loglevel": "null",
"severity": "null",
"since": "null"
}
}
- templ = args[0]
- api_response = aa.post(keypath, body)
+ api_response = aa.post(keypath, body, response_type='string')
try:
if api_response.ok():
response = api_response.content
if response is not None and "openconfig-infra-logging:output" in response:
- show_cli_output(templ, response)
+ full_cmd = os.getenv('USER_COMMAND', None)
+ if full_cmd is not None:
+ pipestr().write(full_cmd.split())
+ data = response.split("\"status-detail\":[\"")
+ if len(data) > 1:
+ cli.write(data[1].split("\"]}}")[0])
+ else:
+ print("%Error:{}".format(data))
except Exception as e:
print(e)
raise e
@@ -272,7 +281,7 @@ def get_openconfig_system_logging_filter(args):
body = {
"openconfig-infra-logging:input": {
"num-lines": 0,
- "logtype": args[1],
+ "logtype": "syslog" if args[1] == "null" else args[1],
"loglevel": args[2],
"severity": args[3],
"since": ' '.join(args[4:])
diff --git a/CLI/actioner/sonic_cli_mclag.py b/CLI/actioner/sonic_cli_mclag.py
index 92158e748c..54ff1bcaa9 100755
--- a/CLI/actioner/sonic_cli_mclag.py
+++ b/CLI/actioner/sonic_cli_mclag.py
@@ -23,6 +23,7 @@
import cli_client as cc
from rpipe_utils import pipestr
from scripts.render_cli import show_cli_output
+from natsort import natsorted
MCLAG_DEFAULT_DELAY_RESTORE = 300
@@ -405,6 +406,14 @@ def invoke(func, args):
)
return aa.get(keypath)
+ if func == "get_sonic_mclag_sonic_mclag_mclag_fdb_table_mclag_fdb_table_list":
+ keypath = cc.Path(
+ "/restconf/data/sonic-mclag:sonic-mclag/MCLAG_FDB_TABLE/MCLAG_FDB_TABLE_LIST"
+ )
+ return aa.get(keypath)
+
+
+
#######################################
# Get APIs - END
#######################################
@@ -733,6 +742,51 @@ def mclag_show_mclag_peer_gateway(args):
return
+# show mclag remote mac command
+def mclag_show_mclag_remote_mac(args):
+ mclag_iface_info = {}
+
+ api_response = invoke(
+ "get_sonic_mclag_sonic_mclag_mclag_fdb_table_mclag_fdb_table_list",
+ args[1:])
+ arg_length = len(args)
+ if api_response.ok():
+ response = api_response.content
+ if response is None:
+ print("no mclag fdb entries")
+ elif len(response) != 0 and "sonic-mclag:MCLAG_FDB_TABLE_LIST" in response:
+ index = 0
+ while index < len(response["sonic-mclag:MCLAG_FDB_TABLE_LIST"]):
+ iter = response["sonic-mclag:MCLAG_FDB_TABLE_LIST"][index]
+ if iter["mac_type"] != "remote":
+ response["sonic-mclag:MCLAG_FDB_TABLE_LIST"].pop(index)
+ if arg_length > 2 and args[2] != iter["vlan"] and args[2] != iter["port"]:
+ response["sonic-mclag:MCLAG_FDB_TABLE_LIST"].pop(index)
+ else:
+ index = index + 1
+ if args[1] == "show":
+ mclag_fdb_list = response[
+ "sonic-mclag:MCLAG_FDB_TABLE_LIST"]
+ response["sonic-mclag:MCLAG_FDB_TABLE_LIST"] = natsorted(mclag_fdb_list, key=lambda x: x['vlan'])
+ show_cli_output(args[0], response)
+ elif args[1] == "count":
+ if "sonic-mclag:MCLAG_FDB_TABLE_LIST" in response:
+ remote_mac_list = response[
+ "sonic-mclag:MCLAG_FDB_TABLE_LIST"]
+ remote_mac_list_len = str(len(remote_mac_list))
+ print(("Total Count: " + remote_mac_list_len))
+ else:
+ print("Total Count: 0")
+ else:
+ if api_response.status_code != 404:
+ print((api_response.error_message()))
+ else:
+ print(("No MCLAG remote mac "))
+
+
+ return
+
+
def run(func, args):
@@ -751,6 +805,9 @@ def run(func, args):
if func == "show_mclag_peer_gateway":
mclag_show_mclag_peer_gateway(args)
return 0
+ if func == "show_mclag_remote_mac":
+ mclag_show_mclag_remote_mac(args)
+ return 0
except Exception as e:
print((sys.exc_info()[1]))
diff --git a/CLI/actioner/sonic_cli_oc_kdump.py b/CLI/actioner/sonic_cli_oc_kdump.py
index cc055d8a71..f67439a696 100644
--- a/CLI/actioner/sonic_cli_oc_kdump.py
+++ b/CLI/actioner/sonic_cli_oc_kdump.py
@@ -24,10 +24,10 @@ def invoke(func, args):
)
return cl.get(path)
else:
- msg = "Kdump configuration has been updated in the startup configuration"
- mem_msg = (
- "\nKdump updated memory will be only operational after the system reboots"
- )
+ msg = "Kdump configuration changes will be applied after the system reboots.\n"
+ save_msg = "Save SONiC configuration using 'write memory' before issuing the reboot command."
+ mem_msg = "Kdump updated memory will be only operational after the system reboots.\n"
+ msg = msg + save_msg
path = cc.Path(
"/restconf/data/openconfig-system:system/openconfig-system-ext:kdump/config"
)
@@ -37,37 +37,36 @@ def invoke(func, args):
"enable": False if func == "disable" else True
}
}
- msg = (
- msg +
- "\nKdump configuration changes will be applied after the system reboots"
- )
elif func == "config":
param = args[0]
value = args[1]
if param == "memory":
- msg = msg + mem_msg
+ msg = mem_msg + save_msg
elif param == "max-dumps":
value = int(args[1])
+ msg = ""
else:
return None
body = {"openconfig-system-ext:config": {param: value}}
elif func == "reset":
if args[0] == "memory":
- msg = msg + mem_msg
- elif args[0] != "max-dumps":
+ msg = mem_msg + save_msg
+ elif args[0] == "max-dumps":
+ msg = ""
+ else:
return None
path = cc.Path(
"/restconf/data/openconfig-system:system/openconfig-system-ext:kdump/config/{}"
.format(args[0]))
res = cl.delete(path)
- if res.ok():
+ if res.ok() and len(msg):
print(msg)
return res
else:
return None
res = cl.patch(path, body)
- if res.ok():
+ if res.ok() and len(msg):
print(msg)
return res
diff --git a/CLI/actioner/sonic_cli_ospfv2.py b/CLI/actioner/sonic_cli_ospfv2.py
index 1bc98b75de..fd09f4421f 100644
--- a/CLI/actioner/sonic_cli_ospfv2.py
+++ b/CLI/actioner/sonic_cli_ospfv2.py
@@ -973,6 +973,22 @@ def invoke_api(func, args=[]):
api, args[0],
"config/openconfig-ospfv2-ext:ospf-rfc1583-compatible")
+ elif (func ==
+ "patch_openconfig_ospfv2_ext_network_instances_network_instance_protocols_protocol_ospfv2_global_config_opaque_lsa_capability"
+ ):
+ body = {
+ "config": {
+ "openconfig-ospfv2-ext:opaque-lsa-capability": True
+ }
+ }
+ return patch_ospf_router_global_config(api, args[0], body)
+
+ elif (func ==
+ "delete_openconfig_ospfv2_ext_network_instances_network_instance_protocols_protocol_ospfv2_global_config_opaque_lsa_capability"
+ ):
+ return delete_ospf_router_global_config(
+ api, args[0],
+ "config/openconfig-ospfv2-ext:opaque-lsa-capability")
elif (func ==
"patch_openconfig_network_instance_network_instances_network_instance_protocols_protocol_config_default_metric"
):
@@ -1044,6 +1060,176 @@ def invoke_api(func, args=[]):
return patch_ospf_router_passive_interface_config(
api, args[0], args[1], args[2], body)
+ elif (func ==
+ "patch_openconfig_network_instance_network_instances_network_instance_protocols_protocol_ospfv2_global_graceful_restart_config"
+ ):
+ vrf = args[0]
+ gr = False
+ grgraceperiod = ""
+ grtype = ""
+ helperoption = ""
+ grhelper = False
+ grhelpernbr = ""
+ grhelperlsa = False
+ grhelperplanned = False
+ grhelpersupportedgracetime = ""
+ grhelperoptions = False
+
+ #print("gr-config: argslen {} args={}".format(len(args), args))
+
+ #i = 0
+ #for arg in args:
+ #print(i, args[i])
+ #i = i + 1
+
+ i = 0
+ for arg in args:
+ if arg == "graceful-restart":
+ gr = True
+ if (len(args) > 2):
+ grtype = args[i + 1]
+ if grtype == "helper":
+ helperoption = args[i+2]
+ if helperoption == "enable":
+ grhelper = True
+ if (len(args) > 4):
+ grhelpernbr = args[i+3]
+ elif helperoption == "strict-lsa-checking":
+ grhelperoptions = True
+ grhelperlsa = True
+ elif helperoption == "planned-only":
+ grhelperoptions = True
+ grhelperplanned = True
+ elif helperoption == "supported-grace-time":
+ grhelperoptions = True
+ grhelpersupportedgracetime = args[i+3]
+ elif grtype == "grace-period":
+ grgraceperiod = args[i+2]
+ i = i + 1
+ # Graceful-Restart enabled
+ if gr == True and grhelper != True and grhelperoptions != True:
+ body = {
+ "openconfig-network-instance:graceful-restart": {
+ "config": { "enabled": True}
+ }
+ }
+ if grgraceperiod != "":
+ body = {
+ "openconfig-network-instance:graceful-restart": {
+ "config": { "enabled": True,
+ "openconfig-ospfv2-ext:grace-period": int(grgraceperiod)}
+ }
+ }
+ return patch_ospf_router_global_config(api, args[0], body)
+ # Graceful-Restart helper for all neighbors
+ elif grhelper == True and grhelpernbr == "":
+ body = {
+ "openconfig-network-instance:graceful-restart": {
+ "config": { "helper-only": True}
+ }
+ }
+ return patch_ospf_router_global_config(api, args[0], body)
+ # Graceful-Restart helper for a specific neighbor
+ elif grhelper == True and grhelpernbr != "":
+ body = {
+ "openconfig-network-instance:graceful-restart": {
+ "openconfig-ospfv2-ext:helpers": {
+ "helper": [
+ {
+ "neighbour-id": grhelpernbr,
+ "config": {
+ "vrf-name": vrf,
+ "neighbour-id": grhelpernbr,
+ "enabled": True
+ }
+ }
+ ]
+ }
+ }
+ }
+ return patch_ospf_router_global_config(api, args[0], body)
+ # Graceful-Restart helper options
+ elif grhelperoptions == True:
+ body = {"openconfig-network-instance:graceful-restart": {"config": {}}}
+ if grhelperlsa == True:
+ body["openconfig-network-instance:graceful-restart"].update({"config":{"openconfig-ospfv2-ext:strict-lsa-checking": True}})
+ if grhelperplanned == True:
+ body["openconfig-network-instance:graceful-restart"].update({"config":{"openconfig-ospfv2-ext:planned-only": True}})
+ if grhelpersupportedgracetime != "":
+ body["openconfig-network-instance:graceful-restart"].update({"config":{"openconfig-ospfv2-ext:supported-grace-time": int(grhelpersupportedgracetime)}})
+ return patch_ospf_router_global_config(api, args[0], body)
+
+ elif (func ==
+ "delete_openconfig_network_instance_network_instances_network_instance_protocols_protocol_ospfv2_global_graceful_restart_config"
+ ):
+ vrf = args[0]
+ gr = False
+ grgraceperiod = False
+ grtype = ""
+ helperoption = ""
+ grhelper = False
+ grhelpernbr = ""
+ grhelperlsa = False
+ grhelperplanned = False
+ grhelpersupportedgracetime = False
+ grhelperoptions = False
+
+ #print("gr-config(delete): argslen {} args={}".format(len(args), args))
+
+ #i = 0
+ #for arg in args:
+ #print(i, args[i])
+ #i = i + 1
+
+ i = 0
+ for arg in args:
+ if arg == "graceful-restart":
+ gr = True
+ if (len(args) > 3):
+ grtype = args[i + 1]
+ if grtype == "helper":
+ helperoption = args[i+2]
+ if helperoption == "enable":
+ grhelper = True
+ if (len(args) > 5):
+ grhelpernbr = args[i+3]
+ elif helperoption == "strict-lsa-checking":
+ grhelperoptions = True
+ grhelperlsa = True
+ elif helperoption == "planned-only":
+ grhelperoptions = True
+ grhelperplanned = True
+ elif helperoption == "supported-grace-time":
+ grhelperoptions = True
+ grhelpersupportedgracetime = True
+ elif grtype == "grace-period":
+ grgraceperiod = True
+ i = i + 1
+ if gr == True and grhelper != True and grhelperoptions != True:
+ response = delete_ospf_router_global_config(
+ api, args[0], "graceful-restart/config/enabled")
+ if response.ok() == False:
+ return response
+ return delete_ospf_router_global_config(
+ api, args[0], "graceful-restart/config/openconfig-ospfv2-ext:grace-period")
+ elif grhelper == True and grhelpernbr == "":
+ return delete_ospf_router_global_config(
+ api, args[0], "graceful-restart/config/helper-only")
+ elif grhelper == True and grhelpernbr != "":
+ ospf_gr_helper_nbr_uri = "graceful-restart/openconfig-ospfv2-ext:helpers/helper={}".format(grhelpernbr)
+ return delete_ospf_router_global_config(
+ api, args[0], ospf_gr_helper_nbr_uri)
+ elif grhelperoptions == True:
+ if grhelperlsa == True:
+ body = {"openconfig-network-instance:graceful-restart": {"config": {"openconfig-ospfv2-ext:strict-lsa-checking": False}}}
+ return patch_ospf_router_global_config(api, args[0], body)
+ if grhelperplanned == True:
+ return delete_ospf_router_global_config(
+ api, args[0], "graceful-restart/config/openconfig-ospfv2-ext:planned-only")
+ if grhelpersupportedgracetime == True:
+ return delete_ospf_router_global_config(
+ api, args[0], "graceful-restart/config/openconfig-ospfv2-ext:supported-grace-time")
+
elif (func ==
"patch_openconfig_network_instance_network_instances_network_instance_protocols_protocol_ospfv2_global_timers"
):
@@ -2776,10 +2962,28 @@ def show_ospf_running_config(args):
pipestr().write(full_cmd.split())
write(ospf_all_cfgs.replace("\t", "\n"))
+def gr_prepare_ospf_api(args):
+ api = cc.ApiClient()
+ keypath = []
+ body = None
+
+ keypath = cc.Path("/restconf/operations/sonic-graceful-restart-prepare:gr-prepare")
+
+ body = {"sonic-graceful-restart-prepare:input": {"ospfv2": True}}
+
+ return api.post(keypath, body)
def run(func, args):
if func == "show_running_config_ospf":
show_ospf_running_config(args)
+ elif func == "gr_prepare_ospf":
+ response = gr_prepare_ospf_api(args)
+ if response.ok():
+ if response.content is not None:
+ api_response = response.content
+ if api_response is None:
+ print("Failed")
+ sys.exit(1)
else:
response = invoke_api(func, args)
if response.ok():
diff --git a/CLI/actioner/sonic_cli_qos_buffer.py b/CLI/actioner/sonic_cli_qos_buffer.py
index caccf3ccdd..9a01bad0ea 100644
--- a/CLI/actioner/sonic_cli_qos_buffer.py
+++ b/CLI/actioner/sonic_cli_qos_buffer.py
@@ -22,6 +22,8 @@
import cli_client as cc
from scripts.render_cli import show_cli_output
from natsort import natsorted, ns
+import os
+import syslog as log
buffer_types = {"ingress": "INGRESS", "egress": "EGRESS"}
buffer_modes = {"static": "STATIC", "dynamic": "DYNAMIC"}
@@ -37,6 +39,20 @@ def prompt(msg):
else:
return True
+def qos_buffer_rpc_reboot(api,path,body):
+ try:
+ api_response = api.post(path, body)
+ except Exception as e:
+ log.syslog(log.LOG_NOTICE, str(e))
+ print("% Error: Internal error")
+ return
+
+ server_down_msg="Could not connect to Management REST Server"
+ if server_down_msg in api_response.error_message():
+ return 1
+ else:
+ print((api_response.error_message()))
+
def get_list_from_range(val_lists):
lst = val_lists.split(",")
lst = [sub.replace("-", "..") for sub in lst]
@@ -65,9 +81,9 @@ def invoke_api(func, args=[]):
body = {"openconfig-qos-private:input": {"operation": "INIT"}}
path = cc.Path(
"/restconf/operations/openconfig-qos-private:qos-buffer-config")
- return api.post(path, body)
- else:
- return None
+ qos_buffer_rpc_reboot(api,path,body)
+
+ return None
elif func == "rpc_clear_qos_buffer":
initMessage = prompt(msg)
@@ -75,9 +91,9 @@ def invoke_api(func, args=[]):
body = {"openconfig-qos-private:input": {"operation": "CLEAR"}}
path = cc.Path(
"/restconf/operations/openconfig-qos-private:qos-buffer-config")
- return api.post(path, body)
- else:
- return None
+ qos_buffer_rpc_reboot(api,path,body)
+
+ return None
elif func == "create_qos_buffer_pool":
path = cc.Path(
diff --git a/CLI/actioner/sonic_cli_sflow.py b/CLI/actioner/sonic_cli_sflow.py
index 0df59b8397..5216253e9e 100755
--- a/CLI/actioner/sonic_cli_sflow.py
+++ b/CLI/actioner/sonic_cli_sflow.py
@@ -129,6 +129,8 @@ def invoke_api(func, args=[]):
path = cc.Path(
"/restconf/data/openconfig-sampling-sflow:sampling/sflow/config/agent"
)
+ if args[0] == "Management0":
+ args[0] = "eth0"
body = {"openconfig-sampling-sflow:agent": args[0]}
return api.patch(path, body)
elif func == "patch_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval":
diff --git a/CLI/actioner/sonic_cli_show_ospfv2.py b/CLI/actioner/sonic_cli_show_ospfv2.py
index 51efa4f0a8..4032c3c992 100644
--- a/CLI/actioner/sonic_cli_show_ospfv2.py
+++ b/CLI/actioner/sonic_cli_show_ospfv2.py
@@ -478,12 +478,153 @@ def build_area_id_list(vrf_name):
print("%Error: Internal error")
return output
-# The below function checks if the neighbor_name provided by user contains
+def build_gr_helper_neighbor_list(vrf_name):
+ api = cc.ApiClient()
+ output = []
+ tableNeighbour = (
+ "/restconf/data/sonic-ospfv2:sonic-ospfv2/OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER/OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER_LIST",
+ "sonic-ospfv2:OSPFV2_ROUTER_GR_NEIGHBOUR_HELPER_LIST",
+ "vrf_name",
+ "neighbour-id",
+ )
+ requests = [tableNeighbour]
+ for request in requests:
+ keypath = cc.Path(request[0])
+ try:
+ response = api.get(keypath)
+ response = response.content
+ if response is None:
+ continue
+ neighbourList = response.get(request[1])
+ if neighbourList is None:
+ continue
+ for neighbour in neighbourList:
+ vrfName = neighbour.get(request[2])
+ neighbourId = neighbour.get(request[3])
+ if neighbourId is None:
+ continue
+ if vrfName is None or vrfName != vrf_name:
+ continue
+ output.append(neighbourId)
+ output.sort()
+ except Exception as e:
+ log.syslog(log.LOG_ERR, str(e))
+ print("%Error: Internal error")
+ return output
+
+def generate_show_ip_ospf_graceful_restart_helper(vrf, detail):
+ api = cc.ApiClient()
+ keypath = []
+ vrfName = ""
+ vrfName = vrf
+ d = {}
+ dlist = []
+ det = {}
+ d = {"vrfName": vrfName}
+ det = {"flagDetail": True}
+ dlist.append(d)
+ keypath = cc.Path(
+ "/restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol=OSPF,ospfv2/ospfv2/global/state",
+ name=vrfName,
+ )
+ response = api.get(keypath)
+ if response.ok():
+ if response.content is not None:
+ # Get Command Output
+ api_response = response.content
+ if api_response is None or len(api_response) == 0:
+ print("% OSPF instance not found")
+ return
+ if ("openconfig-network-instance:state" in api_response
+ and api_response["openconfig-network-instance:state"] is
+ not None):
+ api_response[
+ "openconfig-network-instance:global_state"] = api_response.pop(
+ "openconfig-network-instance:state")
+ dlist.append(api_response)
+ else:
+ print("% OSPF instance not found")
+ return
+
+ keypath = cc.Path(
+ "/restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol=OSPF,ospfv2/ospfv2/global/graceful-restart/state",
+ name=vrfName,
+ )
+ response = api.get(keypath)
+ if response.ok():
+ if response.content is not None:
+ # Get Command Output
+ api_response = response.content
+ if ("openconfig-network-instance:state" in api_response
+ and api_response["openconfig-network-instance:state"] is
+ not None):
+ api_response[
+ "openconfig-network-instance:graceful_restart_state"] = api_response.pop(
+ "openconfig-network-instance:state")
+ dlist.append(api_response)
+ else:
+ return
+
+ gr_helper_nbr_list = build_gr_helper_neighbor_list(vrfName)
+ if len(gr_helper_nbr_list) != 0:
+ neighbourInfoList = []
+ for neighbourId in gr_helper_nbr_list:
+ neighbourInfoList.append(neighbourId)
+ neighboursInfo = OrderedDict()
+ neighboursInfo["openconfig-network-instance:gr_helper_neighbors"] = neighbourInfoList
+ dlist.append(neighboursInfo)
+
+ area_id_list = []
+ area_id_list = build_area_id_list(vrfName)
+ if len(area_id_list) == 0:
+ detail = False
+
+ areaInfoList = []
+ if detail == True:
+ for areaId in area_id_list:
+ areaConfig = OrderedDict()
+ areaConfig["identifier"] = areaId
+ areaInfo = OrderedDict()
+ areaInfo["config"] = areaConfig
+ areaInfo["identifier"] = areaId
+ areaInfoList.append(areaInfo)
+ keypath = cc.Path(
+ "/restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols"
+ +
+ "/protocol=OSPF,ospfv2/ospfv2/areas/area={identifier1}/interfaces",
+ name=vrfName,
+ identifier1=areaId,
+ )
+ response = api.get(keypath)
+ if response.ok():
+ if response.content is not None:
+ # Get Command Output
+ api_response = response.content
+ if ("openconfig-network-instance:interfaces" in api_response
+ and
+ api_response["openconfig-network-instance:interfaces"]
+ is not None):
+ areaInfo["interfaces"] = api_response[
+ "openconfig-network-instance:interfaces"]
+
+ areasInfo = OrderedDict()
+ areasInfo["area"] = areaInfoList
+ areasOuter = OrderedDict()
+ areasOuter["openconfig-network-instance:areas"] = areasInfo
+ dlist.append(areasOuter)
+
+ if detail == True:
+ dlist.append(det)
+
+ show_cli_output("show_ip_ospf_graceful_restart_helper.j2", dlist)
+
+
+# The below function checks if the neighbor_name provided by user contains
# interface name like Vlan, Ethernet or portChannel or Loopback interface
def check_interface_name(neighbor_name):
if 'Eth' in neighbor_name or 'Vlan' in neighbor_name or 'PortChannel' in neighbor_name or 'Loopback' in neighbor_name:
return True
- return False
+ return False
def invoke_show_api(func, args=[]):
vrf = args[0]
@@ -549,6 +690,18 @@ def invoke_show_api(func, args=[]):
return generate_show_ip_ospf_interfaces(
vrf, "show_ip_ospf_neighbor.j2", intfname)
+ elif arg == "graceful-restart":
+ j = i + 1
+ detail = False
+ for grargs in args[j:]:
+ if grargs == "helper":
+ continue
+ if grargs == "detail":
+ detail = True
+ j = j + 1
+
+ return generate_show_ip_ospf_graceful_restart_helper(vrf, detail)
+
elif arg == "interface":
j = i
trafficcmd = False
diff --git a/CLI/actioner/sonic_cli_vlan.py b/CLI/actioner/sonic_cli_vlan.py
index cbfa556068..cdc2ce516b 100644
--- a/CLI/actioner/sonic_cli_vlan.py
+++ b/CLI/actioner/sonic_cli_vlan.py
@@ -109,7 +109,6 @@ def invoke_api(func, args=[]):
elif (int(vlan) + 127 > 4094):
return api._make_error_response("Invalid starting vlan id for 128 vlans to be reserved")
path1 = cc.Path("/restconf/data/openconfig-vlan-ext:reserve-vlans")
- response = api.delete(path1)
keypath = cc.Path("/restconf/data/openconfig-vlan-ext:reserve-vlans")
body = {
@@ -138,26 +137,7 @@ def invoke_api(func, args=[]):
path1 = cc.Path("/restconf/data/openconfig-vlan-ext:reserve-vlans")
response = api.delete(path1)
- if response.ok():
- j = 3967
- body = {
- "openconfig-vlan-ext:reserve-vlans": {
- "reserve-vlan": []
- }
- }
- i = 0
- while i <= 127:
- vlan_name = "Vlan" + str(int(i+j))
- body["openconfig-vlan-ext:reserve-vlans"]["reserve-vlan"].append({
- "vlan-name": vlan_name,
- "config": {
- "vlan-name": vlan_name
- }
- })
-
- i = i+1
-
- response = api.patch(path1, body)
+
return response
if func == "reserved_vlan_show":
@@ -378,6 +358,7 @@ def run(func, args):
if func == "get_sonic_vlan_sonic_vlan":
if "sonic-vlan:sonic-vlan" in api_response:
value = api_response["sonic-vlan:sonic-vlan"]
+ reserved_vlan_list = []
if "RESERVED_VLAN" in value:
reserved_vlan_list = value["RESERVED_VLAN"]["RESERVED_VLAN_LIST"]
else:
diff --git a/CLI/actioner/sonic_cli_vxlan.py b/CLI/actioner/sonic_cli_vxlan.py
index c8f656b15b..42a2a2bd32 100755
--- a/CLI/actioner/sonic_cli_vxlan.py
+++ b/CLI/actioner/sonic_cli_vxlan.py
@@ -23,6 +23,7 @@
from rpipe_utils import pipestr
from scripts.render_cli import show_cli_output
import sonic_intf_utils as ifutils
+from natsort import natsorted
# vxlan_global_info = []
def is_vlan_vni_mapping_exists():
@@ -649,6 +650,9 @@ def vxlan_show_vxlan_vlanvnimap(args):
elif response is not None:
if args[1] == "show":
if len(response) != 0:
+ vlan_vni_map_list = response[
+ "sonic-vxlan:VXLAN_TUNNEL_MAP_LIST"]
+ response["sonic-vxlan:VXLAN_TUNNEL_MAP_LIST"] = natsorted(vlan_vni_map_list, key=lambda x: x['vlan'])
show_cli_output(args[0], response)
elif args[1] == "count":
if "sonic-vxlan:VXLAN_TUNNEL_MAP_LIST" in response:
@@ -674,11 +678,12 @@ def vxlan_show_vxlan_vrfvnimap(args):
elif response is not None:
if len(response) != 0:
if args[1] == "show":
- vrf_list = response["sonic-vrf:VRF_LIST"][0]
- for iter in vrf_list:
- iter_len = len(iter)
- if iter_len == 3:
+ vrf_list = response["sonic-vrf:VRF_LIST"]
+ for vrf in vrf_list:
+ if "vni" in vrf:
+ response["sonic-vrf:VRF_LIST"] = natsorted(vrf_list, key=lambda x: x['vrf_name'])
show_cli_output(args[0], response)
+ break
elif args[1] == "count":
vrf_map_count = 0
if "sonic-vrf:VRF_LIST" in response:
@@ -746,6 +751,9 @@ def vxlan_show_vxlan_remote_vni(args):
else:
index = index + 1
if args[1] == "show":
+ remote_map_list = response[
+ "sonic-vxlan:VXLAN_REMOTE_VNI_TABLE_LIST"]
+ response["sonic-vxlan:VXLAN_REMOTE_VNI_TABLE_LIST"] = natsorted(remote_map_list, key=lambda x: x['vlan'])
show_cli_output(args[0], response)
elif args[1] == "count":
if "sonic-vxlan:VXLAN_REMOTE_VNI_TABLE_LIST" in response:
@@ -782,6 +790,9 @@ def vxlan_show_vxlan_remote_mac(args):
else:
index = index + 1
if args[1] == "show":
+ vxlan_fdb_list = response[
+ "sonic-vxlan:VXLAN_FDB_TABLE_LIST"]
+ response["sonic-vxlan:VXLAN_FDB_TABLE_LIST"] = natsorted(vxlan_fdb_list, key=lambda x: x['vlan'])
show_cli_output(args[0], response)
elif args[1] == "count":
if "sonic-vxlan:VXLAN_FDB_TABLE_LIST" in response:
diff --git a/CLI/clitree/cli-xml/kdump.xml b/CLI/clitree/cli-xml/kdump.xml
index ca6a9f7e4a..cc93a31b10 100644
--- a/CLI/clitree/cli-xml/kdump.xml
+++ b/CLI/clitree/cli-xml/kdump.xml
@@ -61,13 +61,12 @@
sonic# configure terminal
- sonic(config)# enable kdump
- KDUMP configuration has been updated in the startup configuration
- Kdump configuration changes will be applied after the system reboots
+ sonic(config)# kdump enable
+ Kdump configuration changes will be applied after the system reboots.
+ Save SONiC configuration using 'write memory' before issuing the reboot command.
sonic(config)# no kdump
- KDUMP configuration has been updated in the startup configuration
- ALERT! A system reboot is highly recommended.
- Kdump configuration changes will be applied after the system reboots
+ Kdump configuration changes will be applied after the system reboots.
+ Save SONiC configuration using 'write memory' before issuing the reboot command.
KDUMP
@@ -93,9 +92,11 @@
sonic# configure terminal
sonic(config)# kdump memory 512M
- KDUMP configuration has been updated in the startup configuration
- kdump updated memory will be only operational after the system reboots
- sonic(config)# no kdump memory
+ Kdump updated memory will be only operational after the system reboots.
+ Save SONiC configuration using 'write memory' before issuing the reboot command.
+ sonic(config)# no kdump memory
+ Kdump updated memory will be only operational after the system reboots.
+ Save SONiC configuration using 'write memory' before issuing the reboot command.
KDUMP
@@ -190,14 +191,16 @@
- Show the amount of memory reserved for kdump operation.
+ Show the amount of memory reserved and allocated for kdump operation.
- Use this command to show the amount of memory reserved for kdump operation.
+ Use this command to show the configured amount of memory reserved for kdump operation.
+ It also displays the actual memory allocated.
sonic# show kdump memory
Memory Reserved: 0M-2G:256M,2G-4G:256M,4G-8G:384M,8G-:448M
+ Memory Allocated: 448M
KDUMP
diff --git a/CLI/clitree/cli-xml/link_track.xml b/CLI/clitree/cli-xml/link_track.xml
index c2a9f3adec..09f6cf5c7c 100644
--- a/CLI/clitree/cli-xml/link_track.xml
+++ b/CLI/clitree/cli-xml/link_track.xml
@@ -73,6 +73,27 @@ limitations under the License.
+
+ sonic_cli_show_config show_view views=configure-link-state-track view_keys="group=${grp-name}"
+
+
+ Sh
+
+
+ Use this command to display running configurations within current link state track group
+
+
+ sonic(config-link-track)# show configuration
+ link state track track1
+ description mlag
+ downstream all-mclag
+ threshold type percentage up 10
+
+
+
+
+
+
diff --git a/CLI/clitree/cli-xml/mclag.xml b/CLI/clitree/cli-xml/mclag.xml
index 2a0023cb77..d8b5d3ba51 100644
--- a/CLI/clitree/cli-xml/mclag.xml
+++ b/CLI/clitree/cli-xml/mclag.xml
@@ -333,7 +333,51 @@
session-timeout 30
+
+
+
+
+
+
+
+
+
+ sonic_cli_mclag show_mclag_remote_mac show_mclag_remote_mac.j2 show ${__params}
+
+
+ Show command to display MCLAG remote mac count
+
+
+ sonic-cli# show mclag mac remote
+
+
+ sonic-cli# show mclag mac remote
+ ========================================================================
+ Vlan Mac Port Type
+ ========================================================================
+ Vlan300 00:0a:0a:0a:0a:0a PortChannel10 static
+ Vlan400 00:0a:0a:0a:0b:0b Ethernet12 dynamic
+ Vlan400 00:0a:0a:0a:0a:0a PortChannel101 dynamic
+
+
+
+
+
+ sonic_cli_mclag show_mclag_remote_mac show_mclag_remote_mac.j2 count
+
+
+ Show command to display MCLAG remote mac count
+
+
+ sonic-cli# show mclag remote mac count
+
+
+ sonic-cli# show mclag remote mac count
+
+
+
+
diff --git a/CLI/clitree/cli-xml/ospf.xml b/CLI/clitree/cli-xml/ospf.xml
index 8391ccf001..f769706dc7 100644
--- a/CLI/clitree/cli-xml/ospf.xml
+++ b/CLI/clitree/cli-xml/ospf.xml
@@ -19,7 +19,7 @@
-
+
@@ -483,17 +483,39 @@
OSPFv2
-
sonic_cli_ospfv2 patch_openconfig_ospfv2_ext_network_instances_network_instance_protocols_protocol_ospfv2_global_config_ospf_rfc1583_compatible ${vrf-name}
@@ -528,6 +550,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sonic_cli_ospfv2 patch_openconfig_network_instance_network_instances_network_instance_protocols_protocol_ospfv2_global_graceful_restart_config ${vrf-name} ${__full_line}
+
+
+ Configures OSPFv2 Graceful Restart.
+
+
+ Configure Graceful Restart (RFC 3623).
+
+
+ sonic-cli(config-router-ospf)# graceful-restart
+ sonic-cli(config-router-ospf)# graceful-restart grace-time 120
+ sonic-cli(config-router-ospf)# graceful-restart helper enable
+ sonic-cli(config-router-ospf)# graceful-restart helper enable 192.168.1.1
+ sonic-cli(config-router-ospf)# graceful-restart helper strict-lsa-checking
+ sonic-cli(config-router-ospf)# graceful-restart helper planned-only
+ sonic-cli(config-router-ospf)# graceful-restart helper supported-grace-time 120
+
+ OSPFv2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sonic_cli_ospfv2 delete_openconfig_network_instance_network_instances_network_instance_protocols_protocol_ospfv2_global_graceful_restart_config ${vrf-name} ${__full_line}
+
+
+ Unconfigures OSPFv2 Graceful Restart.
+
+
+ Unconfigure Graceful Restart (RFC 3623).
+
+
+ sonic-cli(config-router-ospf)# no graceful-restart
+ sonic-cli(config-router-ospf)# no graceful-restart grace-time
+ sonic-cli(config-router-ospf)# no graceful-restart helper enable
+ sonic-cli(config-router-ospf)# no graceful-restart helper enable 192.168.1.1
+ sonic-cli(config-router-ospf)# no graceful-restart helper strict-lsa-checking
+ sonic-cli(config-router-ospf)# no graceful-restart helper planned-only
+ sonic-cli(config-router-ospf)# no graceful-restart helper supported-grace-time
+
+ OSPFv2
+
+
+
@@ -1385,6 +1485,27 @@
+
+
+
+
+
+
+
+ sonic_cli_ospfv2 gr_prepare_ospf ${__full_line}
+
+
+ OSPFv2 GR
+
+
+ OSPFv2 GR
+
+
+ sonic# graceful-restart prepare ip ospf
+
+
+
+
@@ -1557,6 +1678,14 @@
+
+
+
+
+
+
+
+
sonic_cli_show_ospfv2 show_ospf_cmds ${vrf-name} ${__full_line}
@@ -2051,6 +2180,16 @@ VRF Name: default
Attached Router: 2.2.2.2
+
+ sonic# show ip ospf graceful-restart helper detail
+
+ OSPF Router with ID (192.168.10.2)
+
+ Graceful restart helper support enabled.
+ Strict LSA check is enabled.
+ Helper supported for planned restarts only.
+ Supported Graceful restart interval: 500(in seconds).
+
OSPFv2
@@ -2065,6 +2204,7 @@ VRF Name: default
-->
+
diff --git a/CLI/clitree/cli-xml/sflow.xml b/CLI/clitree/cli-xml/sflow.xml
index 7ebf70aedb..dcd3db0cd2 100644
--- a/CLI/clitree/cli-xml/sflow.xml
+++ b/CLI/clitree/cli-xml/sflow.xml
@@ -120,8 +120,9 @@ limitations under the License.
+
- sonic_cli_sflow patch_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id ${phy-if-name}${vlan-if-name}${loop-if-name}
+ sonic_cli_sflow patch_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id ${phy-if-name}${vlan-if-name}${loop-if-name}${mgmt-if-name}
Configure sFlow agent interface
diff --git a/CLI/clitree/scripts/config_db_data.py b/CLI/clitree/scripts/config_db_data.py
index 0160434480..db8464af7a 100644
--- a/CLI/clitree/scripts/config_db_data.py
+++ b/CLI/clitree/scripts/config_db_data.py
@@ -19,13 +19,14 @@
VIEW_PRIORITY_FILE_MAP = {
"configure-if-view": ["interface.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
"configure-lag-view": ["interface.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
- "configure-vlan-view": ["interface.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
+ "configure-vlan-view": ["interface.xml", "sag.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
"configure-if-mgmt-view": ["interface.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
"configure-lo-view": ["interface.xml", "vrf.xml", "ipv4.xml", "ipv6.xml"],
"configure-vxlan-view": ["vxlan.xml"],
"configure-subif-view": [
"interface.xml",
"subinterface.xml",
+ "sag.xml",
"vrf.xml",
"ipv4.xml",
"ipv6.xml",
diff --git a/CLI/klish/patches/klish-2.1.4/bin/clish.c.diff b/CLI/klish/patches/klish-2.1.4/bin/clish.c.diff
index 037d4b3571..8db2274254 100644
--- a/CLI/klish/patches/klish-2.1.4/bin/clish.c.diff
+++ b/CLI/klish/patches/klish-2.1.4/bin/clish.c.diff
@@ -1,25 +1,39 @@
-41a42,43
+18a19,20
+> #include
+> #include
+41a44,45
> #include
>
-48a51,56
+48a53,58
> bool _nos_use_alt_name = false;
>
> bool nos_use_alt_name() {
> return _nos_use_alt_name;
> }
>
-88a97,101
+54a65
+> char pipestr_file[PATH_MAX];
+88a100,104
> char *mode = getenv("SONIC_CLI_IFACE_MODE");
> if (mode) {
> _nos_use_alt_name = (strcmp(mode, "standard") == 0);
> }
>
-307c320
+307c323
< clish_shell__set_idle_timeout(shell, timeout);
---
> clish_shell__set_timeout(shell, timeout);
-336a350,353
+336a353,356
> if (tmpfd == -1) {
> fprintf(stderr, "Error: Can't dup stdin.\n");
> goto end;
> }
+380a401,408
+>
+> /* Delete pipestr file */
+> snprintf(pipestr_file, sizeof(pipestr_file),
+> "%s-%u", "/tmp/pipestr", getppid());
+> if (access(pipestr_file, F_OK) == 0 && unlink(pipestr_file) < 0) {
+> syslog(LOG_ERR, "Can't remove pipestr-file %s: %s\n",
+> pipestr_file, strerror(errno));
+> }
diff --git a/CLI/renderer/scripts/rpipe_utils.py b/CLI/renderer/scripts/rpipe_utils.py
index 89a17de5bd..c7ca276db6 100644
--- a/CLI/renderer/scripts/rpipe_utils.py
+++ b/CLI/renderer/scripts/rpipe_utils.py
@@ -14,8 +14,7 @@ class pipestr:
"""
def __init__(self):
- pwrec = pwd.getpwuid(os.getuid())
- self.pipestr = "/tmp/pipestr-" + pwrec.pw_name
+ self.pipestr = "/tmp/pipestr-" + str(os.getppid())
def write(self, argv):
pipe_str = ""
diff --git a/CLI/renderer/templates/show_interface_advertise.j2 b/CLI/renderer/templates/show_interface_advertise.j2
index 89dbef8cfb..aab356b8f7 100644
--- a/CLI/renderer/templates/show_interface_advertise.j2
+++ b/CLI/renderer/templates/show_interface_advertise.j2
@@ -15,8 +15,8 @@
{% if 'present' in data %}
{% set present = data['present'] %}
{% endif %}
-{% if present == 'PRESENT' and 'openconfig-platform-ext:display-name' in data %}
-{% set type = data['openconfig-platform-ext:display-name'] %}
+{% if present == 'PRESENT' and 'display-name' in data %}
+{% set type = data['display-name'] %}
{% endif %}
{% endif %}
diff --git a/CLI/renderer/templates/show_interface_advertise_one.j2 b/CLI/renderer/templates/show_interface_advertise_one.j2
index 550ed111c9..ff977c66fb 100644
--- a/CLI/renderer/templates/show_interface_advertise_one.j2
+++ b/CLI/renderer/templates/show_interface_advertise_one.j2
@@ -13,8 +13,8 @@
{% if 'present' in data %}
{% set present = data['present'] %}
{% endif %}
-{% if present == 'PRESENT' and 'openconfig-platform-ext:display-name' in data %}
-{% set type = data['openconfig-platform-ext:display-name'] %}
+{% if present == 'PRESENT' and 'display-name' in data %}
+{% set type = data['display-name'] %}
{% endif %}
{% endif %}
diff --git a/CLI/renderer/templates/show_interface_link_training.j2 b/CLI/renderer/templates/show_interface_link_training.j2
index 358780af8f..97788ffd0b 100644
--- a/CLI/renderer/templates/show_interface_link_training.j2
+++ b/CLI/renderer/templates/show_interface_link_training.j2
@@ -15,8 +15,8 @@
{% if 'present' in data %}
{% set present = data['present'] %}
{% endif %}
-{% if present == 'PRESENT' and 'openconfig-platform-ext:display-name' in data %}
-{% set type = data['openconfig-platform-ext:display-name'] %}
+{% if present == 'PRESENT' and 'display-name' in data %}
+{% set type = data['display-name'] %}
{% endif %}
{% endif %}
diff --git a/CLI/renderer/templates/show_ip_ospf_graceful_restart_helper.j2 b/CLI/renderer/templates/show_ip_ospf_graceful_restart_helper.j2
new file mode 100644
index 0000000000..a76edc1f2c
--- /dev/null
+++ b/CLI/renderer/templates/show_ip_ospf_graceful_restart_helper.j2
@@ -0,0 +1,84 @@
+{% set vars = {'vrfname': "False", 'helper': "Graceful restart helper support disabled.", 'planned_only': "Helper supported for Planned and Unplanned Restarts.", 'strict_lsa_checking': "Strict LSA check is enabled.", 'supported_interval': "1800", 'neighbors': "", 'detail': "False", 'active_restart_count': "0", 'last_exit_reason':"", 'cnt': 0} %}
+{% set ospf_global_list = json_output %}
+{% for list_elem in ospf_global_list %}
+ {% if 'vrfName' in list_elem %}
+ {% if vars.update({'vrfname':list_elem['vrfName']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'vrfName' in list_elem and list_elem['vrfName'] != 'default' %}
+ {% if vars.update({'vrfname':list_elem['vrfName']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'flagDetail' in list_elem %}
+ {% if vars.update({'detail':list_elem['flagDetail']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:global_state' in list_elem %}
+ {% if 'router-id' in list_elem['openconfig-network-instance:global_state'] %}
+ {% if vars.update({'router_id':list_elem['openconfig-network-instance:global_state' ]['router-id']}) %}{% endif -%}
+ {% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'helper-only' in list_elem['openconfig-network-instance:graceful_restart_state'] and list_elem['openconfig-network-instance:graceful_restart_state']['helper-only'] == True %}
+ {% if vars.update({'helper':'Graceful restart helper support enabled.'}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'openconfig-ospfv2-ext:strict-lsa-checking' in list_elem['openconfig-network-instance:graceful_restart_state'] and list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:strict-lsa-checking'] == True %}
+ {% if vars.update({'strict_lsa_checking':'Strict LSA check is enabled.'}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'openconfig-ospfv2-ext:planned-only' in list_elem['openconfig-network-instance:graceful_restart_state'] and list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:planned-only'] == True %}
+ {% if vars.update({'planned_only':'Helper supported for planned restarts only.'}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'openconfig-ospfv2-ext:supported-grace-time' in list_elem['openconfig-network-instance:graceful_restart_state'] %}
+ {% if vars.update({'supported_interval':list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:supported-grace-time']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'openconfig-ospfv2-ext:gr-helper-active-restarter-count' in list_elem['openconfig-network-instance:graceful_restart_state'] and list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:gr-helper-active-restarter-count'] != 0 %}
+ {% if vars.update({'active_restart_count':list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:gr-helper-active-restarter-count']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:graceful_restart_state' in list_elem and 'openconfig-ospfv2-ext:gr-last-exit-reason' in list_elem['openconfig-network-instance:graceful_restart_state'] and list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:gr-last-exit-reason'] != "" %}
+ {% if vars.update({'last_exit_reason':list_elem['openconfig-network-instance:graceful_restart_state']['openconfig-ospfv2-ext:gr-last-exit-reason']}) %}{% endif -%}
+ {% endif -%}
+ {% if 'openconfig-network-instance:gr_helper_neighbors' in list_elem %}
+ {% if vars.update({'neighbors':list_elem['openconfig-network-instance:gr_helper_neighbors']}) %}{% endif -%}
+ {% endif -%}
+{% endfor -%}
+{{' '}}VRF Name: {{vars.vrfname}}
+{{' '}}OSPF Router with ID ({{vars.router_id}})
+{{' '}}{{vars.helper}}
+{{' '}}{{vars.strict_lsa_checking}}
+{{' '}}{{vars.planned_only}}
+{{' '}}Supported Graceful restart interval: {{vars.supported_interval}}(in seconds).
+{{' '}}Enable Router List:
+{{' '}}{{vars.neighbors}}
+{% if vars.detail == True %}
+ {% if vars.last_exit_reason != "" %}
+ {{' '}}Last Helper exit Reason: {{vars.last_exit_reason}}
+ {% endif -%}
+ {% if vars.active_restart_count != '0' %}
+ {{' '}}Number of Active neighbours in graceful restart: {{vars.active_restart_count}}
+ {% for list_elem in ospf_global_list %}
+ {% if 'openconfig-network-instance:areas' in list_elem and 'area' in list_elem['openconfig-network-instance:areas'] %}
+ {% set area_list =list_elem['openconfig-network-instance:areas']['area'] %}
+ {% endif -%}
+ {% for areainfo in area_list %}
+ {% if 'interfaces' in areainfo and 'interface' in areainfo['interfaces'] %}
+ {% set interface_list = areainfo['interfaces']['interface'] %}
+ {% for interfaceinfo in interface_list %}
+ {% if 'openconfig-ospfv2-ext:neighbours' in interfaceinfo and 'neighbour' in interfaceinfo['openconfig-ospfv2-ext:neighbours'] %}
+ {% set neighbor_list = interfaceinfo['openconfig-ospfv2-ext:neighbours']['neighbour'] %}
+ {% for neighborinfo in neighbor_list %}
+ {% if 'state' in neighborinfo %}
+ {% if 'gr-active-helper' in neighborinfo['state'] and neighborinfo['state']['gr-active-helper'] == True %}
+ {% if vars.update({'cnt': vars.cnt + 1}) %}{% endif -%}
+{{' '}}Neighbour {{vars.cnt}}:
+{{' '}}Address: {{neighborinfo['neighbor-address']}}
+{{' '}}Routerid: {{neighborinfo['neighbor-id']}}
+{{' '}}Received Grace period: {{neighborinfo['state']['gr-grace-interval']}}(in seconds).
+{{' '}}Actual Grace period: {{neighborinfo['state']['gr-actual-grace-interval']}}(in seconds).
+{{' '}}Remaining GraceTime: {{neighborinfo['state']['gr-remaining-grace-interval']}}(in seconds).
+{{' '}}Graceful Restart reason: {{neighborinfo['state']['gr-restart-reason']}}.
+ {% endif -%}
+ {% endif -%}
+ {% endfor -%}
+ {% endif -%}
+ {% endfor -%}
+ {% endif -%}
+ {% endfor -%}
+ {% endfor -%}
+ {% endif -%}
+{% endif -%}
diff --git a/CLI/renderer/templates/show_ip_ospf_neighbor_detail.j2 b/CLI/renderer/templates/show_ip_ospf_neighbor_detail.j2
index 9b5196df1e..da4d742f76 100644
--- a/CLI/renderer/templates/show_ip_ospf_neighbor_detail.j2
+++ b/CLI/renderer/templates/show_ip_ospf_neighbor_detail.j2
@@ -72,6 +72,20 @@ VRF Name: {{list_elem['vrfName']}}
Thread Link State Update Retransmission on
{% else %}
Thread Link State Update Retransmission off
+{% endif %}
+ Graceful restart Helper info:
+ Graceful Restart HELPER Status: {{neighborinfo['state']['gr-helper-status']}}
+{% if 'gr-grace-interval' in neighborinfo ['state'] %}
+ Graceful Restart grace period time: {{neighborinfo['state']['gr-grace-interval']}} (seconds).
+{% endif %}
+{% if 'gr-restart-reason' in neighborinfo ['state'] %}
+ Graceful Restart reason: {{neighborinfo['state']['gr-restart-reason']}}
+{% endif %}
+{% if 'gr-helper-reject-reason' in neighborinfo ['state'] %}
+ Graceful Restart HELPER Reject Reason: {{neighborinfo['state']['gr-helper-reject-reason']}}
+{% endif %}
+{% if 'gr-helper-exit-reason' in neighborinfo ['state'] %}
+ Graceful Restart HELPER Exit Reason: {{neighborinfo['state']['gr-helper-exit-reason']}}
{% endif %}
{% if 'bfd-state' in neighborinfo['state']%}
{{' '}}
diff --git a/CLI/renderer/templates/show_mclag_remote_mac.j2 b/CLI/renderer/templates/show_mclag_remote_mac.j2
new file mode 100644
index 0000000000..8c3183c5d0
--- /dev/null
+++ b/CLI/renderer/templates/show_mclag_remote_mac.j2
@@ -0,0 +1,17 @@
+{% set count = namespace(value=0) %}
+
+{{"========================================================================"}}
+{{"Vlan".center(9)}} {{"Mac".center(20)}} {{"Port".center(15)}} {{"Type".center(9)}}
+{{"========================================================================"}}
+
+{% if json_output %}
+
+{% for fdblist in json_output['sonic-mclag:MCLAG_FDB_TABLE_LIST']%}
+{{fdblist['vlan'].center(9)}} {{fdblist['mac_addr'].center(20)}} {{fdblist['port'].center(15)}} {{fdblist['type'].center(9)}}
+{% set count.value = count.value + 1 %}
+{% endfor %}
+
+{{'\nTotal count : %4s\n'|format(count.value)}}
+{% endif %}
+
+
diff --git a/CLI/renderer/templates/show_sflow.j2 b/CLI/renderer/templates/show_sflow.j2
index 83f22a8765..3f4f4f4fa9 100755
--- a/CLI/renderer/templates/show_sflow.j2
+++ b/CLI/renderer/templates/show_sflow.j2
@@ -20,6 +20,9 @@
{% endif %}
{% if 'agent' in sfl_gbl %}
+{% if sfl_gbl['agent'] == 'eth0' %}
+ {% do sfl_gbl.update({'agent': "Management 0"}) %}
+{% endif %}
{% if vars.update({'agentId' : sfl_gbl['agent']}) %}{% endif %}
{% else %}
{% if vars.update({'agentId' : "default"}) %} {% endif %}
diff --git a/go.mod b/go.mod
index 91c7747b73..073f189ed3 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@ require (
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/gorilla/mux v1.7.4
github.com/leodido/go-urn v1.2.0 // indirect
- github.com/pkg/profile v1.4.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
diff --git a/rest/Makefile b/rest/Makefile
index 374913e02c..95693b3c53 100644
--- a/rest/Makefile
+++ b/rest/Makefile
@@ -73,8 +73,7 @@ all: $(DEFAULT_TARGETS)
# REST Server binary
%/rest/rest_server: $(REST_SRCS) $$($$(*F)_GEN_SRCS) | $$(@D)/.
ifeq ($(SONIC_COVERAGE_ON),y)
- touch $(@D)/.test
- $(GO) test -mod=vendor -tags=glog_defaults,$(*F)_pkg -coverpkg="./..." -c -o $@ ../rest/main
+ $(GO) test -mod=vendor -tags=glog_defaults,$(*F)_pkg -coverpkg="github.com/Azure/..." -c -o $@ ../rest/main
else
$(GO) build -o=$@ -mod=vendor -tags=glog_defaults,$(*F)_pkg $(GCFLAGS) -v ../rest/main
endif
diff --git a/rest/main/main.go b/rest/main/main.go
index e8f4b63239..128765ffb6 100644
--- a/rest/main/main.go
+++ b/rest/main/main.go
@@ -28,15 +28,11 @@ import (
"net"
"net/http"
"os"
- "os/signal"
- "syscall"
"time"
"github.com/Azure/sonic-mgmt-common/translib/db"
"github.com/Azure/sonic-mgmt-framework/rest/server"
-
"github.com/golang/glog"
- "github.com/pkg/profile"
)
// Command line parameters
@@ -47,13 +43,16 @@ var (
caFile string // Client CA certificate file path
noSocket bool // Do not start unix domain socket lister
internal bool // Enable internal features on https listener
- profDir string // CPU profiler reports directory
clientAuth = server.NewUserAuth()
funnel server.RequestFunnel // Controls number of concurrent requests
// readTimeout is the max duration for reading the entire request (header+body).
// Default value is 30 seconds.. Value 0 means no timeout.
readTimeout time.Duration = 30 * time.Second
+
+ // apiTimeout is the timeout hint for REST APIs, passed through the context.
+ // It does not include readTimeout; but includes the wait time in RequestFunnel.
+ apiTimeout time.Duration = 0
)
const (
@@ -70,9 +69,13 @@ func init() {
flag.Var(clientAuth, "client_auth", "Client auth mode(s) - default: password,jwt")
flag.BoolVar(&noSocket, "no-sock", false, "Do not start unix domain socket listener")
flag.BoolVar(&internal, "internal", false, "Enable internal, non-standard features on https listener")
- flag.StringVar(&profDir, "prof_dir", "", "CPU profiler reports directory")
flag.UintVar(&funnel.Limit, "reqlimit", 0, "Max concurrent requests allowed")
flag.DurationVar(&readTimeout, "readtimeout", readTimeout, "Maximum duration for reading entire request")
+ flag.DurationVar(&apiTimeout, "apitimeout", apiTimeout, "REST API timeout; value 0 disables it")
+}
+
+// Start REST server
+func main() {
flag.Parse()
@@ -88,34 +91,6 @@ func init() {
clientAuth = server.UserAuth{"password": true, "cert": false, "jwt": true}
}
-}
-
-// theProfiler points to the currently active cpu profiler instance
-var theProfiler interface{ Stop() }
-
-// Start REST server
-func main() {
-
- /* Enable profiling by default. Send SIGUSR1 signal to rest_server to
- * stop profiling and save data to /tmp/profile/cpu.pprof file.
- * Copy over the cpu.pprof file and rest_server to a Linux host and run
- * any of the following commands to generate a report in needed format.
- * go tool pprof --txt ./rest_server ./cpu.pprof > report.txt
- * go tool pprof --pdf ./rest_server ./cpu.pprof > report.pdf
- * Note: install graphviz to generate the graph on a pdf format
- */
- startProfiler()
- defer stopProfiler()
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGUSR1)
- go func() {
- for {
- <-sigs
- stopProfiler()
- startProfiler()
- }
- }()
-
if caFile == "" && clientAuth.Enabled("cert") {
clientAuth.Unset("cert")
glog.Warning("Must specify -cacert with -client_auth cert")
@@ -160,6 +135,7 @@ func main() {
glog.Infof("Read timeout = %v", readTimeout)
glog.Infof("Concurrent request limit = %v", funnel.Limit)
+ glog.Infof("Authentication modes = %v", clientAuth)
go check_if_rest_server_up(address, &tlsConfig)
@@ -217,6 +193,7 @@ func newRouter(cfg *server.RouterConfig) http.Handler {
if funnel.Limit > 0 {
r = funnel.Wrap(r)
}
+ r = server.WithTimeout(r, &apiTimeout)
return r
}
@@ -302,60 +279,6 @@ func getPreferredCipherSuites() []uint16 {
}
}
-// startProfiler starts a cpu profiler.
-func startProfiler() {
- // Backup previous profiler reports in profDir. Creates a new
- // temporary reports directory if profDir not specified.
- if profDir != "" {
- rotateFile(profDir+"/cpu.pprof", 5)
- } else {
- profDir = createTempProfileDir()
- }
-
- var opts []func(*profile.Profile)
- opts = append(opts, func(p *profile.Profile) { profile.Quiet(p) })
- opts = append(opts, profile.ProfilePath(profDir))
-
- glog.Infof("Starting profiler.. reports dir %s", profDir)
- theProfiler = profile.Start(opts...)
-}
-
-// stopProfiler stops the cpu profiler.
-func stopProfiler() {
- if theProfiler != nil {
- theProfiler.Stop()
- glog.Infof("Profiler stopped")
- theProfiler = nil
- }
-}
-
-func createTempProfileDir() string {
- path, err := ioutil.TempDir(os.TempDir(), "profile")
- if err != nil {
- glog.Warning("Failed to create temp dir!", err)
- path = os.TempDir()
- }
-
- return path
-}
-
-func rotateFile(file string, count int) {
- info, err := os.Stat(file)
- if err != nil {
- return // file does not exists.. nothing to do
- }
- if info.Size() == 0 {
- os.Remove(file) // remove empty file
- return
- }
- for ; count > 1; count-- {
- os.Rename(fmt.Sprintf("%s.%d", file, count-1), fmt.Sprintf("%s.%d", file, count))
- }
- if count == 1 {
- os.Rename(file, file+".1")
- }
-}
-
func check_if_rest_server_up(address string, serverConfig *tls.Config) {
glog.Infof("Checking if rest_server started on %s", address)
clientConfig := tls.Config{
diff --git a/rest/main/main_test.go b/rest/main/main_test.go
index f42847814d..080795c873 100644
--- a/rest/main/main_test.go
+++ b/rest/main/main_test.go
@@ -20,18 +20,43 @@
package main
import (
+ "flag"
"fmt"
"os"
"os/signal"
+ "path/filepath"
"syscall"
"testing"
)
-func TestMain(t *testing.T) {
+func TestMain(m *testing.M) {
+ // Update default test.coverprofile flag value as "/var/log/rest_server/coverage.out".
+ // Command line argument will overwrite it, if specified.
+ if f := flag.Lookup("test.coverprofile"); f != nil {
+ exeName := filepath.Base(os.Args[0])
+ coverFile := filepath.Join("/var/log", exeName, "coverage.out")
+ f.DefValue = coverFile
+ f.Value.Set(coverFile)
+ }
+
+ // -test.xxx flags will not be available if flag.Parse() was called by an init function.
+ // Everything will work; but coverage report path cannot be customized.
+ if flag.Parsed() {
+ fmt.Println(">>>> WARNING: flag.Parse() called too early. Test flags will not be available.")
+ }
+
+ os.Exit(m.Run())
+}
+
+func Test_main(t *testing.T) {
go main()
sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGUSR1)
+ signal.Notify(sigs, syscall.SIGHUP)
fmt.Println("Listening on sig kill from TestMain")
<-sigs
fmt.Println("Returning from TestMain on sig kill")
+
+ if f := flag.Lookup("test.coverprofile"); f != nil {
+ fmt.Println("Coverage report file:", f.Value)
+ }
}
diff --git a/rest/server/debug.go b/rest/server/debug.go
index 6d4608c80c..f2916d0f11 100644
--- a/rest/server/debug.go
+++ b/rest/server/debug.go
@@ -25,10 +25,31 @@ import (
"net/http"
"reflect"
"sort"
+
+ "github.com/Azure/sonic-mgmt-common/profile"
+ "github.com/Azure/sonic-mgmt-common/translib"
+ "github.com/Azure/sonic-mgmt-common/translib/tlerr"
)
func init() {
AddRoute("getRoutes", "GET", "/debug/rest-routes", getRoutesHandler, nil)
+
+ // Handlers for go profilers
+
+ startCpu := startProfileHandler(profile.CPU_PROFILE)
+ AddRoute("startProf_cpu", "POST", "/debug/cpu-profile-start", startCpu, nil)
+
+ collectCpu := collectProfileHandler(profile.CPU_PROFILE)
+ AddRoute("cpuProf", "POST", "/debug/cpu-profile", collectCpu, nil)
+ AddRoute("cpuProf_", "GET", "/debug/cpu-profile", collectCpu, nil)
+
+ collectHeap := collectProfileHandler(profile.HEAP_PROFILE)
+ AddRoute("memProf", "POST", "/debug/heap-profile", collectHeap, nil)
+ AddRoute("memProf_", "GET", "/debug/heap-profile", collectHeap, nil)
+
+ collectBt := collectProfileHandler(profile.GOROUTINE_PROFILE)
+ AddRoute("goroutineProf", "POST", "/debug/goroutines", collectBt, nil)
+ AddRoute("goroutineProf_", "GET", "/debug/goroutines", collectBt, nil)
}
func getRoutesHandler(w http.ResponseWriter, r *http.Request) {
@@ -72,3 +93,44 @@ func sortedMapKeys(m interface{}) []string {
sort.Strings(keys)
return keys
}
+
+func startProfileHandler(t profile.Type) http.HandlerFunc {
+ h := func(w http.ResponseWriter, r *http.Request) {
+ err := profile.Start(t)
+ if err != nil {
+ writeErrorResponse(w, r, err)
+ }
+ }
+ return authorizeActionMiddleware("Debug/StartProfile", h)
+}
+
+func collectProfileHandler(t profile.Type) http.HandlerFunc {
+ h := func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/octet-stream")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ err := profile.Collect(t, w)
+ if err != nil {
+ writeErrorResponse(w, r, err)
+ }
+ }
+ return authorizeActionMiddleware("Debug/CollectProfile", h)
+}
+
+// authorizeActionMiddleware authorizes the debug actions that do not invoke
+// translib Get/Set APIs. Action name is treated as the resource path.
+// Typically, only admins are allowed to perform these actions.
+func authorizeActionMiddleware(name string, inner http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ rc, r := GetContext(r)
+ aReq := translib.ActionRequest{
+ Path: name,
+ User: translib.UserRoles{Name: rc.Auth.User, Roles: rc.Auth.Roles},
+ AuthEnabled: rc.ClientAuth.Any(),
+ }
+ if translib.IsAuthorizedForAction(aReq) {
+ inner(w, r)
+ } else {
+ writeErrorResponse(w, r, tlerr.AuthorizationError{Path: name})
+ }
+ }
+}
diff --git a/rest/server/error.go b/rest/server/error.go
index 44cf8d59bc..711c93b32e 100644
--- a/rest/server/error.go
+++ b/rest/server/error.go
@@ -20,6 +20,7 @@
package server
import (
+ "context"
"encoding/json"
"fmt"
"net/http"
@@ -54,6 +55,7 @@ const (
// error-type values
errtypeProtocol errtype = "protocol"
errtypeApplication errtype = "application"
+ errtypeRpc errtype = "rpc"
// error-tag values
errtagInvalidValue errtag = "invalid-value"
@@ -116,8 +118,6 @@ func prepareErrorResponse(err error, r *http.Request) (status int, data []byte,
// toErrorEntry translates an error object into HTTP status and an
// errorEntry object.
func toErrorEntry(err error, r *http.Request) (status int, errInfo errorEntry) {
- // By default everything is 500 Internal Server Error
- status = http.StatusInternalServerError
errInfo.Type = errtypeApplication
errInfo.Tag = errtagOperationFailed
@@ -199,6 +199,7 @@ func toErrorEntry(err error, r *http.Request) (status int, errInfo errorEntry) {
errInfo.Message = "Transaction failed. Please try again."
case tlerr.InternalError:
+ status = http.StatusInternalServerError
errInfo.Message = e.Error()
errInfo.Path = e.Path
errInfo.AppTag = e.AppTag
@@ -238,6 +239,26 @@ func toErrorEntry(err error, r *http.Request) (status int, errInfo errorEntry) {
errInfo.AppTag = e.AppTag
errInfo.Tag = errtagAccessDenied
+ case tlerr.RequestContextCancelledError:
+ err = e.CtxError
+ }
+
+ if status == 0 {
+ switch {
+ case err == context.Canceled:
+ // Won't be sent back to client; but used in log message!
+ status = http.StatusBadRequest
+ errInfo.Type = errtypeRpc
+ errInfo.Message = "Cancelled"
+
+ case err == context.DeadlineExceeded:
+ status = http.StatusServiceUnavailable
+ errInfo.Type = errtypeRpc
+ errInfo.Message = "Timed out"
+
+ default:
+ status = http.StatusInternalServerError
+ }
}
return
diff --git a/rest/server/error_test.go b/rest/server/error_test.go
index 8b991f01f3..de7f35de26 100644
--- a/rest/server/error_test.go
+++ b/rest/server/error_test.go
@@ -20,6 +20,7 @@
package server
import (
+ "context"
"errors"
"fmt"
"strings"
@@ -186,6 +187,24 @@ func TestErrorEntry(t *testing.T) {
ClientVersion: "1.2.3", ServerVersion: "1.2.0", ServerBaseVersion: "1.0.0"},
400, "protocol", "operation-not-supported", "", "Unsupported client version 1.2.3"))
+ // context errors
+
+ t.Run("Ctx_cancel", testErrorEntry(
+ context.Canceled,
+ 400, "rpc", "operation-failed", "", "*"))
+
+ t.Run("Ctx_timeout", testErrorEntry(
+ context.DeadlineExceeded,
+ 503, "rpc", "operation-failed", "", "*"))
+
+ t.Run("Ctx_wrap_cancel", testErrorEntry(
+ tlerr.RequestContextCancelled("Context error", context.Canceled),
+ 400, "rpc", "operation-failed", "", "*"))
+
+ t.Run("Ctx_wrap_timeout", testErrorEntry(
+ tlerr.RequestContextCancelled("Context error", context.DeadlineExceeded),
+ 503, "rpc", "operation-failed", "", "*"))
+
}
func testErrorEntry(err error,
diff --git a/rest/server/handler.go b/rest/server/handler.go
index 263c50bc0f..7d3cb0c38e 100644
--- a/rest/server/handler.go
+++ b/rest/server/handler.go
@@ -20,6 +20,7 @@
package server
import (
+ "context"
"fmt"
"io/ioutil"
"log/syslog"
@@ -51,6 +52,7 @@ func Process(w http.ResponseWriter, r *http.Request) {
reqID: reqID,
AuthEnabled: rc.ClientAuth.Any(),
User: translib.UserRoles{Name: rc.Auth.User, Roles: rc.Auth.Roles},
+ Ctx: r.Context(),
}
var err error
@@ -340,6 +342,7 @@ type translibArgs struct {
deleteEmpty bool // Delete empty entry during field delete
AuthEnabled bool // Enable Authorization
User translib.UserRoles // User and role info for RBAC
+ Ctx context.Context
}
// invokeTranslib calls appropriate TransLib API for the given HTTP
@@ -363,6 +366,7 @@ func invokeTranslib(args *translibArgs, rc *RequestContext) (int, []byte, error)
Content: args.content,
Fields: args.fields,
},
+ Ctxt: args.Ctx,
}
resp, err1 := translib.Get(req)
diff --git a/rest/server/rate_limit.go b/rest/server/rate_limit.go
index bffeffaa02..7bb530352f 100644
--- a/rest/server/rate_limit.go
+++ b/rest/server/rate_limit.go
@@ -20,7 +20,10 @@
package server
import (
+ "context"
"net/http"
+ "sync/atomic"
+ "time"
)
// RequestFunnel controls the number of concurrent requests processed by
@@ -30,7 +33,7 @@ type RequestFunnel struct {
slots chan struct{} // empty slots
}
-// Wrap returns a http.Handler which applies request funnelling
+// Wrap returns a http.HandlerFunc which applies request funnelling
// to a given inner http.Handler. One RequestFunnel can wrap multiple
// handlers, in which case they all share the same limit.
func (rf *RequestFunnel) Wrap(inner http.Handler) http.HandlerFunc {
@@ -39,8 +42,30 @@ func (rf *RequestFunnel) Wrap(inner http.Handler) http.HandlerFunc {
}
return func(w http.ResponseWriter, r *http.Request) {
- rf.slots <- struct{}{}
- defer func() { <-rf.slots }()
+ ctx := r.Context()
+ select {
+ case rf.slots <- struct{}{}:
+ defer func() { <-rf.slots }()
+ inner.ServeHTTP(w, r)
+ case <-ctx.Done():
+ writeErrorResponse(w, r, ctx.Err())
+ }
+ }
+}
+
+// WithTimeout creates a http.HandlerFunc which wraps a http.Handler to add context timeouts.
+// It has no effect if the timeout duration is <= 0. Changes to this duration will reflect in
+// the subsequent requests.
+// Unlike the standard library http.TimeoutHandler, it does not write a response immediately
+// after timeout. It expects the inner Handler to manage context timeouts.
+func WithTimeout(inner http.Handler, timeout *time.Duration) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ t := atomic.LoadInt64((*int64)(timeout))
+ if t > 0 {
+ ctx, cancel := context.WithTimeout(r.Context(), time.Duration(t))
+ defer cancel()
+ r = r.WithContext(ctx)
+ }
inner.ServeHTTP(w, r)
}
}
diff --git a/rest/server/rate_limit_test.go b/rest/server/rate_limit_test.go
index a96eb04711..a716052317 100644
--- a/rest/server/rate_limit_test.go
+++ b/rest/server/rate_limit_test.go
@@ -110,3 +110,70 @@ func TestReqFunnel_mix(t *testing.T) {
f := RequestFunnel{Limit: 2}
runReqFunnelTests(t, &f, 50, time.Second, 71*time.Millisecond, 321*time.Millisecond)
}
+
+// timeoutTest holds input and state info for API timeout tests
+type timeoutTest struct {
+ Timeout time.Duration
+ Handler http.Handler
+ _h http.Handler // internal state, not to be set by test case
+}
+
+func (test *timeoutTest) Run(t *testing.T, expStatus int, expDelay time.Duration) {
+ if test._h == nil {
+ test._h = WithTimeout(test.Handler, &test.Timeout)
+ }
+
+ w := httptest.NewRecorder()
+ t0 := time.Now()
+ test._h.ServeHTTP(w, httptest.NewRequest("GET", "/test", nil))
+ delay := time.Since(t0)
+ if delay.Round(500*time.Millisecond) != expDelay {
+ t.Errorf("Expected delay %v; actual %v", expDelay, delay)
+ }
+ if w.Code != expStatus {
+ t.Errorf("Expected status %d; got %d", expStatus, w.Code)
+ }
+}
+
+// timeoutTestHandler returns status 200 after waiting for 1s. Returns status 503 if
+// context timeout is reached earlier.
+var timeoutTestHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
+ select {
+ case <-time.After(time.Second):
+ w.WriteHeader(http.StatusOK)
+ case <-r.Context().Done():
+ w.WriteHeader(http.StatusServiceUnavailable)
+ }
+}
+
+func TestApiTimeout_0(t *testing.T) {
+ test := timeoutTest{Timeout: 0, Handler: timeoutTestHandler}
+ test.Run(t, http.StatusOK, time.Second)
+}
+
+func TestApiTimeout_fast(t *testing.T) {
+ test := timeoutTest{Timeout: 2 * time.Second, Handler: timeoutTestHandler}
+ test.Run(t, http.StatusOK, time.Second)
+}
+
+func TestApiTimeout_slow(t *testing.T) {
+ test := timeoutTest{Timeout: 500 * time.Millisecond, Handler: timeoutTestHandler}
+ test.Run(t, http.StatusServiceUnavailable, test.Timeout)
+}
+
+func TestApiTimeout_update(t *testing.T) {
+ test := timeoutTest{Timeout: 0, Handler: timeoutTestHandler}
+ test.Run(t, http.StatusOK, time.Second)
+ test.Timeout = 500 * time.Millisecond
+ test.Run(t, http.StatusServiceUnavailable, test.Timeout)
+ test.Timeout = 2 * time.Second
+ test.Run(t, http.StatusOK, time.Second)
+}
+
+func TestApiTimeout_ignore(t *testing.T) {
+ var h http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(2 * time.Second)
+ }
+ test := timeoutTest{Timeout: time.Second, Handler: h}
+ test.Run(t, http.StatusOK, 2*time.Second)
+}
diff --git a/tools/mock/sonic_py_common/device_info.py b/tools/mock/sonic_py_common/device_info.py
index 9e8e952db4..428e2a485d 100644
--- a/tools/mock/sonic_py_common/device_info.py
+++ b/tools/mock/sonic_py_common/device_info.py
@@ -32,5 +32,11 @@ def __has_feature(name):
b_info = get_sonic_branding_info()
if b_info is None or "features" not in b_info:
return True
+ name = to_feature_name(name)
f_list = b_info["features"]
return f_list is not None and name in f_list
+
+
+def to_feature_name(name):
+ f_name = name.upper()
+ return f_name[len("FEATURE_"):] if f_name.startswith("FEATURE_") else f_name