3131from sonic_yang_cfg_generator import SonicYangCfgDbGenerator
3232from utilities_common import util_base
3333from swsscommon import swsscommon
34- from swsscommon .swsscommon import SonicV2Connector , ConfigDBConnector
34+ from swsscommon .swsscommon import SonicV2Connector , ConfigDBConnector , ConfigDBPipeConnector
3535from utilities_common .db import Db
3636from utilities_common .intf_filter import parse_interface_in_filter
3737from utilities_common import bgp_util
@@ -1196,7 +1196,7 @@ def validate_gre_type(ctx, _, value):
11961196 raise click .UsageError ("{} is not a valid GRE type" .format (value ))
11971197
11981198
1199- def multi_asic_save_config (db , filename ):
1199+ def multiasic_save_to_singlefile (db , filename ):
12001200 """A function to save all asic's config to single file
12011201 """
12021202 all_current_config = {}
@@ -1263,6 +1263,96 @@ def validate_patch(patch):
12631263 except Exception as e :
12641264 raise GenericConfigUpdaterError (f"Validate json patch: { patch } failed due to:{ e } " )
12651265
1266+
1267+ def multiasic_validate_single_file (filename ):
1268+ ns_list = [DEFAULT_NAMESPACE , * multi_asic .get_namespace_list ()]
1269+ file_input = read_json_file (filename )
1270+ file_ns_list = [DEFAULT_NAMESPACE if key == HOST_NAMESPACE else key for key in file_input ]
1271+ if set (ns_list ) != set (file_ns_list ):
1272+ click .echo (
1273+ "Input file {} must contain all asics config. ns_list: {} file ns_list: {}" .format (
1274+ filename , ns_list , file_ns_list )
1275+ )
1276+ raise click .Abort ()
1277+
1278+
1279+ def load_sysinfo_if_missing (asic_config ):
1280+ device_metadata = asic_config .get ('DEVICE_METADATA' , {})
1281+ platform = device_metadata .get ("localhost" , {}).get ("platform" )
1282+ mac = device_metadata .get ("localhost" , {}).get ("mac" )
1283+ if not platform :
1284+ log .log_warning ("platform is missing from Input file" )
1285+ return True
1286+ elif not mac :
1287+ log .log_warning ("mac is missing from Input file" )
1288+ return True
1289+ else :
1290+ return False
1291+
1292+
1293+ def flush_configdb (namespace = DEFAULT_NAMESPACE ):
1294+ if namespace is DEFAULT_NAMESPACE :
1295+ config_db = ConfigDBConnector ()
1296+ else :
1297+ config_db = ConfigDBConnector (use_unix_socket_path = True , namespace = namespace )
1298+
1299+ config_db .connect ()
1300+ client = config_db .get_redis_client (config_db .CONFIG_DB )
1301+ client .flushdb ()
1302+ return client , config_db
1303+
1304+
1305+ def migrate_db_to_lastest (namespace = DEFAULT_NAMESPACE ):
1306+ # Migrate DB contents to latest version
1307+ db_migrator = '/usr/local/bin/db_migrator.py'
1308+ if os .path .isfile (db_migrator ) and os .access (db_migrator , os .X_OK ):
1309+ if namespace is DEFAULT_NAMESPACE :
1310+ command = [db_migrator , '-o' , 'migrate' ]
1311+ else :
1312+ command = [db_migrator , '-o' , 'migrate' , '-n' , namespace ]
1313+ clicommon .run_command (command , display_cmd = True )
1314+
1315+
1316+ def multiasic_write_to_db (filename , load_sysinfo ):
1317+ file_input = read_json_file (filename )
1318+ for ns in [DEFAULT_NAMESPACE , * multi_asic .get_namespace_list ()]:
1319+ asic_name = HOST_NAMESPACE if ns == DEFAULT_NAMESPACE else ns
1320+ asic_config = file_input [asic_name ]
1321+
1322+ asic_load_sysinfo = True if load_sysinfo else False
1323+ if not asic_load_sysinfo :
1324+ asic_load_sysinfo = load_sysinfo_if_missing (asic_config )
1325+
1326+ if asic_load_sysinfo :
1327+ cfg_hwsku = asic_config .get ("DEVICE_METADATA" , {}).\
1328+ get ("localhost" , {}).get ("hwsku" )
1329+ if not cfg_hwsku :
1330+ click .secho ("Could not get the HWSKU from config file, Exiting!" , fg = 'magenta' )
1331+ sys .exit (1 )
1332+
1333+ client , _ = flush_configdb (ns )
1334+
1335+ if asic_load_sysinfo :
1336+ if ns is DEFAULT_NAMESPACE :
1337+ command = [str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '--write-to-db' ]
1338+ else :
1339+ command = [str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '-n' , str (ns ), '--write-to-db' ]
1340+ clicommon .run_command (command , display_cmd = True )
1341+
1342+ if ns is DEFAULT_NAMESPACE :
1343+ config_db = ConfigDBPipeConnector (use_unix_socket_path = True )
1344+ else :
1345+ config_db = ConfigDBPipeConnector (use_unix_socket_path = True , namespace = ns )
1346+
1347+ config_db .connect (False )
1348+ sonic_cfggen .FormatConverter .to_deserialized (asic_config )
1349+ data = sonic_cfggen .FormatConverter .output_to_db (asic_config )
1350+ config_db .mod_config (sonic_cfggen .FormatConverter .output_to_db (data ))
1351+ client .set (config_db .INIT_INDICATOR , 1 )
1352+
1353+ migrate_db_to_lastest (ns )
1354+
1355+
12661356# This is our main entrypoint - the main 'config' command
12671357@click .group (cls = clicommon .AbbreviationGroup , context_settings = CONTEXT_SETTINGS )
12681358@click .pass_context
@@ -1350,7 +1440,7 @@ def save(db, filename):
13501440 # save all ASIC configurations to that single file.
13511441 if len (cfg_files ) == 1 and multi_asic .is_multi_asic ():
13521442 filename = cfg_files [0 ]
1353- multi_asic_save_config (db , filename )
1443+ multiasic_save_to_singlefile (db , filename )
13541444 return
13551445 elif len (cfg_files ) != num_cfg_file :
13561446 click .echo ("Input {} config file(s) separated by comma for multiple files " .format (num_cfg_file ))
@@ -1668,11 +1758,15 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, force, file_form
16681758 if multi_asic .is_multi_asic () and file_format == 'config_db' :
16691759 num_cfg_file += num_asic
16701760
1761+ multiasic_single_file_mode = False
16711762 # If the user give the filename[s], extract the file names.
16721763 if filename is not None :
16731764 cfg_files = filename .split (',' )
16741765
1675- if len (cfg_files ) != num_cfg_file :
1766+ if len (cfg_files ) == 1 and multi_asic .is_multi_asic ():
1767+ multiasic_validate_single_file (cfg_files [0 ])
1768+ multiasic_single_file_mode = True
1769+ elif len (cfg_files ) != num_cfg_file :
16761770 click .echo ("Input {} config file(s) separated by comma for multiple files " .format (num_cfg_file ))
16771771 return
16781772
@@ -1681,127 +1775,109 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, force, file_form
16811775 log .log_notice ("'reload' stopping services..." )
16821776 _stop_services ()
16831777
1684- # In Single ASIC platforms we have single DB service. In multi-ASIC platforms we have a global DB
1685- # service running in the host + DB services running in each ASIC namespace created per ASIC.
1686- # In the below logic, we get all namespaces in this platform and add an empty namespace ''
1687- # denoting the current namespace which we are in ( the linux host )
1688- for inst in range (- 1 , num_cfg_file - 1 ):
1689- # Get the namespace name, for linux host it is None
1690- if inst == - 1 :
1691- namespace = None
1692- else :
1693- namespace = "{}{}" .format (NAMESPACE_PREFIX , inst )
1694-
1695- # Get the file from user input, else take the default file /etc/sonic/config_db{NS_id}.json
1696- if cfg_files :
1697- file = cfg_files [inst + 1 ]
1698- # Save to tmpfile in case of stdin input which can only be read once
1699- if file == "/dev/stdin" :
1700- file_input = read_json_file (file )
1701- (_ , tmpfname ) = tempfile .mkstemp (dir = "/tmp" , suffix = "_configReloadStdin" )
1702- write_json_file (file_input , tmpfname )
1703- file = tmpfname
1704- else :
1705- if file_format == 'config_db' :
1706- if namespace is None :
1707- file = DEFAULT_CONFIG_DB_FILE
1708- else :
1709- file = "/etc/sonic/config_db{}.json" .format (inst )
1778+ if multiasic_single_file_mode :
1779+ multiasic_write_to_db (cfg_files [0 ], load_sysinfo )
1780+ else :
1781+ # In Single ASIC platforms we have single DB service. In multi-ASIC platforms we have a global DB
1782+ # service running in the host + DB services running in each ASIC namespace created per ASIC.
1783+ # In the below logic, we get all namespaces in this platform and add an empty namespace ''
1784+ # denoting the current namespace which we are in ( the linux host )
1785+ for inst in range (- 1 , num_cfg_file - 1 ):
1786+ # Get the namespace name, for linux host it is DEFAULT_NAMESPACE
1787+ if inst == - 1 :
1788+ namespace = DEFAULT_NAMESPACE
17101789 else :
1711- file = DEFAULT_CONFIG_YANG_FILE
1712-
1790+ namespace = "{}{}" .format (NAMESPACE_PREFIX , inst )
1791+
1792+ # Get the file from user input, else take the default file /etc/sonic/config_db{NS_id}.json
1793+ if cfg_files :
1794+ file = cfg_files [inst + 1 ]
1795+ # Save to tmpfile in case of stdin input which can only be read once
1796+ if file == "/dev/stdin" :
1797+ file_input = read_json_file (file )
1798+ (_ , tmpfname ) = tempfile .mkstemp (dir = "/tmp" , suffix = "_configReloadStdin" )
1799+ write_json_file (file_input , tmpfname )
1800+ file = tmpfname
1801+ else :
1802+ if file_format == 'config_db' :
1803+ if namespace is DEFAULT_NAMESPACE :
1804+ file = DEFAULT_CONFIG_DB_FILE
1805+ else :
1806+ file = "/etc/sonic/config_db{}.json" .format (inst )
1807+ else :
1808+ file = DEFAULT_CONFIG_YANG_FILE
17131809
1714- # Check the file exists before proceeding.
1715- if not os .path .exists (file ):
1716- click .echo ("The config file {} doesn't exist" .format (file ))
1717- continue
1810+ # Check the file exists before proceeding.
1811+ if not os .path .exists (file ):
1812+ click .echo ("The config file {} doesn't exist" .format (file ))
1813+ continue
17181814
1719- if file_format == 'config_db' :
1720- file_input = read_json_file (file )
1815+ if file_format == 'config_db' :
1816+ file_input = read_json_file (file )
1817+ if not load_sysinfo :
1818+ load_sysinfo = load_sysinfo_if_missing (file_input )
1819+
1820+ if load_sysinfo :
1821+ try :
1822+ command = [SONIC_CFGGEN_PATH , "-j" , file , '-v' , "DEVICE_METADATA.localhost.hwsku" ]
1823+ proc = subprocess .Popen (command , text = True , stdout = subprocess .PIPE )
1824+ output , err = proc .communicate ()
1825+
1826+ except FileNotFoundError as e :
1827+ click .echo ("{}" .format (str (e )), err = True )
1828+ raise click .Abort ()
1829+ except Exception as e :
1830+ click .echo ("{}\n {}" .format (type (e ), str (e )), err = True )
1831+ raise click .Abort ()
1832+
1833+ if not output :
1834+ click .secho ("Could not get the HWSKU from config file, Exiting!!!" , fg = 'magenta' )
1835+ sys .exit (1 )
17211836
1722- platform = file_input .get ("DEVICE_METADATA" , {}).\
1723- get (HOST_NAMESPACE , {}).get ("platform" )
1724- mac = file_input .get ("DEVICE_METADATA" , {}).\
1725- get (HOST_NAMESPACE , {}).get ("mac" )
1837+ cfg_hwsku = output .strip ()
17261838
1727- if not platform or not mac :
1728- log .log_warning ("Input file does't have platform or mac. platform: {}, mac: {}"
1729- .format (None if platform is None else platform , None if mac is None else mac ))
1730- load_sysinfo = True
1839+ client , config_db = flush_configdb (namespace )
17311840
1732- if load_sysinfo :
1733- try :
1734- command = [SONIC_CFGGEN_PATH , "-j" , file , '-v' , "DEVICE_METADATA.localhost.hwsku" ]
1735- proc = subprocess .Popen (command , text = True , stdout = subprocess .PIPE )
1736- output , err = proc .communicate ()
1841+ if load_sysinfo :
1842+ if namespace is DEFAULT_NAMESPACE :
1843+ command = [
1844+ str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '--write-to-db' ]
1845+ else :
1846+ command = [
1847+ str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '-n' , str (namespace ), '--write-to-db' ]
1848+ clicommon .run_command (command , display_cmd = True )
17371849
1738- except FileNotFoundError as e :
1739- click .echo ("{}" .format (str (e )), err = True )
1740- raise click .Abort ()
1741- except Exception as e :
1742- click .echo ("{}\n {}" .format (type (e ), str (e )), err = True )
1743- raise click .Abort ()
1850+ # For the database service running in linux host we use the file user gives as input
1851+ # or by default DEFAULT_CONFIG_DB_FILE. In the case of database service running in namespace,
1852+ # the default config_db<namespaceID>.json format is used.
17441853
1745- if not output :
1746- click .secho ("Could not get the HWSKU from config file, Exiting!!!" , fg = 'magenta' )
1747- sys .exit (1 )
1854+ config_gen_opts = []
17481855
1749- cfg_hwsku = output .strip ()
1856+ if os .path .isfile (INIT_CFG_FILE ):
1857+ config_gen_opts += ['-j' , str (INIT_CFG_FILE )]
17501858
1751- if namespace is None :
1752- config_db = ConfigDBConnector ()
1753- else :
1754- config_db = ConfigDBConnector (use_unix_socket_path = True , namespace = namespace )
1755-
1756- config_db .connect ()
1757- client = config_db .get_redis_client (config_db .CONFIG_DB )
1758- client .flushdb ()
1759-
1760- if load_sysinfo :
1761- if namespace is None :
1762- command = [str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '--write-to-db' ]
1859+ if file_format == 'config_db' :
1860+ config_gen_opts += ['-j' , str (file )]
17631861 else :
1764- command = [str (SONIC_CFGGEN_PATH ), '-H' , '-k' , str (cfg_hwsku ), '-n' , str (namespace ), '--write-to-db' ]
1765- clicommon .run_command (command , display_cmd = True )
1766-
1767- # For the database service running in linux host we use the file user gives as input
1768- # or by default DEFAULT_CONFIG_DB_FILE. In the case of database service running in namespace,
1769- # the default config_db<namespaceID>.json format is used.
1862+ config_gen_opts += ['-Y' , str (file )]
17701863
1864+ if namespace is not DEFAULT_NAMESPACE :
1865+ config_gen_opts += ['-n' , str (namespace )]
17711866
1772- config_gen_opts = []
1867+ command = [SONIC_CFGGEN_PATH ] + config_gen_opts + [ '--write-to-db' ]
17731868
1774- if os .path .isfile (INIT_CFG_FILE ):
1775- config_gen_opts += ['-j' , str (INIT_CFG_FILE )]
1776-
1777- if file_format == 'config_db' :
1778- config_gen_opts += ['-j' , str (file )]
1779- else :
1780- config_gen_opts += ['-Y' , str (file )]
1781-
1782- if namespace is not None :
1783- config_gen_opts += ['-n' , str (namespace )]
1784-
1785- command = [SONIC_CFGGEN_PATH ] + config_gen_opts + ['--write-to-db' ]
1786-
1787- clicommon .run_command (command , display_cmd = True )
1788- client .set (config_db .INIT_INDICATOR , 1 )
1869+ clicommon .run_command (command , display_cmd = True )
1870+ client .set (config_db .INIT_INDICATOR , 1 )
17891871
1790- if os .path .exists (file ) and file .endswith ("_configReloadStdin" ):
1791- # Remove tmpfile
1792- try :
1793- os .remove (file )
1794- except OSError as e :
1795- click .echo ("An error occurred while removing the temporary file: {}" .format (str (e )), err = True )
1872+ if os .path .exists (file ) and file .endswith ("_configReloadStdin" ):
1873+ # Remove tmpfile
1874+ try :
1875+ os .remove (file )
1876+ except OSError as e :
1877+ click .echo ("An error occurred while removing the temporary file: {}" .format (str (e )), err = True )
17961878
1797- # Migrate DB contents to latest version
1798- db_migrator = '/usr/local/bin/db_migrator.py'
1799- if os .path .isfile (db_migrator ) and os .access (db_migrator , os .X_OK ):
1800- if namespace is None :
1801- command = [db_migrator , '-o' , 'migrate' ]
1802- else :
1803- command = [db_migrator , '-o' , 'migrate' , '-n' , str (namespace )]
1804- clicommon .run_command (command , display_cmd = True )
1879+ # Migrate DB contents to latest version
1880+ migrate_db_to_lastest (namespace )
18051881
18061882 # Re-generate the environment variable in case config_db.json was edited
18071883 update_sonic_environment ()
0 commit comments