Skip to content

Commit

Permalink
update hostconfig daemon
Browse files Browse the repository at this point in the history
Signed-off-by: Arham-Nasir <[email protected]>
  • Loading branch information
Arham-Nasir committed Nov 14, 2024
1 parent 800ea27 commit 75b99ef
Showing 1 changed file with 86 additions and 45 deletions.
131 changes: 86 additions & 45 deletions scripts/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -1718,130 +1718,170 @@ class FipsCfg(object):
syslog.syslog(syslog.LOG_INFO, f'FipsCfg: update the FIPS enforce option {self.enforce}.')
loader.set_fips(image, self.enforce)

import syslog
import os
import psutil
import subprocess
import signal
import time

class MemoryStatisticsCfg:
"""
MemoryStatisticsCfg class handles the configuration updates for the MemoryStatisticsDaemon.
It listens to ConfigDB changes and applies them by restarting, shutting down, or reloading
the MemoryStatisticsDaemon.
The MemoryStatisticsCfg class manages the configuration updates for the MemoryStatisticsDaemon, a daemon
responsible for collecting memory usage statistics. It monitors configuration changes in ConfigDB and, based
on those updates, performs actions such as restarting, shutting down, or reloading the daemon.
Attributes:
VALID_KEYS (list): List of valid configuration keys ("enabled", "sampling_interval", "retention_period").
PID_FILE_PATH (str): Path where the daemon’s process ID (PID) is stored.
DAEMON_EXEC_PATH (str): Path to the executable file of the memory statistics daemon.
DAEMON_PROCESS_NAME (str): Name of the daemon process used for validation.
"""

VALID_KEYS = ["enabled", "sampling_interval", "retention_period"] # Valid keys for configuration
PID_FILE_PATH = '/var/run/memory_statistics_daemon.pid' # Configurable PID file path
DAEMON_EXEC_PATH = '/usr/bin/memory_statistics_service.py' # Configurable path to the daemon executable
DAEMON_PROCESS_NAME = 'memory_statistics_service.py' # The daemon process name for validation
VALID_KEYS = ["enabled", "sampling_interval", "retention_period"]
PID_FILE_PATH = '/var/run/memory_statistics_daemon.pid'
DAEMON_EXEC_PATH = '/usr/bin/memory_statistics_service.py'
DAEMON_PROCESS_NAME = 'memory_statistics_service.py'

def __init__(self, config_db):
"""Initialize MemoryStatisticsCfg with a configuration database."""
"""
Initialize MemoryStatisticsCfg with a configuration database.
Parameters:
config_db (object): Instance of the configuration database (ConfigDB) used to retrieve and
apply configuration changes.
"""
self.cache = {
"enabled": "false",
"sampling_interval": "5",
"retention_period": "15"
}
self.config_db = config_db # Store config_db instance for further use
self.config_db = config_db

def load(self, memory_statistics_config: dict):
"""Load initial memory statistics configuration."""
"""
Load the initial memory statistics configuration from a provided dictionary.
Parameters:
memory_statistics_config (dict): Dictionary containing the initial configuration values.
"""
syslog.syslog(syslog.LOG_INFO, 'MemoryStatisticsCfg: Loading initial configuration')

if not memory_statistics_config:
memory_statistics_config = {}

# Validate and handle each key during the initial load
for key, value in memory_statistics_config.items():
if key not in self.VALID_KEYS:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Invalid key '{key}' in initial configuration.")
continue # Skip invalid keys
self.memory_statistics_update(key, value) # Valid key, apply the setting
continue
self.memory_statistics_update(key, value)

def memory_statistics_update(self, key, data):
"""
Apply memory statistics settings handler.
Args:
key: DB table's key that triggered the change.
data: New table data to process.
Handles updates for each configuration setting, validates the data, and updates the cache if the value changes.
Parameters:
key (str): Configuration key, e.g., "enabled", "sampling_interval", or "retention_period".
data (str): The new value for the configuration key.
"""
if key not in self.VALID_KEYS:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Invalid key '{key}' received.")
return

# Convert data to string if it's not already
data = str(data)
data = str(data)

# Validate numeric keys (retention_period and sampling_interval)
if key in ["retention_period", "sampling_interval"] and (not data.isdigit() or int(data) <= 0):
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Invalid value '{data}' for key '{key}'. Must be a positive integer.")
return

# Check if any value has changed
if data != self.cache.get(key):
syslog.syslog(syslog.LOG_INFO, f"MemoryStatisticsCfg: Detected change in '{key}' to '{data}'")

try:
self.apply_setting(key, data)
self.cache[key] = data # Update cache with the new value
self.cache[key] = data
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f'MemoryStatisticsCfg: Failed to manage MemoryStatisticsDaemon: {e}')

