@@ -40,6 +40,74 @@ def cli():
4040 pass
4141
4242
43+ @cli .command ()
44+ def list_profiles ():
45+ """List available workload profiles."""
46+ profiles = WorkloadProfiles .list_profiles ()
47+ click .echo ("Available workload profiles:" )
48+ for profile in profiles :
49+ workload = WorkloadProfiles .get_profile (profile )
50+ operations = workload .get_option ("operations" , [workload .type ])
51+ if isinstance (operations , list ):
52+ ops_str = ', ' .join (operations )
53+ else :
54+ ops_str = workload .type
55+ click .echo (f" { profile } : { ops_str } " )
56+
57+
58+ @cli .command ()
59+ @click .argument ('profile_name' , type = click .Choice (WorkloadProfiles .list_profiles ()))
60+ def describe_profile (profile_name ):
61+ """Describe a specific workload profile."""
62+ workload = WorkloadProfiles .get_profile (profile_name )
63+
64+ click .echo (f"Workload Profile: { profile_name } " )
65+ click .echo (f"Type: { workload .type } " )
66+ click .echo (f"Duration: { workload .max_duration } " )
67+
68+ # Show operations if available
69+ operations = workload .get_option ("operations" )
70+ if operations :
71+ click .echo (f"Operations: { ', ' .join (operations )} " )
72+
73+ # Show operation weights if available
74+ weights = workload .get_option ("operation_weights" )
75+ if weights :
76+ click .echo ("Operation Weights:" )
77+ for op , weight in weights .items ():
78+ click .echo (f" { op } : { weight } " )
79+
80+ # Show key configuration options
81+ value_size = workload .get_option ("valueSize" )
82+ if value_size :
83+ click .echo (f"Value Size: { value_size } bytes" )
84+
85+ iteration_count = workload .get_option ("iterationCount" )
86+ if iteration_count :
87+ click .echo (f"Iteration Count: { iteration_count } " )
88+
89+ # Show pipeline configuration
90+ use_pipeline = workload .get_option ("usePipeline" , False )
91+ click .echo (f"Pipeline: { 'Yes' if use_pipeline else 'No' } " )
92+ if use_pipeline :
93+ pipeline_size = workload .get_option ("pipelineSize" , 10 )
94+ click .echo (f"Pipeline Size: { pipeline_size } " )
95+
96+ # Show async configuration
97+ async_mode = workload .get_option ("asyncMode" , False )
98+ click .echo (f"Async Mode: { 'Yes' if async_mode else 'No' } " )
99+
100+ # Show channels if available
101+ channels = workload .get_option ("channels" )
102+ if channels :
103+ click .echo (f"Channels: { ', ' .join (channels )} " )
104+
105+ # Show all other options
106+ click .echo ("All Options:" )
107+ for key , value in workload .options .items ():
108+ click .echo (f" { key } : { value } " )
109+
110+
43111@cli .command ()
44112# ============================================================================
45113# Redis Connection Parameters
@@ -55,11 +123,12 @@ def cli():
55123@click .option ('--ssl-ca-certs' , default = lambda : get_env_or_default ('REDIS_SSL_CA_CERTS' , None ), help = 'Path to CA certificates file' )
56124@click .option ('--ssl-certfile' , default = lambda : get_env_or_default ('REDIS_SSL_CERTFILE' , None ), help = 'Path to client certificate file' )
57125@click .option ('--ssl-keyfile' , default = lambda : get_env_or_default ('REDIS_SSL_KEYFILE' , None ), help = 'Path to client private key file' )
58- @click .option ('--socket-timeout' , type = float , default = lambda : get_env_or_default ('REDIS_SOCKET_TIMEOUT' , 5.0 , float ), help = 'Socket timeout in seconds' )
59- @click .option ('--socket-connect-timeout' , type = float , default = lambda : get_env_or_default ('REDIS_SOCKET_CONNECT_TIMEOUT' , 5.0 , float ), help = 'Socket connect timeout in seconds' )
126+ @click .option ('--socket-timeout' , type = float , default = lambda : get_env_or_default ('REDIS_SOCKET_TIMEOUT' , None ), help = 'Socket timeout in seconds' )
127+ @click .option ('--socket-connect-timeout' , type = float , default = lambda : get_env_or_default ('REDIS_SOCKET_CONNECT_TIMEOUT' , None ), help = 'Socket connect timeout in seconds' )
60128@click .option ('--max-connections' , type = int , default = lambda : get_env_or_default ('REDIS_MAX_CONNECTIONS' , 50 , int ), help = 'Maximum connections per client' )
61129@click .option ('--client-retry-attempts' , type = int , default = lambda : get_env_or_default ('REDIS_CLIENT_RETRY_ATTEMPTS' , 3 , int ), help = 'Number of client-level retry attempts for network/connection issues (uses redis-py Retry class)' )
62- @click .option ('--maintenance-events-enabled' , type = bool , default = lambda : get_env_or_default ('REDIS_MAINT_EVENTS_ENABLED' , True , bool ), help = 'Server maintenance events (hitless upgrades push notifications)' )
130+ @click .option ('--maintenance-notifications-enabled' , type = bool , default = lambda : get_env_or_default ('REDIS_MAINT_NOTIFICATIONS_ENABLED' , True , bool ), help = 'Server maintenance events (hitless upgrades push notifications)' )
131+ @click .option ('--maintenance-relaxed-timeout' , type = float , default = lambda : get_env_or_default ('REDIS_MAINT_RELAXED_TIMEOUT' , None ), help = 'Relaxedimeout during maintenance events' )
63132@click .option ('--protocol' ,type = int , default = lambda : get_env_or_default ('REDIS_PROTOCOL' , 3 , int ), help = 'RESP Version (2 or 3)' )
64133
65134# ============================================================================
@@ -123,7 +192,7 @@ def cli():
123192@click .option ('--save-config' , help = 'Save current configuration to file' )
124193def run (** kwargs ):
125194 """Run Redis load test with specified configuration."""
126-
195+
127196 try :
128197 # Load configuration from file if specified
129198 if kwargs ['config_file' ]:
@@ -140,89 +209,21 @@ def run(**kwargs):
140209 save_config_to_file (config , kwargs ['save_config' ])
141210 click .echo (f"Configuration saved to { kwargs ['save_config' ]} " )
142211 return
143-
212+
144213 # Validate configuration
145214 _validate_config (config )
146-
215+
147216 # Run the test
148217 runner = TestRunner (config )
149218 runner .start ()
150-
219+
151220 except KeyboardInterrupt :
152221 click .echo ("\n Test interrupted by user" )
153222 sys .exit (0 )
154223 except Exception as e :
155224 click .echo (f"Error: { e } " , err = True )
156225 sys .exit (1 )
157226
158-
159- @cli .command ()
160- def list_profiles ():
161- """List available workload profiles."""
162- profiles = WorkloadProfiles .list_profiles ()
163- click .echo ("Available workload profiles:" )
164- for profile in profiles :
165- workload = WorkloadProfiles .get_profile (profile )
166- operations = workload .get_option ("operations" , [workload .type ])
167- if isinstance (operations , list ):
168- ops_str = ', ' .join (operations )
169- else :
170- ops_str = workload .type
171- click .echo (f" { profile } : { ops_str } " )
172-
173-
174- @cli .command ()
175- @click .argument ('profile_name' , type = click .Choice (WorkloadProfiles .list_profiles ()))
176- def describe_profile (profile_name ):
177- """Describe a specific workload profile."""
178- workload = WorkloadProfiles .get_profile (profile_name )
179-
180- click .echo (f"Workload Profile: { profile_name } " )
181- click .echo (f"Type: { workload .type } " )
182- click .echo (f"Duration: { workload .max_duration } " )
183-
184- # Show operations if available
185- operations = workload .get_option ("operations" )
186- if operations :
187- click .echo (f"Operations: { ', ' .join (operations )} " )
188-
189- # Show operation weights if available
190- weights = workload .get_option ("operation_weights" )
191- if weights :
192- click .echo ("Operation Weights:" )
193- for op , weight in weights .items ():
194- click .echo (f" { op } : { weight } " )
195-
196- # Show key configuration options
197- value_size = workload .get_option ("valueSize" )
198- if value_size :
199- click .echo (f"Value Size: { value_size } bytes" )
200-
201- iteration_count = workload .get_option ("iterationCount" )
202- if iteration_count :
203- click .echo (f"Iteration Count: { iteration_count } " )
204-
205- # Show pipeline configuration
206- use_pipeline = workload .get_option ("usePipeline" , False )
207- click .echo (f"Pipeline: { 'Yes' if use_pipeline else 'No' } " )
208- if use_pipeline :
209- pipeline_size = workload .get_option ("pipelineSize" , 10 )
210- click .echo (f"Pipeline Size: { pipeline_size } " )
211-
212- # Show async configuration
213- async_mode = workload .get_option ("asyncMode" , False )
214- click .echo (f"Async Mode: { 'Yes' if async_mode else 'No' } " )
215-
216- # Show channels if available
217- channels = workload .get_option ("channels" )
218- if channels :
219- click .echo (f"Channels: { ', ' .join (channels )} " )
220-
221- # Show all other options
222- click .echo ("All Options:" )
223- for key , value in workload .options .items ():
224- click .echo (f" { key } : { value } " )
225-
226227@cli .command ()
227228def test_connection ():
228229 """Test Redis connection with current configuration."""
@@ -236,19 +237,19 @@ def test_connection():
236237 cluster_mode = get_env_or_default ('REDIS_CLUSTER' , False , bool ),
237238 ssl = get_env_or_default ('REDIS_SSL' , False , bool )
238239 )
239-
240+
240241 from redis_client import RedisClient
241242 client = RedisClient (redis_config )
242-
243+
243244 # Test basic operations
244245 client .ping ()
245246 info = client .get_info ()
246-
247+
247248 click .echo ("✓ Redis connection successful!" )
248249 click .echo (f"Redis version: { info .get ('redis_version' , 'unknown' )} " )
249250 click .echo (f"Redis mode: { 'cluster' if redis_config .cluster_mode else 'standalone' } " )
250251 click .echo (f"Connected clients: { info .get ('connected_clients' , 'unknown' )} " )
251-
252+
252253 client .close ()
253254
254255 except Exception as e :
@@ -293,7 +294,8 @@ def _build_config_from_args(kwargs) -> RunnerConfig:
293294 socket_connect_timeout = kwargs ['socket_connect_timeout' ],
294295 max_connections = kwargs ['max_connections' ],
295296 client_retry_attempts = kwargs ['client_retry_attempts' ],
296- maintenance_events_enabled = kwargs ['maintenance_events_enabled' ]
297+ maintenance_notifications_enabled = kwargs ['maintenance_notifications_enabled' ],
298+ maintenance_relaxed_timeout = kwargs ['maintenance_relaxed_timeout' ]
297299 )
298300
299301
0 commit comments