1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! Alert configuration and management
//!
//! ## Overview
//! - Configure alert settings
//! - Query alert history
//! - Manage alert thresholds
use crate::client::RestClient;
use crate::error::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
/// Alert information
/// Represents an alert state for a cluster object (database, node, or cluster)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Alert {
/// Unique identifier for the alert
pub uid: String,
/// Name/type of the alert
pub name: String,
/// Alert severity level: DEBUG, INFO, WARNING, ERROR, CRITICAL
pub severity: String,
/// Current alert state - true if alert is currently triggered
pub state: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// Type of entity this alert is associated with (e.g., "bdb", "node", "cluster")
pub entity_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Name of the entity this alert is associated with
pub entity_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// UID of the entity this alert is associated with
pub entity_uid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// String representing an alert threshold when applicable
pub threshold: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
/// ISO 8601 timestamp when alert state last changed
pub change_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Object containing data relevant to the evaluation time when the alert went on/off (thresholds/sampled values/etc.)
pub change_value: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Human-readable description of the alert
pub description: Option<String>,
/// Error code associated with the alert
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
}
/// Cluster-alert state as returned by `GET /v1/cluster/alerts`.
///
/// The cluster-alerts endpoint returns a map keyed by alert name —
/// for example `cluster_ca_cert_about_to_expire`,
/// `cluster_multiple_nodes_down`, etc. — whose values share this
/// shape. The alert name itself lives in the map key, not in this
/// struct.
///
/// Distinct from [`Alert`], which models the per-entity alert object
/// returned by `/v1/bdbs/{uid}/alerts` and `/v1/nodes/{uid}/alerts`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterAlertState {
/// Whether this alert is configured to fire.
pub enabled: bool,
/// Whether the alert is currently triggered.
pub state: bool,
/// Severity level (e.g. `"INFO"`, `"WARNING"`).
pub severity: String,
/// ISO-8601 timestamp of the last state change.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub change_time: Option<String>,
/// Alert-specific snapshot taken at the last state change
/// (thresholds, sampled values, etc.). Shape varies per alert.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub change_value: Option<Value>,
}
/// Generic alert settings (legacy - kept for compatibility)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertSettings {
/// True if alert is enabled
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
/// Alert threshold value when applicable
pub threshold: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
/// List of email addresses to notify when alert triggers
pub email_recipients: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Webhook URL to call when alert triggers
pub webhook_url: Option<String>,
}
/// Database alert settings with threshold
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BdbAlertSettingsWithThreshold {
/// True if alert is enabled
pub enabled: bool,
/// String representing the alert threshold value
pub threshold: String,
}
/// Complete database alerts settings object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbAlertsSettings {
/// Periodic backup has been delayed for longer than specified threshold value \[minutes\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_backup_delayed: Option<BdbAlertSettingsWithThreshold>,
/// CRDB source - sync lag is higher than specified threshold value \[seconds\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_crdt_src_high_syncer_lag: Option<BdbAlertSettingsWithThreshold>,
/// CRDB source - sync has connection error while trying to connect replica source
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_crdt_src_syncer_connection_error: Option<BdbAlertSettingsWithThreshold>,
/// CRDB - sync encountered in general error
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_crdt_src_syncer_general_error: Option<BdbAlertSettingsWithThreshold>,
/// Latency is higher than specified threshold value \[micro-sec\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_high_latency: Option<BdbAlertSettingsWithThreshold>,
/// (Deprecated) Replica of - sync lag is higher than specified threshold value \[seconds\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_high_syncer_lag: Option<BdbAlertSettingsWithThreshold>,
/// Throughput is higher than specified threshold value \[requests / sec.\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_high_throughput: Option<BdbAlertSettingsWithThreshold>,
/// An alert for state-machines that are running for too long
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_long_running_action: Option<BdbAlertSettingsWithThreshold>,
/// Throughput is lower than specified threshold value \[requests / sec.\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_low_throughput: Option<BdbAlertSettingsWithThreshold>,
/// Dataset RAM overhead of a shard has reached the threshold value \[% of its RAM limit\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_ram_dataset_overhead: Option<BdbAlertSettingsWithThreshold>,
/// Percent of values kept in a shard's RAM is lower than \[% of its key count\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_ram_values: Option<BdbAlertSettingsWithThreshold>,
/// Replica-of source - sync lag is higher than specified threshold value \[seconds\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_replica_src_high_syncer_lag: Option<BdbAlertSettingsWithThreshold>,
/// Replica-of source - sync has connection error while trying to connect replica source
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_replica_src_syncer_connection_error: Option<BdbAlertSettingsWithThreshold>,
/// Replica-of - sync encountered in general error
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_replica_src_syncer_general_error: Option<BdbAlertSettingsWithThreshold>,
/// Number of values kept in a shard's RAM is lower than \[values\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_shard_num_ram_values: Option<BdbAlertSettingsWithThreshold>,
/// Dataset size has reached the threshold value \[% of the memory limit\]
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_size: Option<BdbAlertSettingsWithThreshold>,
/// (Deprecated) Replica of - sync has connection error while trying to connect replica source
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_syncer_connection_error: Option<BdbAlertSettingsWithThreshold>,
/// (Deprecated) Replica of - sync encountered in general error
#[serde(skip_serializing_if = "Option::is_none")]
pub bdb_syncer_general_error: Option<BdbAlertSettingsWithThreshold>,
}
/// Cluster alert settings with threshold
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterAlertSettingsWithThreshold {
/// True if alert is enabled
pub enabled: bool,
/// String representing the alert threshold value
pub threshold: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// List of email addresses to notify when alert triggers
pub email: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Webhook URL to call when alert triggers
pub webhook_url: Option<String>,
}
/// Complete cluster alerts settings object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterAlertsSettings {
/// CA certificate about to expire
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster_ca_cert_about_to_expire: Option<ClusterAlertSettingsWithThreshold>,
/// Cluster certificates about to expire
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster_certs_about_to_expire: Option<ClusterAlertSettingsWithThreshold>,
/// License about to expire
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster_license_about_to_expire: Option<ClusterAlertSettingsWithThreshold>,
/// Node CPU utilization above threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_cpu_utilization: Option<ClusterAlertSettingsWithThreshold>,
/// Node ephemeral storage below threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_ephemeral_storage: Option<ClusterAlertSettingsWithThreshold>,
/// Node free flash below threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_free_flash: Option<ClusterAlertSettingsWithThreshold>,
/// Node internal certificates about to expire
#[serde(skip_serializing_if = "Option::is_none")]
pub node_internal_certs_about_to_expire: Option<ClusterAlertSettingsWithThreshold>,
/// Node memory below threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_memory: Option<ClusterAlertSettingsWithThreshold>,
/// Node network throughput above threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_net_throughput: Option<ClusterAlertSettingsWithThreshold>,
/// Node persistent storage below threshold
#[serde(skip_serializing_if = "Option::is_none")]
pub node_persistent_storage: Option<ClusterAlertSettingsWithThreshold>,
}
/// Alert handler for managing alerts
pub struct AlertHandler {
client: RestClient,
}
/// Alias for backwards compatibility and intuitive plural naming
pub type AlertsHandler = AlertHandler;
impl AlertHandler {
/// Create a new handler bound to the given REST client.
pub fn new(client: RestClient) -> Self {
AlertHandler { client }
}
/// List alerts for a specific database
pub async fn list_by_database(&self, bdb_uid: u32) -> Result<Vec<Alert>> {
self.client
.get(&format!("/v1/bdbs/{}/alerts", bdb_uid))
.await
}
/// List alerts for a specific node
pub async fn list_by_node(&self, node_uid: u32) -> Result<Vec<Alert>> {
self.client
.get(&format!("/v1/nodes/{}/alerts", node_uid))
.await
}
/// List active cluster-level alerts.
///
/// `GET /v1/cluster/alerts`. The API returns a map keyed by alert
/// name (e.g. `cluster_multiple_nodes_down`), so the return type is
/// `HashMap<String, ClusterAlertState>`. The alert name itself is
/// the map key. See [`ClusterAlertState`] for the per-entry shape.
pub async fn list_cluster_alerts(&self) -> Result<HashMap<String, ClusterAlertState>> {
self.client.get("/v1/cluster/alerts").await
}
/// Get alert settings for a specific alert type
pub async fn get_settings(&self, alert_name: &str) -> Result<AlertSettings> {
self.client
.get(&format!("/v1/cluster/alert_settings/{}", alert_name))
.await
}
/// Update alert settings (generic/legacy)
pub async fn update_settings(
&self,
alert_name: &str,
settings: AlertSettings,
) -> Result<AlertSettings> {
self.client
.put(
&format!("/v1/cluster/alert_settings/{}", alert_name),
&settings,
)
.await
}
/// Get database alert settings
pub async fn get_database_alert_settings(&self, bdb_uid: u32) -> Result<DbAlertsSettings> {
self.client
.get(&format!("/v1/bdbs/{}/alert_settings", bdb_uid))
.await
}
/// Update database alert settings
pub async fn update_database_alert_settings(
&self,
bdb_uid: u32,
settings: &DbAlertsSettings,
) -> Result<DbAlertsSettings> {
self.client
.put(&format!("/v1/bdbs/{}/alert_settings", bdb_uid), settings)
.await
}
/// Get cluster alert settings
pub async fn get_cluster_alert_settings(&self) -> Result<ClusterAlertsSettings> {
self.client.get("/v1/cluster/alert_settings").await
}
/// Update cluster alert settings
pub async fn update_cluster_alert_settings(
&self,
settings: &ClusterAlertsSettings,
) -> Result<ClusterAlertsSettings> {
self.client
.put("/v1/cluster/alert_settings", settings)
.await
}
}