redis_enterprise/
nodes.rs

1//! Nodes management for Redis Enterprise
2//!
3//! ## Overview
4//! - List and query resources
5//! - Create and update configurations
6//! - Monitor status and metrics
7
8use crate::client::RestClient;
9use crate::error::Result;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use typed_builder::TypedBuilder;
13
14/// Response from node action operations
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct NodeActionResponse {
17    /// The action UID for tracking async operations
18    pub action_uid: String,
19    /// Description of the action
20    pub description: Option<String>,
21    /// Additional fields from the response
22    #[serde(flatten)]
23    pub extra: Value,
24}
25
26/// Node information
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Node {
29    /// Cluster unique ID of node (read-only)
30    pub uid: u32,
31
32    /// Internal IP address of node
33    #[serde(rename = "addr")]
34    pub addr: Option<String>,
35
36    /// Node status (read-only)
37    pub status: String,
38
39    /// Node accepts new shards if true
40    pub accept_servers: Option<bool>,
41
42    /// Hardware architecture (read-only)
43    pub architecture: Option<String>,
44
45    /// Total number of CPU cores (read-only)
46    #[serde(rename = "cores")]
47    pub cores: Option<u32>,
48
49    /// External IP addresses of node
50    pub external_addr: Option<Vec<String>>,
51
52    /// Total memory in bytes
53    pub total_memory: Option<u64>,
54
55    /// Installed OS version (read-only)
56    pub os_version: Option<String>,
57    /// Operating system name (read-only)
58    pub os_name: Option<String>,
59    /// Operating system family (read-only)
60    pub os_family: Option<String>,
61    /// Full version number (read-only)
62    pub os_semantic_version: Option<String>,
63
64    /// Ephemeral storage size in bytes (read-only)
65    pub ephemeral_storage_size: Option<f64>,
66    /// Persistent storage size in bytes (read-only)
67    pub persistent_storage_size: Option<f64>,
68
69    /// Ephemeral storage path (read-only)
70    pub ephemeral_storage_path: Option<String>,
71    /// Persistent storage path (read-only)
72    pub persistent_storage_path: Option<String>,
73    /// Flash storage path (read-only)
74    pub bigredis_storage_path: Option<String>,
75
76    /// Rack ID where node is installed
77    pub rack_id: Option<String>,
78    /// Second rack ID where node is installed
79    pub second_rack_id: Option<String>,
80
81    /// Number of shards on the node (read-only)
82    pub shard_count: Option<u32>,
83    /// Cluster unique IDs of all node shards
84    pub shard_list: Option<Vec<u32>>,
85    /// RAM shard count
86    pub ram_shard_count: Option<u32>,
87    /// Flash shard count
88    pub flash_shard_count: Option<u32>,
89
90    /// Flash storage enabled for Auto Tiering databases
91    pub bigstore_enabled: Option<bool>,
92    /// FIPS mode enabled
93    pub fips_enabled: Option<bool>,
94    /// Use internal IPv6
95    pub use_internal_ipv6: Option<bool>,
96
97    /// Maximum number of listeners on the node
98    pub max_listeners: Option<u32>,
99    /// Maximum number of shards on the node
100    pub max_redis_servers: Option<u32>,
101    /// Maximum background processes forked from shards
102    pub max_redis_forks: Option<i32>,
103    /// Maximum simultaneous replica full syncs
104    pub max_slave_full_syncs: Option<i32>,
105
106    /// Node uptime in seconds
107    pub uptime: Option<u64>,
108    /// Installed Redis Enterprise cluster software version (read-only)
109    pub software_version: Option<String>,
110
111    /// Supported database versions
112    pub supported_database_versions: Option<Vec<Value>>,
113
114    /// Bigstore driver name (deprecated)
115    pub bigstore_driver: Option<String>,
116
117    /// Storage size of bigstore storage (read-only)
118    pub bigstore_size: Option<u64>,
119
120    /// Public IP address (deprecated)
121    pub public_addr: Option<String>,
122
123    /// Recovery files path
124    pub recovery_path: Option<String>,
125
126    /// Capture any additional fields not explicitly defined
127    #[serde(flatten)]
128    pub extra: Value,
129}
130
131/// Node stats
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct NodeStats {
134    pub uid: u32,
135    pub cpu_user: Option<f64>,
136    pub cpu_system: Option<f64>,
137    pub cpu_idle: Option<f64>,
138    pub free_memory: Option<u64>,
139    pub network_bytes_in: Option<u64>,
140    pub network_bytes_out: Option<u64>,
141    pub persistent_storage_free: Option<u64>,
142    pub ephemeral_storage_free: Option<u64>,
143
144    #[serde(flatten)]
145    pub extra: Value,
146}
147
148/// Node action request
149#[derive(Debug, Serialize, TypedBuilder)]
150pub struct NodeActionRequest {
151    #[builder(setter(into))]
152    pub action: String,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    #[builder(default, setter(strip_option))]
155    pub node_uid: Option<u32>,
156}
157
158/// Node handler for executing node commands
159pub struct NodeHandler {
160    client: RestClient,
161}
162
163/// Alias for backwards compatibility and intuitive plural naming
164pub type NodesHandler = NodeHandler;
165
166impl NodeHandler {
167    pub fn new(client: RestClient) -> Self {
168        NodeHandler { client }
169    }
170
171    /// List all nodes
172    pub async fn list(&self) -> Result<Vec<Node>> {
173        self.client.get("/v1/nodes").await
174    }
175
176    /// Get specific node info
177    pub async fn get(&self, uid: u32) -> Result<Node> {
178        self.client.get(&format!("/v1/nodes/{}", uid)).await
179    }
180
181    /// Update node configuration
182    pub async fn update(&self, uid: u32, updates: Value) -> Result<Node> {
183        self.client
184            .put(&format!("/v1/nodes/{}", uid), &updates)
185            .await
186    }
187
188    /// Remove node from cluster
189    pub async fn remove(&self, uid: u32) -> Result<()> {
190        self.client.delete(&format!("/v1/nodes/{}", uid)).await
191    }
192
193    /// Get node stats
194    pub async fn stats(&self, uid: u32) -> Result<NodeStats> {
195        self.client.get(&format!("/v1/nodes/{}/stats", uid)).await
196    }
197
198    /// Get node actions
199    pub async fn actions(&self, uid: u32) -> Result<Value> {
200        self.client.get(&format!("/v1/nodes/{}/actions", uid)).await
201    }
202
203    /// Execute node action (e.g., "maintenance_on", "maintenance_off")
204    pub async fn execute_action(&self, uid: u32, action: &str) -> Result<NodeActionResponse> {
205        let request = NodeActionRequest {
206            action: action.to_string(),
207            node_uid: Some(uid),
208        };
209        self.client
210            .post(&format!("/v1/nodes/{}/actions", uid), &request)
211            .await
212    }
213
214    // raw variant removed in favor of typed execute_action
215
216    /// List all available node actions (global) - GET /v1/nodes/actions
217    pub async fn list_actions(&self) -> Result<Value> {
218        self.client.get("/v1/nodes/actions").await
219    }
220
221    /// Get node action detail - GET /v1/nodes/{uid}/actions/{action}
222    pub async fn action_detail(&self, uid: u32, action: &str) -> Result<Value> {
223        self.client
224            .get(&format!("/v1/nodes/{}/actions/{}", uid, action))
225            .await
226    }
227
228    /// Execute named node action - POST /v1/nodes/{uid}/actions/{action}
229    pub async fn action_execute(&self, uid: u32, action: &str, body: Value) -> Result<Value> {
230        self.client
231            .post(&format!("/v1/nodes/{}/actions/{}", uid, action), &body)
232            .await
233    }
234
235    /// Delete node action - DELETE /v1/nodes/{uid}/actions/{action}
236    pub async fn action_delete(&self, uid: u32, action: &str) -> Result<()> {
237        self.client
238            .delete(&format!("/v1/nodes/{}/actions/{}", uid, action))
239            .await
240    }
241
242    /// List snapshots for a node - GET /v1/nodes/{uid}/snapshots
243    pub async fn snapshots(&self, uid: u32) -> Result<Value> {
244        self.client
245            .get(&format!("/v1/nodes/{}/snapshots", uid))
246            .await
247    }
248
249    /// Create a snapshot - POST /v1/nodes/{uid}/snapshots/{name}
250    pub async fn snapshot_create(&self, uid: u32, name: &str) -> Result<Value> {
251        self.client
252            .post(
253                &format!("/v1/nodes/{}/snapshots/{}", uid, name),
254                &serde_json::json!({}),
255            )
256            .await
257    }
258
259    /// Delete a snapshot - DELETE /v1/nodes/{uid}/snapshots/{name}
260    pub async fn snapshot_delete(&self, uid: u32, name: &str) -> Result<()> {
261        self.client
262            .delete(&format!("/v1/nodes/{}/snapshots/{}", uid, name))
263            .await
264    }
265
266    /// All nodes status - GET /v1/nodes/status
267    pub async fn status_all(&self) -> Result<Value> {
268        self.client.get("/v1/nodes/status").await
269    }
270
271    /// Watchdog status for all nodes - GET /v1/nodes/wd_status
272    pub async fn wd_status_all(&self) -> Result<Value> {
273        self.client.get("/v1/nodes/wd_status").await
274    }
275
276    /// Node status - GET /v1/nodes/{uid}/status
277    pub async fn status(&self, uid: u32) -> Result<Value> {
278        self.client.get(&format!("/v1/nodes/{}/status", uid)).await
279    }
280
281    /// Node watchdog status - GET /v1/nodes/{uid}/wd_status
282    pub async fn wd_status(&self, uid: u32) -> Result<Value> {
283        self.client
284            .get(&format!("/v1/nodes/{}/wd_status", uid))
285            .await
286    }
287
288    /// All node alerts - GET /v1/nodes/alerts
289    pub async fn alerts_all(&self) -> Result<Value> {
290        self.client.get("/v1/nodes/alerts").await
291    }
292
293    /// Alerts for node - GET /v1/nodes/alerts/{uid}
294    pub async fn alerts_for(&self, uid: u32) -> Result<Value> {
295        self.client.get(&format!("/v1/nodes/alerts/{}", uid)).await
296    }
297
298    /// Alert detail - GET /v1/nodes/alerts/{uid}/{alert}
299    pub async fn alert_detail(&self, uid: u32, alert: &str) -> Result<Value> {
300        self.client
301            .get(&format!("/v1/nodes/alerts/{}/{}", uid, alert))
302            .await
303    }
304}