Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 146 additions & 1 deletion config/config_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Port Breakout.
'''

from enum import Enum, auto
from logging import config
import os
import re
import shutil
Expand All @@ -25,7 +27,7 @@

# Globals
YANG_DIR = "/usr/local/yang-models"
CONFIG_DB_JSON_FILE = '/etc/sonic/confib_db.json'
CONFIG_DB_JSON_FILE = '/etc/sonic/config_db.json'
# TODO: Find a place for it on sonic switch.
DEFAULT_CONFIG_DB_JSON_FILE = '/etc/sonic/port_breakout_config_db.json'

Expand Down Expand Up @@ -983,3 +985,146 @@ def readJsonFile(fileName):
raise Exception(e)

return result

class ConfigMgmtScheduledConfig(ConfigMgmt):
'''
Config MGMT class for Scheduled Configurations. This is derived from ConfigMgmt.
'''

def __init__(self, source="configDB", debug=False, allowTablesWithoutYang=True):
'''
Initialise the class

Parameters:
source (str): source for input config, default configDb else file.
debug (bool): verbose mode.
allowTablesWithoutYang (bool): allow tables without yang model in
config or not.

Returns:
void
'''
try:
super().__init__(source=source, debug=debug, allowTablesWithoutYang=allowTablesWithoutYang)

except Exception as e:
self.sysLog(doPrint=True, logLevel=syslog.LOG_ERR, msg=str(e))
raise Exception('ConfigMgmtScheduledConfig Class creation failed')

def validateConfigData(self):
'''
Validate current config data Tree, including scheduled configurations.

Parameters:
void

Returns:
bool
'''
# First validate the base configuration
if not super().validateConfigData():
return False

# Validate the scheduled configurations
try:
self._validateScheduledConfigurations()
except Exception as e:
self.sysLog(doPrint=True, logLevel=syslog.LOG_ERR, msg=f'Scheduled Config Data Validation Failed: {str(e)}')
return False

self.sysLog(msg='Scheduled Config Data Validation successful', doPrint=True)
return True

def _validateScheduledConfigurations(self):
'''
Validate the scheduled configurations within the data tree.

Parameters:
void

Returns:
void
'''
# Get the scheduled configurations from the loaded data
scheduled_configs = self.configdbJsonIn.get("SCHEDULED_CONFIGURATIONS")

if not scheduled_configs:
return

for config_name, scheduled_config in scheduled_configs.items():
self.sysLog(msg=f'Validating Scheduled Configuration: {config_name}', doPrint=True)

# Validate the 'configuration' field
config_node = scheduled_config.get("configuration")
if config_node:
self._validate_configuration(config_node)
else:
raise Exception(f'Missing "configuration" field in scheduled configuration: {config_name}')

# Validate the 'deactivation_configuration' field
deactivation_node = scheduled_config.get("deactivation_configuration")
if deactivation_node:
if isinstance(deactivation_node, str) and deactivation_node.lower() == "remove":
continue
else:
self._validate_configuration(deactivation_node)
else:
raise Exception(f'Missing "deactivation_configuration" field in scheduled configuration: {config_name}')

def _validate_configuration(self, node):
'''
Recursively validate a configuration node.

Parameters:
node (lyd_node): The node to validate.

Returns:
void
'''
self.loadData(node)
super().validateConfigData()


def writeConfigDB(self, configJson = ""):
'''
Write the validated config to Config DB.

Parameters:
configJson (dict): config to push in config DB.

