diff --git a/scripts/hostcfgd b/scripts/hostcfgd index 0a33d1d..266fc15 100644 --- a/scripts/hostcfgd +++ b/scripts/hostcfgd @@ -14,7 +14,7 @@ from shutil import copy2 from datetime import datetime from sonic_py_common import device_info from sonic_py_common.general import check_output_pipe - # from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table +from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table from swsscommon import swsscommon from sonic_installer import bootloader hostcfg_file_path = os.path.abspath(__file__) @@ -1716,8 +1716,7 @@ class FipsCfg(object): syslog.syslog(syslog.LOG_INFO, f'FipsCfg: update the FIPS enforce option {self.enforce}.') loader.set_fips(image, self.enforce) - -class Memory_StatisticsCfg(object): +class MemoryStatsCfg(object): """ Memory Stats Config Daemon Handles changes in MEMORY_STATS table. @@ -1725,77 +1724,113 @@ class Memory_StatisticsCfg(object): 2) Handle change of retention period 3) Handle change of sampling interval """ - - def __init__(self, CfgDb): - self.config_db = CfgDb + + def __init__(self): self.cache = {} - self.memory_statistics_defaults = { - "enabled": "false", - "retention_time": "15 days", - "sampling_interval": "5 minutes" - } - - def load(self, memory_statistics_table): - """ - Load memory statistics configuration when the daemon starts. + + def load(self, memory_stats_config: dict): + """Memory stats configuration + Force load memory statistics configuration. Args: memory_stats_config: Configured memory statistics settings. """ - syslog.syslog(syslog.LOG_INFO, "Memory_StatisticsCfg init ...") - memory_statistics_conf = memory_statistics_table.get("config", {}) - - # Apply default configurations if not present - for row, value in self.memory_statistics_defaults.items(): - if not memory_statistics_conf.get(row): - self.config_db.mod_entry("MEMORY_STATISTICS", "config", {row: value}) - - # Apply configurations to ensure they are set correctly on startup - self.apply_configuration(memory_statistics_conf) - - def apply_configuration(self, config): - """ - Apply the memory statistics configuration settings. - Args: - config: Configuration data for memory statistics. - """ - # Determine if the feature is enabled or disabled - enabled = config.get("enabled", self.memory_statistics_defaults["enabled"]).lower() == "true" - retention_time = config.get("retention_time", self.memory_statistics_defaults["retention_time"]) - sampling_interval = config.get("sampling_interval", self.memory_statistics_defaults["sampling_interval"]) - - # Enable or disable memory statistics - if enabled: - self.run_cmd(["sonic-memory_statistics-config", "--enable"]) - else: - self.run_cmd(["sonic-memory_statistics-config", "--disable"]) - - # Set retention time and sampling interval - self.run_cmd(["sonic-memory_statistics-config", "--retention_time", retention_time]) - self.run_cmd(["sonic-memory_statistics-config", "--sampling_interval", sampling_interval]) - - def memory_statistics_update(self, key, data): + syslog.syslog(syslog.LOG_INFO, 'MemoryStatsCfg: load initial') + + if not memory_stats_config: + memory_stats_config = {} + + # Force load memory statistics settings. + enable_data = memory_stats_config.get("enable", {}) + retention_data = memory_stats_config.get("retention", {}) + sampling_data = memory_stats_config.get("sampling", {}) + + self.memory_stats_message("enable", enable_data) + self.memory_stats_message("retention", retention_data) + self.memory_stats_message("sampling", sampling_data) + + def memory_stats_message(self, key, data): """ - Handle updates to the memory statistics configuration. + Apply memory stats settings handler. Args: - key: Key identifying the config type. - data: Updated configuration data. - """ - syslog.syslog(syslog.LOG_INFO, "Memory_Statistics global configuration update") - if key == "config": - self.apply_configuration(data) - - def run_cmd(self, cmd): - """ - Execute a shell command and return the output. - Args: - cmd: List of command arguments. + key: DB table's key that was triggered change. + data: Read table data. """ + if type(data) != dict: + # Nothing to handle + return + + update_required = False + # Check with cache + for k, v in data.items(): + if v != self.cache.get(k): + update_required = True + break + + if not update_required: + return + try: - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - syslog.syslog(syslog.LOG_INFO, output.decode('utf-8')) + # If the disable flag is set, stop the daemon. + if key == "enable" and not data.get("enabled", True): + self.shutdown_memorystats() + else: + # Signal the memory stats daemon to reload configuration + self.reload_memory_stats_daemon() + except Exception as e: + syslog.syslog(syslog.LOG_ERR, f'MemoryStatsCfg: Failed to manage memory-stats-daemon: {e}') + return + + # Update cache + for k, v in data.items(): + self.cache[k] = v + + def reload_memory_stats_daemon(self): + """Reload the memory stats daemon""" + syslog.syslog(syslog.LOG_INFO, 'MemoryStatsCfg: Reloading memory-stats-daemon') + try: + subprocess.run(["systemctl", "reload", "memory-stats-daemon"], check=True) + syslog.syslog(syslog.LOG_INFO, 'MemoryStatsCfg: memory-stats-daemon successfully reloaded') except subprocess.CalledProcessError as e: - syslog.syslog(syslog.LOG_ERR, e.output.decode('utf-8')) - + syslog.syslog(syslog.LOG_ERR, f"MemoryStatsCfg: Failed to reload memory-stats-daemon: {e}") + raise + + # Additional methods for handling signals + def get_memorystats_pid(self): + """Get the PID of the memorystatsd process.""" + try: + for proc in psutil.process_iter(['name']): + if proc.info['name'] == 'memorystatsd': + return proc.pid + except psutil.NoSuchProcess: + return None + return None + + def reload_memorystats(self): + """Send SIGHUP to reload configuration.""" + pid = self.get_memorystats_pid() + if pid: + os.kill(pid, signal.SIGHUP) + + def shutdown_memorystats(self): + """Send SIGTERM to gracefully shut down memorystatsd.""" + pid = self.get_memorystats_pid() + if pid: + os.kill(pid, signal.SIGTERM) + + def monitor_config_changes(self): + """Monitor configuration changes from ConfigDB.""" + while True: + config = configdb.get_memory_stats_config() + + # Handle changes in retention_period, sampling_interval, or enable/disable feature + if config.get('changed'): # Assuming 'changed' is a flag indicating a change + if config.get('enabled', True): # If 'enabled' is True, reload the daemon + self.reload_memorystats() + else: + # If 'enabled' is False, disable the feature by sending SIGTERM to the daemon + self.shutdown_memorystats() + + time.sleep(5) # Polling interval class SerialConsoleCfg: