Skip to content

Commit 9f1eab4

Browse files
authored
Memory Statistics Config and Show Commands (#3575)
What I did Provided support for the configuration and show commands of memory statistics feature. How I did it I added a new config file config/memory_statistics.py for the configuration commands of memory statistics feature. Added a show file show/memory_statistics.py for the show commands of memory statistics feature. Added a test file tests/memory_statistics_test.py to test the config and show commands of memory statistics feature How to verify it Cli support for config and show file is available
1 parent 595c2aa commit 9f1eab4

5 files changed

+2366
-1
lines changed

config/memory_statistics.py

+331
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
# Standard library imports
2+
import syslog
3+
4+
# Third-party imports
5+
import click
6+
7+
# Type hints
8+
from typing import Tuple, Optional
9+
10+
# Local imports
11+
from swsscommon.swsscommon import ConfigDBConnector
12+
13+
# Constants
14+
MEMORY_STATISTICS_TABLE = "MEMORY_STATISTICS"
15+
MEMORY_STATISTICS_KEY = "memory_statistics"
16+
17+
SAMPLING_INTERVAL_MIN = 3
18+
SAMPLING_INTERVAL_MAX = 15
19+
RETENTION_PERIOD_MIN = 1
20+
RETENTION_PERIOD_MAX = 30
21+
22+
DEFAULT_SAMPLING_INTERVAL = 5 # minutes
23+
DEFAULT_RETENTION_PERIOD = 15 # days
24+
25+
syslog.openlog("memory_statistics_config", syslog.LOG_PID | syslog.LOG_CONS, syslog.LOG_USER)
26+
27+
28+
def log_to_syslog(message: str, level: int = syslog.LOG_INFO) -> None:
29+
"""
30+
Logs a message to the system log (syslog) with error handling.
31+
32+
This function logs the provided message to syslog at the specified level.
33+
It handles potential errors such as system-related issues (OSError) and
34+
invalid parameters (ValueError) by displaying appropriate error messages.
35+
36+
Args:
37+
message (str): The message to log.
38+
level (int, optional): The log level (default is syslog.LOG_INFO).
39+
"""
40+
try:
41+
syslog.syslog(level, message)
42+
except OSError as e:
43+
click.echo(f"System error while logging to syslog: {e}", err=True)
44+
except ValueError as e:
45+
click.echo(f"Invalid syslog parameters: {e}", err=True)
46+
47+
48+
def generate_error_message(error_type: str, error: Exception) -> str:
49+
"""
50+
Generates a formatted error message for logging and user feedback.
51+
52+
Args:
53+
error_type (str): A short description of the error type.
54+
error (Exception): The actual exception object.
55+
56+
Returns:
57+
str: A formatted error message string.
58+
"""
59+
return f"{error_type}: {error}"
60+
61+
62+
def validate_range(value: int, min_val: int, max_val: int) -> bool:
63+
"""
64+
Validates whether a given integer value falls within a specified range.
65+
66+
Args:
67+
value (int): The value to validate.
68+
min_val (int): The minimum allowable value.
69+
max_val (int): The maximum allowable value.
70+
71+
Returns:
72+
bool: True if the value is within range, False otherwise.
73+
"""
74+
return min_val <= value <= max_val
75+
76+
77+
class MemoryStatisticsDB:
78+
"""
79+
Singleton class for managing the connection to the memory statistics configuration database.
80+
"""
81+
_instance = None
82+
_db = None
83+
84+
def __new__(cls):
85+
"""
86+
Creates and returns a singleton instance of MemoryStatisticsDB.
87+
88+
Returns:
89+
MemoryStatisticsDB: The singleton instance.
90+
"""
91+
if cls._instance is None:
92+
cls._instance = super(MemoryStatisticsDB, cls).__new__(cls)
93+
cls._connect_db()
94+
return cls._instance
95+
96+
@classmethod
97+
def _connect_db(cls):
98+
"""
99+
Establishes a connection to the ConfigDB database.
100+
101+
Logs an error if the connection fails.
102+
"""
103+
try:
104+
cls._db = ConfigDBConnector()
105+
cls._db.connect()
106+
except RuntimeError as e:
107+
log_to_syslog(f"ConfigDB connection failed: {e}", syslog.LOG_ERR)
108+
cls._db = None
109+
110+
@classmethod
111+
def get_db(cls):
112+
"""
113+
Retrieves the database connection instance, reconnecting if necessary.
114+
115+
Returns:
116+
ConfigDBConnector: The active database connection.
117+
118+
Raises:
119+
RuntimeError: If the database connection is unavailable.
120+
"""
121+
if cls._db is None:
122+
cls._connect_db()
123+
if cls._db is None:
124+
raise RuntimeError("Database connection unavailable")
125+
return cls._db
126+
127+
128+
def update_memory_statistics_status(enabled: bool) -> Tuple[bool, Optional[str]]:
129+
"""
130+
Updates the enable/disable status of memory statistics in the configuration database.
131+
132+
This function modifies the configuration database to enable or disable
133+
memory statistics collection based on the provided status. It also logs
134+
the action and returns a tuple indicating whether the operation was successful.
135+
136+
Args:
137+
enabled (bool): True to enable memory statistics, False to disable.
138+
139+
Returns:
140+
Tuple[bool, Optional[str]]: A tuple containing success status and an optional error message.
141+
"""
142+
try:
143+
db = MemoryStatisticsDB.get_db()
144+
145+
db.mod_entry(MEMORY_STATISTICS_TABLE, MEMORY_STATISTICS_KEY, {"enabled": str(enabled).lower()})
146+
msg = f"Memory statistics feature {'enabled' if enabled else 'disabled'} successfully."
147+
click.echo(msg)
148+
log_to_syslog(msg)
149+
return True, None
150+
except (KeyError, ConnectionError, RuntimeError) as e:
151+
error_msg = generate_error_message(f"Failed to {'enable' if enabled else 'disable'} memory statistics", e)
152+
153+
click.echo(error_msg, err=True)
154+
log_to_syslog(error_msg, syslog.LOG_ERR)
155+
return False, error_msg
156+
157+
158+
@click.group(help="Tool to manage memory statistics configuration.")
159+
def cli():
160+
"""
161+
Memory statistics configuration tool.
162+
163+
This command-line interface (CLI) allows users to configure and manage
164+
memory statistics settings such as enabling/disabling the feature and
165+
modifying parameters like the sampling interval and retention period.
166+
"""
167+
pass
168+
169+
170+
@cli.group(help="Commands to configure system settings.")
171+
def config():
172+
"""
173+
Configuration commands for managing memory statistics.
174+
175+
Example:
176+
$ config memory-stats enable
177+
$ config memory-stats sampling-interval 5
178+
"""
179+
pass
180+
181+
182+
@config.group(name='memory-stats', help="Manage memory statistics collection settings.")
183+
def memory_stats():
184+
"""Configure memory statistics collection and settings.
185+
186+
This group contains commands to enable/disable memory statistics collection
187+
and configure related parameters.
188+
189+
Examples:
190+
Enable memory statistics:
191+
$ config memory-stats enable
192+
193+
Set sampling interval to 5 minutes:
194+
$ config memory-stats sampling-interval 5
195+
196+
Set retention period to 7 days:
197+
$ config memory-stats retention-period 7
198+
199+
Disable memory statistics:
200+
$ config memory-stats disable
201+
"""
202+
pass
203+
204+
205+
@memory_stats.command(name='enable')
206+
def memory_stats_enable():
207+
"""Enable memory statistics collection.
208+
209+
This command enables the collection of memory statistics on the device.
210+
It updates the configuration and reminds the user to run 'config save'
211+
to persist changes.
212+
213+
Example:
214+
$ config memory-stats enable
215+
Memory statistics feature enabled successfully.
216+
Reminder: Please run 'config save' to persist changes.
217+
"""
218+
success, error = update_memory_statistics_status(True)
219+
if success:
220+
click.echo("Reminder: Please run 'config save' to persist changes.")
221+
log_to_syslog("Memory statistics enabled. Reminder to run 'config save'")
222+
223+
224+
@memory_stats.command(name='disable')
225+
def memory_stats_disable():
226+
"""Disable memory statistics collection.
227+
228+
This command disables the collection of memory statistics on the device.
229+
It updates the configuration and reminds the user to run 'config save'
230+
to persist changes.
231+
232+
Example:
233+
$ config memory-stats disable
234+
Memory statistics feature disabled successfully.
235+
Reminder: Please run 'config save' to persist changes.
236+
"""
237+
success, error = update_memory_statistics_status(False)
238+
if success:
239+
click.echo("Reminder: Please run 'config save' to persist changes.")
240+
log_to_syslog("Memory statistics disabled. Reminder to run 'config save'")
241+
242+
243+
@memory_stats.command(name='sampling-interval')
244+
@click.argument("interval", type=int)
245+
def memory_stats_sampling_interval(interval: int):
246+
"""
247+
Configure the sampling interval for memory statistics collection.
248+
249+
This command updates the interval at which memory statistics are collected.
250+
The interval must be between 3 and 15 minutes.
251+
252+
Args:
253+
interval (int): The desired sampling interval in minutes.
254+
255+
Examples:
256+
Set sampling interval to 5 minutes:
257+
$ config memory-stats sampling-interval 5
258+
Sampling interval set to 5 minutes successfully.
259+
Reminder: Please run 'config save' to persist changes.
260+
261+
Invalid interval example:
262+
$ config memory-stats sampling-interval 20
263+
Error: Sampling interval must be between 3 and 15 minutes.
264+
"""
265+
if not validate_range(interval, SAMPLING_INTERVAL_MIN, SAMPLING_INTERVAL_MAX):
266+
error_msg = f"Error: Sampling interval must be between {SAMPLING_INTERVAL_MIN} and {SAMPLING_INTERVAL_MAX}."
267+
click.echo(error_msg, err=True)
268+
log_to_syslog(error_msg, syslog.LOG_ERR)
269+
return
270+
271+
try:
272+
db = MemoryStatisticsDB.get_db()
273+
db.mod_entry(MEMORY_STATISTICS_TABLE, MEMORY_STATISTICS_KEY, {"sampling_interval": str(interval)})
274+
success_msg = f"Sampling interval set to {interval} minutes successfully."
275+
click.echo(success_msg)
276+
log_to_syslog(success_msg)
277+
click.echo("Reminder: Please run 'config save' to persist changes.")
278+
except (KeyError, ConnectionError, ValueError, RuntimeError) as e:
279+
error_msg = generate_error_message(f"{type(e).__name__} setting sampling interval", e)
280+
click.echo(error_msg, err=True)
281+
log_to_syslog(error_msg, syslog.LOG_ERR)
282+
return
283+
284+
285+
@memory_stats.command(name='retention-period')
286+
@click.argument("period", type=int)
287+
def memory_stats_retention_period(period: int):
288+
"""
289+
Configure the retention period for memory statistics storage.
290+
291+
This command sets the number of days memory statistics should be retained.
292+
The retention period must be between 1 and 30 days.
293+
294+
Args:
295+
period (int): The desired retention period in days.
296+
297+
Examples:
298+
Set retention period to 7 days:
299+
$ config memory-stats retention-period 7
300+
Retention period set to 7 days successfully.
301+
Reminder: Please run 'config save' to persist changes.
302+
303+
Invalid period example:
304+
$ config memory-stats retention-period 45
305+
Error: Retention period must be between 1 and 30 days.
306+
"""
307+
if not validate_range(period, RETENTION_PERIOD_MIN, RETENTION_PERIOD_MAX):
308+
error_msg = f"Error: Retention period must be between {RETENTION_PERIOD_MIN} and {RETENTION_PERIOD_MAX}."
309+
click.echo(error_msg, err=True)
310+
log_to_syslog(error_msg, syslog.LOG_ERR)
311+
return
312+
313+
try:
314+
db = MemoryStatisticsDB.get_db()
315+
db.mod_entry(MEMORY_STATISTICS_TABLE, MEMORY_STATISTICS_KEY, {"retention_period": str(period)})
316+
success_msg = f"Retention period set to {period} days successfully."
317+
click.echo(success_msg)
318+
log_to_syslog(success_msg)
319+
click.echo("Reminder: Please run 'config save' to persist changes.")
320+
except (KeyError, ConnectionError, ValueError, RuntimeError) as e:
321+
error_msg = generate_error_message(f"{type(e).__name__} setting retention period", e)
322+
click.echo(error_msg, err=True)
323+
log_to_syslog(error_msg, syslog.LOG_ERR)
324+
return
325+
326+
327+
if __name__ == "__main__":
328+
try:
329+
cli()
330+
finally:
331+
syslog.closelog()

0 commit comments

Comments
 (0)