Returns:
void
'''
if configJson == "":
configJson = self.configdbJsonIn

self.sysLog(doPrint=True, msg='Writing in Config DB')
data = dict()
if self.configdb is None:
configdb = ConfigDBConnector()
configdb.connect(False)
else:
configdb = self.configdb
sonic_cfggen.deep_update(data, configJson)
self.sysLog(msg="Write in DB: {}".format(data))
configdb.mod_config(sonic_cfggen.FormatConverter.output_to_db(data))



class ConfigMgmtType(Enum):
BASIC = auto()
DPB = auto()
SCHEDULED_CONFIG = auto()


def load_ConfigMgmt(type: ConfigMgmtType = ConfigMgmtType.BASIC, source: str = "", verbose: bool =False, allowTablesWithoutYang: bool = True, sonicYangOptions: int = 0, configdb=None) -> ConfigMgmt:
""" Load config for the commands which are capable of change in config DB. """
try:
if type == ConfigMgmtType.DPB:
return ConfigMgmtDPB(source=source, debug=verbose, allowTablesWithoutYang=allowTablesWithoutYang)
elif type == ConfigMgmtType.SCHEDULED_CONFIG:
return ConfigMgmtScheduledConfig(source=source, debug=verbose, allowTablesWithoutYang=allowTablesWithoutYang)
else: # ConfigMgmtType.BASIC
return ConfigMgmt(source=source, debug=verbose, allowTablesWithoutYang=allowTablesWithoutYang, sonicYangOptions=sonicYangOptions, configdb=configdb)
except Exception as e:
raise Exception("Failed to load the config. Error: {}".format(str(e)))
43 changes: 30 additions & 13 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
from . import vlan
from . import vxlan
from . import plugins
from .config_mgmt import ConfigMgmtDPB, ConfigMgmt
from .config_mgmt import ConfigMgmtDPB, ConfigMgmt, ConfigMgmtType, load_ConfigMgmt, ConfigMgmtScheduledConfig
from . import mclag
from . import syslog
from . import switchport
Expand Down Expand Up @@ -207,14 +207,6 @@ def _validate_interface_mode(ctx, breakout_cfg_file, interface_name, target_brko
sys.exit(0)
return True

def load_ConfigMgmt(verbose):
""" Load config for the commands which are capable of change in config DB. """
try:
cm = ConfigMgmtDPB(debug=verbose)
return cm
except Exception as e:
raise Exception("Failed to load the config. Error: {}".format(str(e)))

def breakout_warnUser_extraTables(cm, final_delPorts, confirm=True):
"""
Function to warn user about extra tables while Dynamic Port Breakout(DPB).
Expand Down Expand Up @@ -2086,7 +2078,7 @@ def override_config_table(db, input_config_db, dry_run):
# The ConfigMgmt will load YANG and running
# config during initialization.
try:
cm = ConfigMgmt(configdb=config_db)
cm: ConfigMgmt = load_ConfigMgmt(configdb=config_db)
cm.validateConfigData()
except Exception as ex:
click.secho("Failed to validate running config. Error: {}".format(ex), fg="magenta")
Expand All @@ -2100,7 +2092,7 @@ def override_config_table(db, input_config_db, dry_run):
cm = None
try:
# YANG validate of config minigraph generated
cm = ConfigMgmt(configdb=config_db)
cm: ConfigMgmt= load_ConfigMgmt(configdb=config_db)
cm.validateConfigData()
except Exception as ex:
log.log_warning("Failed to validate running config. Alerting: {}".format(ex))
Expand Down Expand Up @@ -4552,7 +4544,7 @@ def breakout(ctx, interface_name, mode, verbose, force_remove_dependencies, load
# Start Interation with Dy Port BreakOut Config Mgmt
try:
""" Load config for the commands which are capable of change in config DB """
cm = load_ConfigMgmt(verbose)
cm: ConfigMgmtDPB = load_ConfigMgmt(type=ConfigMgmtType.DPB, verbose=verbose)

""" Delete all ports if forced else print dependencies using ConfigMgmt API """
final_delPorts = [intf for intf in del_intf_dict]
Expand Down Expand Up @@ -7630,7 +7622,6 @@ def date(date, time):
date_time = f'{date} {time}'
clicommon.run_command(['timedatectl', 'set-time', date_time])


#
# 'asic-sdk-health-event' group ('config asic-sdk-health-event ...')
#
Expand Down Expand Up @@ -7749,6 +7740,32 @@ def warning(db, category_list, max_events, namespace):
def notice(db, category_list, max_events, namespace):
handle_asic_sdk_health_suppress(db, 'notice', category_list, max_events, namespace)

@config.command("scheduled-configuration")
@click.argument('json_file_name', metavar='<json_name>', required=True)
@click.option('-d', '--dry-run', is_flag=True, help="Validate configuration without applying to config db")
def scheduled_configuration(json_file_name, dry_run):
""" config scheduled-configuration """

verbose = False
cm: ConfigMgmtScheduledConfig
try:
cm = load_ConfigMgmt(ConfigMgmtType.SCHEDULED_CONFIG, source= json_file_name, verbose=verbose)
except Exception as e:
error_msg = "Failed to load ConfigMgmtScheduledConfig: {}".format(str(e))
click.secho(error_msg, fg='red')
return

if not cm.validateConfigData():
error_msg = "Failed to validate data"
click.secho(error_msg, fg='red')
return

if dry_run:
click.echo("Successfully validated configuration data, dry-run mode enabled, no changes will be applied")
return

cm.writeConfigDB()


if __name__ == '__main__':
config()
Loading