fix(generic-snmp): more and more near of the solution

This commit is contained in:
David Boucher 2025-03-08 21:16:23 +01:00
parent 8a25007654
commit aa20a3232f
4 changed files with 143 additions and 110 deletions

View File

@ -15,6 +15,6 @@ rasn = "0.24.0"
rasn-snmp = "0.24.0" rasn-snmp = "0.24.0"
rasn-smi = "0.24.0" rasn-smi = "0.24.0"
log = "0.4.25" log = "0.4.25"
clap = { version = "4.5.26", features = ["derive"] } clap = { version = "4.5.29", features = ["derive"] }
serde = { version = "1.0.104", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.44" serde_json = "1.0.138"

View File

@ -2,15 +2,26 @@
"leaf": { "leaf": {
"name": "cpu", "name": "cpu",
"output": { "output": {
"status": { "header": "{status}: ",
"header": "{status}: ", "text": [
"text": "CPU {idx} usage: {value} %" "{count} CPU(s) average usage is {total_cpu_avg} %",
}, "CPU '{idx}' usage: {value} %"
"default": "{status}: {count} CPU(s) average usage is {total_cpu_avg} %" ]
}, },
"entries": [ "entries": [
{ "Agregation": { "name": "total_cpu_avg", "op": "Average"}}, {
{ "Query": { "name": "cpu_{idx}", "oid": "1.3.6.1.2.1.25.3.3.1.2", "query": "Walk" }} "Agregation": {
"name": "total_cpu_avg",
"op": "Average"
}
},
{
"Query": {
"name": "cpu_{idx}",
"oid": "1.3.6.1.2.1.25.3.3.1.2",
"query": "Walk"
}
}
], ],
"data": { "data": {
"uom": "%", "uom": "%",

View File

@ -12,6 +12,15 @@ pub enum Status {
Unknown = 3, Unknown = 3,
} }
struct Metric<'b> {
name: String,
value: f32,
warning: &'b Option<String>,
critical: &'b Option<String>,
status: Status,
agregated: bool,
}
fn worst(a: Status, b: Status) -> Status { fn worst(a: Status, b: Status) -> Status {
let a_int = match a { let a_int = match a {
Status::Ok => 0, Status::Ok => 0,
@ -69,16 +78,10 @@ struct Data {
max: Option<f32>, max: Option<f32>,
} }
#[derive(Deserialize, Debug)]
struct StatusText {
header: String,
text: String,
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct OutputTable { struct OutputTable {
default: String, header: String,
status: Option<StatusText>, text: Vec<String>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -126,11 +129,8 @@ fn build_metrics<'a>(
values: &Vec<(String, f32)>, values: &Vec<(String, f32)>,
ag: &Option<(&str, usize, f32)>, ag: &Option<(&str, usize, f32)>,
ext: &'a CommandExt, ext: &'a CommandExt,
) -> ( ) -> (Vec<Metric<'a>>, Status) {
Vec<(String, f32, &'a Option<String>, &'a Option<String>, Status)>, let mut metrics: Vec<Metric> = Vec::new();
Status,
) {
let mut metrics: Vec<(String, f32, &Option<String>, &Option<String>, Status)> = Vec::new();
let mut status = Status::Ok; let mut status = Status::Ok;
match ag { match ag {
Some(a) => { Some(a) => {
@ -140,7 +140,14 @@ fn build_metrics<'a>(
let c = &ext.critical_agregation; let c = &ext.critical_agregation;
let current_status = let current_status =
compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation); compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation);
metrics.push((a.0.to_string(), a.2, w, c, current_status)); metrics.push(Metric {
name: a.0.to_string(),
value: a.2,
warning: w,
critical: c,
status: current_status,
agregated: true,
});
status = worst(current_status, status); status = worst(current_status, status);
} }
} }
@ -148,13 +155,14 @@ fn build_metrics<'a>(
} }
values.iter().enumerate().for_each(|(i, v)| { values.iter().enumerate().for_each(|(i, v)| {
let current_status = compute_status(v.1, &ext.warning_core, &ext.critical_core); let current_status = compute_status(v.1, &ext.warning_core, &ext.critical_core);
metrics.push(( metrics.push(Metric {
values[i].0.clone(), name: values[i].0.clone(),
v.1, value: v.1,
&ext.warning_core, warning: &ext.warning_core,
&ext.critical_core, critical: &ext.critical_core,
current_status, status: current_status,
)); agregated: false,
});
status = worst(current_status, status); status = worst(current_status, status);
}); });
match ag { match ag {
@ -162,13 +170,14 @@ fn build_metrics<'a>(
if a.1 > 0 { if a.1 > 0 {
let current_status = let current_status =
compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation); compute_status(a.2, &ext.warning_agregation, &ext.critical_agregation);
metrics.push(( metrics.push(Metric {
a.0.to_string(), name: a.0.to_string(),
a.2, value: a.2,
&ext.warning_agregation, warning: &ext.warning_agregation,
&ext.critical_agregation, critical: &ext.critical_agregation,
current_status, status: current_status,
)); agregated: true,
});
status = worst(current_status, status); status = worst(current_status, status);
} }
} }
@ -237,90 +246,99 @@ impl Command {
&self, &self,
count: usize, count: usize,
status: Status, status: Status,
metrics: &Vec<(String, f32, &Option<String>, &Option<String>, Status)>, metrics: &Vec<Metric>,
ag: &Option<(&str, usize, f32)>, ag: &Option<(&str, usize, f32)>,
ext: &CommandExt, ext: &CommandExt,
) -> String { ) -> String {
let mut output_text: String; let mut output_text = "".to_string();
if status == Status::Ok { let mut begun = false;
output_text = self if &self.leaf.output.header != "" {
.leaf output_text = match status {
.output Status::Ok => self.leaf.output.header.replace("{status}", "OK"),
.default Status::Warning => self.leaf.output.header.replace("{status}", "WARNING"),
.replace("{count}", count.to_string().as_str()) Status::Critical => self.leaf.output.header.replace("{status}", "CRITICAL"),
.replace( Status::Unknown => self.leaf.output.header.replace("{status}", "UNKNOWN"),
"{status}",
match status {
Status::Ok => "OK",
Status::Warning => "WARNING",
Status::Critical => "CRITICAL",
Status::Unknown => "UNKNOWN",
},
);
match ag {
Some(a) => {
output_text = output_text
.replace(format!("{{{}}}", a.0).as_str(), a.2.to_string().as_str());
}
None => (),
}; };
} else { begun = true;
let mut warning_array = Vec::new(); }
let mut critical_array = Vec::new(); let mut idx = 0;
let part = &self.leaf.output.status; for line in &self.leaf.output.text {
for (idx, m) in metrics.iter().enumerate() { if line.contains("idx") {
if m.4 == Status::Warning { // We have to iterate on metrics
let output_str = match *part { let mut output_vec = (Vec::new(), Vec::new(), Vec::new());
Some(ref s) => { for m in metrics.iter() {
s.text.replace("{value}", m.1.to_string().as_str()) if !m.agregated {
.replace("{idx}", idx.to_string().as_str()) let text = line
.replace("{idx}", idx.to_string().as_str())
.replace("{name}", m.name.as_str())
.replace("{value}", format!("{:.2}", m.value).as_str());
match m.status {
Status::Ok => {
output_vec.0.push(text);
}
Status::Warning => {
output_vec.1.push(text);
}
Status::Critical => {
output_vec.2.push(text);
}
Status::Unknown => (),
} }
None => "".to_string(), idx += 1;
}; }
warning_array.push(output_str); }
} else if m.4 == Status::Critical { if !output_vec.2.is_empty() {
let part = &self.leaf.output.status; if begun {
let output_str = match *part { output_text += " - ";
Some(ref s) => { } else {
s.text.replace("{value}", m.1.to_string().as_str()) begun = true;
.replace("{idx}", idx.to_string().as_str()) }
} output_text += output_vec.2.join(" - ").as_str();
None => "".to_string(), }
}; if !output_vec.1.is_empty() {
critical_array.push(output_str); if begun {
output_text += " - ";
} else {
begun = true;
}
output_text += output_vec.1.join(" - ").as_str();
}
if !output_vec.0.is_empty() {
if begun {
output_text += " - ";
}
output_text += output_vec.0.join(" - ").as_str();
} }
}
let warn_header = match *part {
Some(ref s) => &s.header.replace("{status}", "WARNING"),
None => "",
};
let crit_header = match *part {
Some(ref s) => &s.header.replace("{status}", "CRITICAL"),
None => "",
};
if !warning_array.is_empty() && !critical_array.is_empty() {
output_text = format!("{} {} - {} {}", crit_header, &critical_array.join(" - "), warn_header, &warning_array.join(" - "));
} else if !warning_array.is_empty() {
output_text = format!("{} {}", warn_header, &warning_array.join(" - "));
} else { } else {
output_text = format!("{} {}", crit_header, critical_array.join(" - ")); match ag {
Some(a) => {
output_text += line
.replace(
format!("{{{}}}", a.0).as_str(),
format!("{:.2}", a.2).as_str(),
)
.as_str();
}
None => output_text += line,
};
} }
} }
output_text = output_text.replace("{count}", idx.to_string().as_str());
let mut perfdata = " |".to_string(); let mut perfdata = " |".to_string();
match &self.leaf.data { match &self.leaf.data {
Some(d) => { Some(d) => {
metrics.iter().for_each(|(k, v, w, c, s)| { metrics.iter().for_each(|m| {
perfdata += format!( perfdata += format!(
" {}={}{};{};{};{};{}", " {}={}{};{};{};{};{}",
k, m.name,
v, m.value,
d.uom, d.uom,
match w { match m.warning {
Some(m) => m.to_string(), Some(m) => m.to_string(),
None => "".to_string(), None => "".to_string(),
}, },
match c { match m.critical {
Some(m) => m.to_string(), Some(m) => m.to_string(),
None => "".to_string(), None => "".to_string(),
}, },
@ -337,16 +355,16 @@ impl Command {
}); });
} }
None => { None => {
metrics.iter().for_each(|(k, v, w, c, s)| { metrics.iter().for_each(|m| {
perfdata += format!( perfdata += format!(
" {}={};{};{}", " {}={};{};{}",
k, m.name,
v, m.value,
match w { match m.warning {
Some(v) => v.to_string(), Some(v) => v.to_string(),
None => "".to_string(), None => "".to_string(),
}, },
match c { match m.critical {
Some(v) => v.to_string(), Some(v) => v.to_string(),
None => "".to_string(), None => "".to_string(),
} }

View File

@ -39,12 +39,12 @@ cpu ${tc}
... 9 --warning-core='0' WARNING: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;0:0;;0;100 ... 9 --warning-core='0' WARNING: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;0:0;;0;100
... 10 --critical-core='0' CRITICAL: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;;0:0;0;100 ... 10 --critical-core='0' CRITICAL: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;;0:0;0;100
gs_cpu ${tc} gs-cpu ${tc}
[Tags] os linux generic-snmp [Tags] os linux generic-snmp
${command} Catenate ${command} Catenate
... ${GS_CMD} ... ${GS_CMD}
... --hostname=${HOSTNAME} ... --hostname=${HOSTNAME}
... --port=2024 ... --port=${SNMPPORT}
... --snmp-version=${SNMPVERSION} ... --snmp-version=${SNMPVERSION}
... --community=os/linux/snmp/network-interfaces ... --community=os/linux/snmp/network-interfaces
... ${extra_options} ... ${extra_options}
@ -53,3 +53,7 @@ gs_cpu ${tc}
Examples: tc extra_options expected_result -- Examples: tc extra_options expected_result --
... 1 ${EMPTY} OK: 1 CPU(s) average usage is 2.00 % - CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;;;0;100 ... 1 ${EMPTY} OK: 1 CPU(s) average usage is 2.00 % - CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;;;0;100
... 2 --warning-agregation=0 WARNING: 1 CPU(s) average usage is 2.00 % | 'total_cpu_avg'=2.00%;0:0;;0;100 'cpu'=2.00%;;;0;100
... 3 --critical-agregation=0 CRITICAL: 1 CPU(s) average usage is 2.00 % | 'total_cpu_avg'=2.00%;;0:0;0;100 'cpu'=2.00%;;;0;100
... 4 --warning-core=0 WARNING: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;0:0;;0;100
... 5 --critical-core=0 CRITICAL: CPU '0' usage : 2.00 % | 'total_cpu_avg'=2.00%;;;0;100 'cpu'=2.00%;;0:0;0;100