def apply_setting(self, key, data):
"""Apply the setting based on the key."""
if key == "enabled":
if data.lower() == "true":
self.restart_memory_statistics()
"""
Apply the setting based on the key. If "enabled" is set to true or false, start or stop the daemon.
For other keys, reload the daemon configuration.
Parameters:
key (str): The specific configuration setting being updated.
data (str): The value for the setting.
"""
try:
if key == "enabled":
if data.lower() == "true":
self.restart_memory_statistics()
else:
self.shutdown_memory_statistics()
else:
self.shutdown_memory_statistics()
else:
self.reload_memory_statistics()
self.reload_memory_statistics()
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Exception in apply_setting() for key '{key}': {e}")

def restart_memory_statistics(self):
"""Restart the memory statistics daemon."""
self.shutdown_memory_statistics() # Stop the daemon before restarting
time.sleep(1) # Optional delay to ensure proper restart
syslog.syslog(syslog.LOG_INFO, "MemoryStatisticsCfg: Starting MemoryStatisticsDaemon")

"""Restarts the memory statistics daemon by first shutting it down (if running) and then starting it again."""
try:
subprocess.Popen([self.DAEMON_EXEC_PATH]) # Start the daemon using the configured path
self.shutdown_memory_statistics()
time.sleep(1)
syslog.syslog(syslog.LOG_INFO, "MemoryStatisticsCfg: Starting MemoryStatisticsDaemon")
subprocess.Popen([self.DAEMON_EXEC_PATH])
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Failed to start MemoryStatisticsDaemon: {e}")

def reload_memory_statistics(self):
"""Send SIGHUP to the MemoryStatisticsDaemon to reload its configuration."""
"""Sends a SIGHUP signal to the daemon to reload its configuration without restarting."""
pid = self.get_memory_statistics_pid()
if pid:
try:
os.kill(pid, signal.SIGHUP) # Notify daemon to reload its configuration
os.kill(pid, signal.SIGHUP)
syslog.syslog(syslog.LOG_INFO, "MemoryStatisticsCfg: Sent SIGHUP to reload daemon configuration")
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Failed to reload MemoryStatisticsDaemon: {e}")

def shutdown_memory_statistics(self):
"""Send SIGTERM to stop the MemoryStatisticsDaemon gracefully."""
"""Sends a SIGTERM signal to gracefully shut down the daemon."""
pid = self.get_memory_statistics_pid()
if pid:
try:
os.kill(pid, signal.SIGTERM) # Graceful shutdown
os.kill(pid, signal.SIGTERM)
syslog.syslog(syslog.LOG_INFO, "MemoryStatisticsCfg: Sent SIGTERM to stop MemoryStatisticsDaemon")
self.wait_for_shutdown(pid)
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Failed to shutdown MemoryStatisticsDaemon: {e}")

def wait_for_shutdown(self, pid, timeout=10):
"""Wait for the daemon to shut down gracefully."""
"""
Waits for the daemon process to terminate gracefully within a given timeout.
Parameters:
pid (int): Process ID of the daemon to shut down.
timeout (int): Maximum wait time in seconds for the process to terminate (default is 10 seconds).
"""
try:
process = psutil.Process(pid)
process.wait(timeout=timeout) # Wait for the process to terminate
process.wait(timeout=timeout)
syslog.syslog(syslog.LOG_INFO, "MemoryStatisticsCfg: MemoryStatisticsDaemon stopped gracefully")
except psutil.TimeoutExpired:
syslog.syslog(syslog.LOG_WARNING, f"MemoryStatisticsCfg: Timed out while waiting for daemon (PID {pid}) to shut down.")
except psutil.NoSuchProcess:
syslog.syslog(syslog.LOG_WARNING, "MemoryStatisticsCfg: MemoryStatisticsDaemon process not found.")
except Exception as e:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Exception in wait_for_shutdown(): {e}")

def get_memory_statistics_pid(self):
"""Retrieve the PID of the running MemoryStatisticsDaemon."""
"""
Retrieves the PID of the currently running daemon from the PID file, verifying it matches the expected daemon.
Returns:
int or None: Returns the PID if the process is running and matches the expected daemon; otherwise, returns None.
"""
try:
with open(self.PID_FILE_PATH, 'r') as pid_file:
pid = int(pid_file.read().strip())
# Verify if the PID is for the correct process
if psutil.pid_exists(pid):
process = psutil.Process(pid)
if process.name() == self.DAEMON_PROCESS_NAME:
Expand All @@ -1858,6 +1898,7 @@ class MemoryStatisticsCfg:
syslog.syslog(syslog.LOG_ERR, f"MemoryStatisticsCfg: Failed to retrieve MemoryStatisticsDaemon PID: {e}")
return None


class SerialConsoleCfg:

def __init__(self):
Expand Down

0 comments on commit 75b99ef

Please sign in to comment.