1414import time
1515import itertools
1616import copy
17+ import tempfile
1718
1819from jsonpatch import JsonPatchConflict
1920from jsonpointer import JsonPointerException
3334from utilities_common .intf_filter import parse_interface_in_filter
3435from utilities_common import bgp_util
3536import utilities_common .cli as clicommon
36- from utilities_common .helper import get_port_pbh_binding , get_port_acl_binding
37+ from utilities_common .helper import get_port_pbh_binding , get_port_acl_binding , update_config
3738from utilities_common .general import load_db_config , load_module_from_source
3839from .validated_config_db_connector import ValidatedConfigDBConnector
3940import utilities_common .multi_asic as multi_asic_util
@@ -142,6 +143,14 @@ def read_json_file(fileName):
142143 raise Exception (str (e ))
143144 return result
144145
146+ # write given JSON file
147+ def write_json_file (json_input , fileName ):
148+ try :
149+ with open (fileName , 'w' ) as f :
150+ json .dump (json_input , f , indent = 4 )
151+ except Exception as e :
152+ raise Exception (str (e ))
153+
145154def _get_breakout_options (ctx , args , incomplete ):
146155 """ Provides dynamic mode option as per user argument i.e. interface name """
147156 all_mode_options = []
@@ -1525,6 +1534,12 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, force, file_form
15251534 # Get the file from user input, else take the default file /etc/sonic/config_db{NS_id}.json
15261535 if cfg_files :
15271536 file = cfg_files [inst + 1 ]
1537+ # Save to tmpfile in case of stdin input which can only be read once
1538+ if file == "/dev/stdin" :
1539+ file_input = read_json_file (file )
1540+ (_ , tmpfname ) = tempfile .mkstemp (dir = "/tmp" , suffix = "_configReloadStdin" )
1541+ write_json_file (file_input , tmpfname )
1542+ file = tmpfname
15281543 else :
15291544 if file_format == 'config_db' :
15301545 if namespace is None :
@@ -1540,6 +1555,19 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, force, file_form
15401555 click .echo ("The config file {} doesn't exist" .format (file ))
15411556 continue
15421557
1558+ if file_format == 'config_db' :
1559+ file_input = read_json_file (file )
1560+
1561+ platform = file_input .get ("DEVICE_METADATA" , {}).\
1562+ get ("localhost" , {}).get ("platform" )
1563+ mac = file_input .get ("DEVICE_METADATA" , {}).\
1564+ get ("localhost" , {}).get ("mac" )
1565+
1566+ if not platform or not mac :
1567+ log .log_warning ("Input file does't have platform or mac. platform: {}, mac: {}"
1568+ .format (None if platform is None else platform , None if mac is None else mac ))
1569+ load_sysinfo = True
1570+
15431571 if load_sysinfo :
15441572 try :
15451573 command = [SONIC_CFGGEN_PATH , "-j" , file , '-v' , "DEVICE_METADATA.localhost.hwsku" ]
@@ -1598,6 +1626,13 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, force, file_form
15981626 clicommon .run_command (command , display_cmd = True )
15991627 client .set (config_db .INIT_INDICATOR , 1 )
16001628
1629+ if os .path .exists (file ) and file .endswith ("_configReloadStdin" ):
1630+ # Remove tmpfile
1631+ try :
1632+ os .remove (file )
1633+ except OSError as e :
1634+ click .echo ("An error occurred while removing the temporary file: {}" .format (str (e )), err = True )
1635+
16011636 # Migrate DB contents to latest version
16021637 db_migrator = '/usr/local/bin/db_migrator.py'
16031638 if os .path .isfile (db_migrator ) and os .access (db_migrator , os .X_OK ):
@@ -1907,14 +1942,15 @@ def override_config_table(db, input_config_db, dry_run):
19071942 if ns in config_input .keys ():
19081943 ns_config_input = config_input [ns ]
19091944 else :
1910- click .secho ( "Wrong config format! {} not found in asic config! cannot override.. abort " .format (ns ))
1911- sys . exit ( 1 )
1945+ click .echo ( "Override config not present for {} " .format (ns ))
1946+ continue
19121947 if not ns_config_input :
19131948 # if ns_config_input is not defined, define it
19141949 # it could be single-asic dut, or config_input is empty
19151950 ns_config_input = config_input
19161951 # Generate sysinfo if missing in ns_config_input
19171952 generate_sysinfo (current_config , ns_config_input , ns )
1953+ # Use deepcopy by default to avoid modifying input config
19181954 updated_config = update_config (current_config , ns_config_input )
19191955
19201956 yang_enabled = device_info .is_yang_config_validation_enabled (config_db )
@@ -1950,14 +1986,6 @@ def validate_config_by_cm(cm, config_json, jname):
19501986 sys .exit (1 )
19511987
19521988
1953- def update_config (current_config , config_input ):
1954- updated_config = copy .deepcopy (current_config )
1955- # Override current config with golden config
1956- for table in config_input :
1957- updated_config [table ] = config_input [table ]
1958- return updated_config
1959-
1960-
19611989def override_config_db (config_db , config_input ):
19621990 # Deserialized golden config to DB recognized format
19631991 sonic_cfggen .FormatConverter .to_deserialized (config_input )
@@ -2909,7 +2937,12 @@ def _qos_update_ports(ctx, ports, dry_run, json_data):
29092937 click .secho ("QoS definition template not found at {}" .format (qos_template_file ), fg = "yellow" )
29102938 ctx .abort ()
29112939
2912- # Remove multi indexed entries first
2940+ # Remove entries first
2941+ for table_name in tables_single_index :
2942+ for port in portset_to_handle :
2943+ if config_db .get_entry (table_name , port ):
2944+ config_db .set_entry (table_name , port , None )
2945+
29132946 for table_name in tables_multi_index :
29142947 entries = config_db .get_keys (table_name )
29152948 for key in entries :
@@ -5960,6 +5993,22 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos
59605993 clicommon .run_command (command , display_cmd = verbose )
59615994
59625995
5996+ #
5997+ # 'mmu' command ('config mmu...')
5998+ #
5999+ @config .command ()
6000+ @click .option ('-p' , metavar = '<profile_name>' , type = str , required = True , help = "Profile name" )
6001+ @click .option ('-a' , metavar = '<alpha>' , type = click .IntRange (- 8 ,8 ), help = "Set alpha for profile type dynamic" )
6002+ @click .option ('-s' , metavar = '<staticth>' , type = int , help = "Set staticth for profile type static" )
6003+ def mmu (p , a , s ):
6004+ """mmuconfig configuration tasks"""
6005+ log .log_info ("'mmuconfig -p {}' executing..." .format (p ))
6006+ command = ['mmuconfig' , '-p' , str (p )]
6007+ if a is not None : command += ['-a' , str (a )]
6008+ if s is not None : command += ['-s' , str (s )]
6009+ clicommon .run_command (command )
6010+
6011+
59636012#
59646013# 'pfc' group ('config interface pfc ...')
59656014#
@@ -6525,7 +6574,9 @@ def add_ntp_server(ctx, ntp_ip_address):
65256574 return
65266575 else :
65276576 try :
6528- db .set_entry ('NTP_SERVER' , ntp_ip_address , {'NULL' : 'NULL' })
6577+ db .set_entry ('NTP_SERVER' , ntp_ip_address ,
6578+ {'resolve_as' : ntp_ip_address ,
6579+ 'association_type' : 'server' })
65296580 except ValueError as e :
65306581 ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
65316582 click .echo ("NTP server {} added to configuration" .format (ntp_ip_address ))
@@ -6646,6 +6697,41 @@ def polling_int(ctx, interval):
66466697 except ValueError as e :
66476698 ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
66486699
6700+ def is_port_egress_sflow_supported ():
6701+ state_db = SonicV2Connector (use_unix_socket_path = True )
6702+ state_db .connect (state_db .STATE_DB , False )
6703+ entry_name = "SWITCH_CAPABILITY|switch"
6704+ supported = state_db .get (state_db .STATE_DB , entry_name ,"PORT_EGRESS_SAMPLE_CAPABLE" )
6705+ return supported
6706+
6707+ #
6708+ # 'sflow' command ('config sflow sample-direction ...')
6709+ #
6710+ @sflow .command ('sample-direction' )
6711+ @click .argument ('direction' , metavar = '<sample_direction>' , required = True , type = str )
6712+ @click .pass_context
6713+ def global_sample_direction (ctx , direction ):
6714+ """Set sampling direction """
6715+ if ADHOC_VALIDATION :
6716+ if direction :
6717+ if direction not in ['rx' , 'tx' , 'both' ]:
6718+ ctx .fail ("Error: Direction {} is invalid" .format (direction ))
6719+
6720+ if ((direction == 'tx' or direction == 'both' ) and (is_port_egress_sflow_supported () == 'false' )):
6721+ ctx .fail ("Sample direction {} is not supported on this platform" .format (direction ))
6722+
6723+ config_db = ValidatedConfigDBConnector (ctx .obj ['db' ])
6724+ sflow_tbl = config_db .get_table ('SFLOW' )
6725+
6726+ if not sflow_tbl :
6727+ sflow_tbl = {'global' : {'admin_state' : 'down' }}
6728+
6729+ sflow_tbl ['global' ]['sample_direction' ] = direction
6730+ try :
6731+ config_db .mod_entry ('SFLOW' , 'global' , sflow_tbl ['global' ])
6732+ except ValueError as e :
6733+ ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
6734+
66496735def is_valid_sample_rate (rate ):
66506736 return rate .isdigit () and int (rate ) in range (256 , 8388608 + 1 )
66516737
@@ -6755,6 +6841,40 @@ def sample_rate(ctx, ifname, rate):
67556841 except ValueError as e :
67566842 ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
67576843
6844+ #
6845+ # 'sflow' command ('config sflow interface sample-direction ...')
6846+ #
6847+ @interface .command ('sample-direction' )
6848+ @click .argument ('ifname' , metavar = '<interface_name>' , required = True , type = str )
6849+ @click .argument ('direction' , metavar = '<sample_direction>' , required = True , type = str )
6850+ @click .pass_context
6851+ def interface_sample_direction (ctx , ifname , direction ):
6852+ config_db = ValidatedConfigDBConnector (ctx .obj ['db' ])
6853+ if ADHOC_VALIDATION :
6854+ if not interface_name_is_valid (config_db , ifname ) and ifname != 'all' :
6855+ click .echo ('Invalid interface name' )
6856+ return
6857+ if direction :
6858+ if direction not in ['rx' , 'tx' , 'both' ]:
6859+ ctx .fail ("Error: Direction {} is invalid" .format (direction ))
6860+
6861+ if (direction == 'tx' or direction == 'both' ) and (is_port_egress_sflow_supported () == 'false' ):
6862+ ctx .fail ("Sample direction {} is not supported on this platform" .format (direction ))
6863+
6864+ sess_dict = config_db .get_table ('SFLOW_SESSION' )
6865+
6866+ if sess_dict and ifname in sess_dict .keys ():
6867+ sess_dict [ifname ]['sample_direction' ] = direction
6868+ try :
6869+ config_db .mod_entry ('SFLOW_SESSION' , ifname , sess_dict [ifname ])
6870+ except ValueError as e :
6871+ ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
6872+ else :
6873+ try :
6874+ config_db .mod_entry ('SFLOW_SESSION' , ifname , {'sample_direction' : direction })
6875+ except ValueError as e :
6876+ ctx .fail ("Invalid ConfigDB. Error: {}" .format (e ))
6877+
67586878
67596879#
67606880# 'sflow collector' group
@@ -7203,7 +7323,7 @@ def clock():
72037323
72047324
72057325def get_tzs (ctx , args , incomplete ):
7206- ret = clicommon .run_command ('timedatectl list-timezones' ,
7326+ ret = clicommon .run_command ([ 'timedatectl' , ' list-timezones'] ,
72077327 display_cmd = False , ignore_error = False ,
72087328 return_cmd = True )
72097329 if len (ret ) == 0 :
0 commit comments