Skip to content

Commit 50e66df

Browse files
Add fault injector client for Redis Enterprise testing
This commit introduces a comprehensive fault injector client that provides a Python interface for interacting with Redis Enterprise fault injection services. The client supports: - Multiple action types (DMC restart, failover, reshard, network failure) - Direct rladmin command execution - Sequence of actions for complex testing scenarios - Full HTTP API integration with proper error handling - Debug output for troubleshooting test scenarios The fault injector client enables automated testing of Redis Enterprise cluster resilience and failover scenarios, supporting the CAE client testing framework approach for real enterprise server integration.
1 parent 78fb85e commit 50e66df

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

tests/test_scenario/__init__.py

Whitespace-only changes.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import json
2+
import urllib.request
3+
from typing import Dict, Any, Optional, Union
4+
from enum import Enum
5+
6+
7+
class ActionType(str, Enum):
8+
DMC_RESTART = "dmc_restart"
9+
FAILOVER = "failover"
10+
RESHARD = "reshard"
11+
SEQUENCE_OF_ACTIONS = "sequence_of_actions"
12+
NETWORK_FAILURE = "network_failure"
13+
EXECUTE_RLUTIL_COMMAND = "execute_rlutil_command"
14+
EXECUTE_RLADMIN_COMMAND = "execute_rladmin_command"
15+
16+
17+
class RestartDmcParams:
18+
def __init__(self, bdb_id: str):
19+
self.bdb_id = bdb_id
20+
21+
def to_dict(self) -> Dict[str, str]:
22+
return {"bdb_id": self.bdb_id}
23+
24+
25+
class ActionRequest:
26+
def __init__(self, action_type: ActionType, parameters: Union[Dict[str, Any], RestartDmcParams]):
27+
self.type = action_type
28+
self.parameters = parameters
29+
30+
def to_dict(self) -> Dict[str, Any]:
31+
return {
32+
"type": self.type.value, # Use the string value of the enum
33+
"parameters": self.parameters.to_dict() if isinstance(self.parameters,
34+
RestartDmcParams) else self.parameters
35+
}
36+
37+
38+
class FaultInjectorClient:
39+
def __init__(self, base_url: str):
40+
self.base_url = base_url.rstrip('/')
41+
42+
def _make_request(self, method: str, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
43+
url = f"{self.base_url}{path}"
44+
headers = {"Content-Type": "application/json"} if data else {}
45+
46+
request_data = None
47+
if data:
48+
request_data = json.dumps(data).encode('utf-8')
49+
print(f"JSON payload being sent: {request_data.decode('utf-8')}")
50+
51+
request = urllib.request.Request(
52+
url,
53+
method=method,
54+
data=request_data,
55+
headers=headers
56+
)
57+
58+
try:
59+
with urllib.request.urlopen(request) as response:
60+
return json.loads(response.read().decode('utf-8'))
61+
except urllib.error.HTTPError as e:
62+
if e.code == 422:
63+
error_body = json.loads(e.read().decode('utf-8'))
64+
raise ValueError(f"Validation Error: {error_body}")
65+
raise
66+
67+
def list_actions(self) -> Dict[str, Any]:
68+
"""List all available actions"""
69+
return self._make_request("GET", "/action")
70+
71+
def trigger_action(self, action_request: ActionRequest) -> Dict[str, Any]:
72+
"""Trigger a new action"""
73+
request_data = action_request.to_dict()
74+
print(f"Sending HTTP request data: {request_data}")
75+
return self._make_request("POST", "/action", request_data)
76+
77+
def get_action_status(self, action_id: str) -> Dict[str, Any]:
78+
"""Get the status of a specific action"""
79+
return self._make_request("GET", f"/action/{action_id}")
80+
81+
def execute_rladmin_command(self, command: str, bdb_id: str = None) -> Dict[str, Any]:
82+
"""Execute rladmin command directly as string"""
83+
url = f"{self.base_url}/rladmin"
84+
85+
# The fault injector expects the raw command string
86+
command_string = f"rladmin {command}"
87+
if bdb_id:
88+
command_string = f"rladmin -b {bdb_id} {command}"
89+
90+
print(f"Sending rladmin command: {command_string}")
91+
92+
headers = {"Content-Type": "text/plain"}
93+
94+
request = urllib.request.Request(
95+
url,
96+
method="POST",
97+
data=command_string.encode('utf-8'),
98+
headers=headers
99+
)
100+
101+
try:
102+
with urllib.request.urlopen(request) as response:
103+
return json.loads(response.read().decode('utf-8'))
104+
except urllib.error.HTTPError as e:
105+
if e.code == 422:
106+
error_body = json.loads(e.read().decode('utf-8'))
107+
raise ValueError(f"Validation Error: {error_body}")
108+
raise

0 commit comments

Comments
 (0)