use crate::error::RedisCtlError;
use anyhow::Context;
use clap::Subcommand;
use redis_enterprise::DiagnosticsHandler;
use crate::cli::OutputFormat;
use crate::connection::ConnectionManager;
use crate::error::Result as CliResult;
#[derive(Debug, Clone, Subcommand)]
pub enum DiagnosticsCommands {
Get,
#[command(after_help = "EXAMPLES:
# Enable diagnostics
redisctl enterprise diagnostics update --enabled true
# Set collection interval
redisctl enterprise diagnostics update --interval 3600
# Using JSON for full configuration
redisctl enterprise diagnostics update --data @config.json")]
Update {
#[arg(long)]
enabled: Option<bool>,
#[arg(long)]
interval: Option<u32>,
#[arg(short, long, value_name = "FILE|JSON")]
data: Option<String>,
},
Run {
#[arg(long)]
checks: Option<String>,
#[arg(long)]
nodes: Option<String>,
#[arg(long)]
databases: Option<String>,
},
#[command(name = "list-checks")]
ListChecks,
#[command(name = "last-report")]
LastReport,
#[command(name = "get-report")]
GetReport {
report_id: String,
},
#[command(name = "list-reports")]
ListReports,
}
impl DiagnosticsCommands {
#[allow(dead_code)]
pub async fn execute(
&self,
conn_mgr: &ConnectionManager,
profile_name: Option<&str>,
output_format: OutputFormat,
query: Option<&str>,
) -> CliResult<()> {
let client = conn_mgr.create_enterprise_client(profile_name).await?;
let handler = DiagnosticsHandler::new(client.clone());
match self {
DiagnosticsCommands::Get => {
let config = handler.get_config().await.map_err(RedisCtlError::from)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&config, q)?
} else {
config
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::Update {
enabled,
interval,
data,
} => {
let mut json_data = if let Some(data_str) = data {
super::utils::read_json_data(data_str)?
} else {
serde_json::json!({})
};
let data_obj = json_data.as_object_mut().unwrap();
if let Some(e) = enabled {
data_obj.insert("enabled".to_string(), serde_json::json!(e));
}
if let Some(i) = interval {
data_obj.insert("interval".to_string(), serde_json::json!(i));
}
let result = handler
.update_config(json_data)
.await
.map_err(RedisCtlError::from)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&result, q)?
} else {
result
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::Run {
checks,
nodes,
databases,
} => {
let mut request = serde_json::json!({});
if let Some(checks_list) = parse_comma_separated(checks) {
request["checks"] = serde_json::json!(checks_list);
}
if let Some(nodes_list) = parse_comma_separated_u32(nodes) {
request["node_uids"] = serde_json::json!(nodes_list);
}
if let Some(databases_list) = parse_comma_separated_u32(databases) {
request["bdb_uids"] = serde_json::json!(databases_list);
}
let report: serde_json::Value = client
.post("/v1/diagnostics", &request)
.await
.map_err(RedisCtlError::from)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&report, q)?
} else {
report
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::ListChecks => {
let checks = handler.list_checks().await.map_err(RedisCtlError::from)?;
let response = serde_json::to_value(&checks)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&response, q)?
} else {
response
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::LastReport => {
let report = handler
.get_last_report()
.await
.map_err(RedisCtlError::from)?;
let response = serde_json::to_value(&report)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&response, q)?
} else {
response
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::GetReport { report_id } => {
let report = handler
.get_report(report_id)
.await
.context(format!("Failed to get diagnostic report {}", report_id))?;
let response = serde_json::to_value(&report)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&response, q)?
} else {
response
};
super::utils::print_formatted_output(output_data, output_format)?;
}
DiagnosticsCommands::ListReports => {
let reports = handler.list_reports().await.map_err(RedisCtlError::from)?;
let response = serde_json::to_value(&reports)?;
let output_data = if let Some(q) = query {
super::utils::apply_jmespath(&response, q)?
} else {
response
};
super::utils::print_formatted_output(output_data, output_format)?;
}
}
Ok(())
}
}
#[allow(dead_code)]
pub async fn handle_diagnostics_command(
conn_mgr: &ConnectionManager,
profile_name: Option<&str>,
diagnostics_cmd: DiagnosticsCommands,
output_format: OutputFormat,
query: Option<&str>,
) -> CliResult<()> {
diagnostics_cmd
.execute(conn_mgr, profile_name, output_format, query)
.await
}
#[allow(dead_code)]
fn parse_comma_separated(input: &Option<String>) -> Option<Vec<String>> {
input.as_ref().map(|s| {
s.split(',')
.map(|item| item.trim().to_string())
.filter(|item| !item.is_empty())
.collect()
})
}
#[allow(dead_code)]
fn parse_comma_separated_u32(input: &Option<String>) -> Option<Vec<u32>> {
input.as_ref().and_then(|s| {
let values: Result<Vec<u32>, _> = s
.split(',')
.map(|item| item.trim())
.filter(|item| !item.is_empty())
.map(|item| item.parse::<u32>())
.collect();
values.ok()
})
}