chore: Refactoring move module functions to respective struct items.

This commit is contained in:
Jesús Pérez Lorenzo 2021-09-16 22:31:57 +01:00
parent 5bd6c74568
commit 5c41eb7120
3 changed files with 627 additions and 316 deletions

View File

@ -1,316 +1,2 @@
use std::{fs}; //,io};
use std::fs::OpenOptions;
use std::path::Path;
use std::io::{Write};
use std::str;
use anyhow::{anyhow,Result};
// use tokio::runtime::Handle;
use std::process::{Command};
use app_env::{
appenv::{AppEnv},
DataStore,
applogs::AppLogs,
// config::{Config}
};
use crate::clouds::defs::{
Cloud,
};
use crate::defs::{ProviderName};
use crate::clouds::monitor_rules::{
MonitorRules,
MonitorRule,
RuleContext,
RuleSchedule,
RuleOperator,
MonitorAction,
};
pub fn write_str_data(output_path: String, data_str: String, msg: String) -> Result<()> {
if Path::new(&output_path).exists() {
fs::remove_file(&output_path)?;
}
let mut file = OpenOptions::new().write(true).create(true).open(&output_path)?;
file.write_all(data_str.as_bytes())?;
if ! msg.is_empty() {
let now = chrono::Utc::now().timestamp();
let out = format!("{}: [{}] -> {}\n",&now,&msg,&output_path);
println!("{}",&out);
}
Ok(())
}
pub async fn on_action_server(task: &str, srvr: &str, provider: &str, args: &str, _rule: &MonitorRule) -> Result<()> {
let provider_name = ProviderName::set_provider(provider.to_owned());
match provider_name {
ProviderName::upcloud => {
let cmd = format!("upclapi -c {}server -id {} {}",&task,&srvr,&args);
println!("action {}: {} on {} {}",&task,&srvr,&provider_name,&args);
let res = run_command(&cmd).unwrap_or_else(|e|{
eprintln!("Error {}: {}",&cmd,e);
String::from("")
});
println!("{}",&res);
Ok(())
},
_ => Err(anyhow!("Provider {} not defined", &provider_name))
}
}
pub async fn on_action_notify(chnl: &str, msg: &str, args: &str, rule: &MonitorRule) -> Result<()> {
let notificator_path = envmnt::get_or("NOTIFICATOR_BIN","");
if notificator_path.is_empty() || ! Path::new(&notificator_path).exists() {
return Err(anyhow!("Error notificator:"));
}
let message=format!("Monitor {}: {} {}",&rule.id,&msg,&args);
let cmd = format!("{} {} {}",&notificator_path,&chnl,&message);
let _ = run_command(&cmd).unwrap_or_else(|e|{
eprintln!("Error {}: {}",&cmd,e);
String::from("")
});
Ok(())
}
fn parse_target(target: &str, val: &str) -> String {
let srvr: String;
if val.contains("$target") {
srvr = val.replace("$target", &target.to_owned());
} else if val.contains("$server") {
srvr = val.replace("$server", &target.to_owned());
} else {
srvr = val.to_owned();
}
srvr
}
pub async fn on_monitor_action(action: &MonitorAction, target: &str, rule: &MonitorRule) -> Result<()> {
match action {
MonitorAction::None => return Ok(()),
MonitorAction::Start(v,p,a) =>
on_action_server("start",&parse_target(target, v),&p,&a,&rule).await?,
MonitorAction::Stop(v,p,a) =>
on_action_server("stop",&parse_target(target, v),&p,&a,&rule).await?,
MonitorAction::Restart(v,p,a) =>
on_action_server("restart",&parse_target(target, v),&p,&a,&rule).await?,
MonitorAction::Notify(v,p,a) => on_action_notify(&v,&parse_target(target,&p),&a,&rule).await?,
};
Ok(())
}
pub fn on_monitor_operator(operator: &RuleOperator, target: &str, value: &str) -> bool {
match operator {
RuleOperator::None => true,
RuleOperator::Equal => target == value,
RuleOperator::NotEqual=> target != value,
RuleOperator::Contains => target.contains(value),
RuleOperator::NotContains => !target.contains(value),
}
}
pub async fn on_server_rule(_srvr: &str, _out_cmd: &str, _rule: &MonitorRule) -> Result<()> {
Ok(())
}
pub async fn on_service_rule(srvc: &str, out_cmd: &str, rule: &MonitorRule) -> Result<()> {
println!("Service: {} -> {}",&srvc,&out_cmd);
if !out_cmd.is_empty() {
let mut targets = out_cmd.split_whitespace();
while let Some(target) = targets.next() {
// println!("target: {}",&target);
for action in &rule.actions {
on_monitor_action(action, &target, &rule).await?;
}
};
}
Ok(())
}
pub async fn on_ip_rule(_ip: &str, _out_cmd: &str, _rule: &MonitorRule) -> Result<()> {
// tokio::spawn(async move {run_on_rule(rule.to_owned()).await; });
Ok(())
}
pub fn run_command(str_cmd: &str) -> Result<String> {
let mut args: Vec<&str> = str_cmd.split(" ").collect();
let cmd = args[0].to_owned();
args.remove(0);
// println!("cmd: {}, args: {:?}",&cmd,&args);
let result = Command::new(&cmd)
.args(&args)
.output()?;
// dbg!(&result);
match &result.status.code() {
Some(code) =>
if format!("{}",code) == "0" {
let mut res=String::from(str::from_utf8(&result.stdout).unwrap_or_else(|_| ""));
if res.ends_with('\n') {
res.pop();
}
res = res.replace('\n'," ");
Ok(res)
} else {
let err = str::from_utf8(&result.stderr).unwrap_or_else(|_| "");
Err(anyhow!("Running {}: failed:\n {}",&cmd,&err))
}
None => Ok(String::from(""))
}
}
pub fn run_on_selector(rule: &MonitorRule) -> Result<String> {
let ctx_val = rule.context.get_value();
let target: Vec<&str> = ctx_val.split("/").collect();
let srv_path = match target.len() {
2 => String::from(target[1]),
_ => String::from(""),
};
let mut cmd = match &rule.context {
RuleContext::Server(v) => String::from(v),
RuleContext::Service(v) =>
if v.contains("kubernetes") {
String::from("kubectl")
} else {
String::from(v)
}
,
RuleContext::Ip(v) => String::from(v),
RuleContext::None => String::from(""),
};
if let Some(ctxpath) = rule.selector.get("ctxpath") {
cmd = format!("{}{} {}",&ctxpath,&srv_path,&cmd);
}
if let Some(run) = rule.selector.get("run") {
cmd = format!("{} {}",&run,&cmd);
}
if let Some(typ) = rule.selector.get("type") {
cmd = format!("{} get {}",&cmd,&typ);
}
if let Some(ns) = rule.selector.get("ns") {
cmd = format!("{} -n {}",&cmd,&ns);
}
if let Some(custom) = rule.selector.get("custom") {
cmd = format!("{} {}",&cmd,&custom);
}
if let Some(pat) = rule.selector.get("match") {
cmd = format!("{} | grep {}",&cmd,&pat);
}
if let Some(awk) = rule.selector.get("awk") {
cmd = format!("{} | awk '{}'",&cmd,&awk);
}
if let Some(sort) = rule.selector.get("sort") {
cmd = format!("{} | sort {}",&cmd,&sort);
}
if cmd.len() > 0 {
run_command(&cmd)
} else {
Err(anyhow!("Selector: failed:\n {}",&rule.id))
}
}
pub async fn log_monitor_state(rule: &MonitorRule, app_env: &AppEnv, env_name: &str, env_state: isize, msg: &str, out_cmd: &str) -> Result<()> {
let now = format!("{}", chrono::Utc::now().timestamp());
let log_entry = AppLogs {
id: format!("{}_{}", &rule.id,&now),
name: format!("rule: {}", &rule.name),
when: String::from("run_monitor_rule"),
source: format!("{}", &env_name),
target: format!("{}", &out_cmd),
state: format!("{}", &env_state),
msg: format!("{}", &msg),
};
if app_env.config.logs_store == DataStore::File {
log_entry.write_applogs_entry(format!("{}/monitor.log",&app_env.config.logs_path).as_str()).await?;
}
Ok(())
}
pub async fn run_monitor_rule(idx: usize, rule: &MonitorRule, app_env: &AppEnv) -> Result<()> {
if envmnt::get_isize("DEBUG",0) > 0 {
println!("{} [{}] {}: {}",&idx,&rule.id,&rule.name,&rule.description);
}
let out_cmd: String;
if rule.command.is_empty() {
out_cmd=run_on_selector(&rule).unwrap_or_else(|e| {
eprintln!("Error on selector {}: {}",&rule.id,e);
String::from("?")
});
} else {
out_cmd=run_command(&rule.command).unwrap_or_else(|e| {
eprintln!("Error on command {}: {}",&rule.command,e);
String::from("?")
});
}
if out_cmd.as_str() == "?" {
return Ok(());
}
let env_name = format!("MONITOR_{}",&rule.id);
let env_state = envmnt::get_isize(&env_name,0);
if ! on_monitor_operator(&rule.operator,&out_cmd, &rule.value) {
if env_state > 0 {
let msg = format!("{} Ok in state: {}",&env_name,&env_state);
println!("{}",&msg);
envmnt::set_isize(&env_name, 0);
log_monitor_state(&rule, &app_env, &env_name, env_state, &msg, &out_cmd).await?;
for item in rule.actions.iter() {
match item {
MonitorAction::Notify(v,p,a) => on_action_notify(&v,&parse_target(format!("Ok state from -> {}",&env_state).as_str(),&p),&a,&rule).await?,
_ => continue,
};
}
}
return Ok(());
}
if env_state > 0 && env_state < rule.wait_checks {
let msg = format!("{} is already set: {} increment counter",&env_name,&env_state);
envmnt::increment(&env_name);
println!("{}",&msg);
log_monitor_state(&rule, &app_env, &env_name, env_state, &msg, &out_cmd).await?;
return Ok(());
}
let mut err = String::from("");
match &rule.context {
RuleContext::Server(v) => {
on_server_rule(&v, &out_cmd, &rule).await.unwrap_or_else(|e| {
err = format!("Error rule {} on {}: {}",&rule.id,&v,e);
eprintln!("{}",err)
});
},
RuleContext::None => {
return Ok(());
},
RuleContext::Service(v) => {
on_service_rule(&v, &out_cmd, &rule).await.unwrap_or_else(|e| {
err = format!("Error rule {} on {}: {}",&rule.id,&v,e);
eprintln!("{}",err)
});
},
RuleContext::Ip(v) => {
on_ip_rule(&v, &out_cmd, &rule).await.unwrap_or_else(|e| {
err = format!("Error rule {} on {}: {}",&rule.id,&v,e);
eprintln!("{}",err)
});
},
}
let msg = match err.is_empty() {
true => format!("{}: 1 done",&env_name),
false => format!("{} error: {}",&env_name,&err),
};
envmnt::set_isize(&env_name,1);
println!("{}",&msg);
log_monitor_state(&rule, &app_env, &env_name, env_state, &msg, &out_cmd).await?;
Ok(())
}
pub async fn run_monitor(monitor_rules: MonitorRules, _cloud: Cloud, app_env: AppEnv) -> Result<()> {
// println!("Run {}: {}",&monitor_rules.name,&monitor_rules.description);
for (idx,rule) in monitor_rules.rules.iter().enumerate() {
match rule.schedule {
RuleSchedule::Check => {
// dbg!(&rule);
let mut err = String::from("");
run_monitor_rule(idx,rule, &app_env).await.unwrap_or_else(|e| {
err = format!("Error rule {}: {}",&rule.id,e);
eprintln!("{}",&err)
});
match err.is_empty() {
true => continue,
false => log_monitor_state(&rule, &app_env, &rule.name, -1, &err, "").await.unwrap_or_else(|e|
eprintln!("Unable to log monitor entry: {}",e)
),
};
},
RuleSchedule::OnDemand => continue,
RuleSchedule::None => continue,
};
}
Ok(())
}
pub mod utils;
pub mod defs;

578
src/monitor/defs.rs Normal file
View File

@ -0,0 +1,578 @@
use std::fmt;
use std::collections::HashMap;
use serde::{Serialize,Deserialize,Deserializer};
use anyhow::{anyhow,Result};
use std::path::Path;
use app_env::{
appenv::{AppEnv},
DataStore,
applogs::AppLogs,
// config::{Config}
};
use crate::defs::{ProviderName};
use kloud::utils::load_fs_content;
use crate::clouds::defs::{ Cloud };
use crate::monitor::utils::{run_command,write_str_data};
fn parse_target(target: String, val: &str) -> String {
let srvr: String;
if val.contains("$target") {
srvr = val.replace("$target", &target);
} else if val.contains("$server") {
srvr = val.replace("$server", &target);
} else {
srvr = val.to_owned();
}
srvr
}
#[derive(Eq, PartialEq, Clone, Serialize, Debug, Deserialize)]
pub enum RuleContext {
None,
Server(String),
Service(String),
Ip(String),
}
impl Default for RuleContext {
fn default() -> Self {
RuleContext::None
}
}
impl fmt::Display for RuleContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RuleContext::Server(c) => write!(f,"server: {}",c),
RuleContext::None => write!(f,"none"),
RuleContext::Service(c) => write!(f,"service: {}",c),
RuleContext::Ip(c) => write!(f,"ip: {}",c),
}
//write!(f, "{:?}", self)
}
}
impl RuleContext {
pub fn get_value(&self) -> String {
match self {
RuleContext::Server(c) => format!("{}",c),
RuleContext::None => format!(""),
RuleContext::Service(c) => format!("{}",c),
RuleContext::Ip(c) => format!("{}",c),
}
}
}
#[derive(Eq, PartialEq, Clone, Serialize, Debug, Deserialize)]
pub enum MonitorAction {
None,
Start(String,String,String),
Stop(String,String,String),
Restart(String,String,String),
Notify(String,String,String),
}
impl Default for MonitorAction {
fn default() -> Self {
MonitorAction::None
}
}
impl fmt::Display for MonitorAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MonitorAction::None => write!(f,"none"),
MonitorAction::Start(v,p,a) => write!(f,"start: {} {} {}",v,p,a),
MonitorAction::Stop(v,p,a) => write!(f,"stop: {} {} {}",v,p,a),
MonitorAction::Restart(v,p,a) => write!(f,"restart: {} {} {}",v,p,a),
MonitorAction::Notify(v,p,a) => write!(f,"notify: {} {} {}",v,p,a),
}
//write!(f, "{:?}", self)
}
}
impl MonitorAction {
pub fn get_value(&self) -> String {
match self {
MonitorAction::None => format!(""),
MonitorAction::Start(v,p,a) => format!("{} {} {}",v,p,a),
MonitorAction::Stop(v,p,a) => format!("{} {} {}",v,p,a),
MonitorAction::Restart(v,p,a) => format!("{} {} {}",v,p,a),
MonitorAction::Notify(v,p,a) => format!("{} {} {}",v,p,a),
}
}
pub fn is_notify(&self) -> bool {
match self {
MonitorAction::Notify(_,_,_) => true,
_ => false
}
}
pub async fn on(&self, target: String, rule: &MonitorRule) -> Result<()> {
match self {
MonitorAction::None => return Ok(()),
MonitorAction::Start(v,p,a) =>
self.on_server("start",parse_target(target, v),p,a,&rule).await?,
MonitorAction::Stop(v,p,a) =>
self.on_server("stop",parse_target(target, v),p,a,&rule).await?,
MonitorAction::Restart(v,p,a) =>
self.on_server("restart",parse_target(target, v),p,a,&rule).await?,
MonitorAction::Notify(v,p,a) =>
self.on_notify(&v,parse_target(target ,p),a,&rule).await?,
};
Ok(())
}
pub async fn on_server(&self, task: &str, srvr: String, provider: &str, args: &str, _rule: &MonitorRule) -> Result<()> {
let provider_name = ProviderName::set_provider(provider.to_owned());
match provider_name {
ProviderName::upcloud => {
let cmd = format!("upclapi -c {}server -id {} {}",&task,&srvr,&args);
println!("action {}: {} on {} {}",&task,&srvr,&provider_name,&args);
let res = run_command(&cmd).unwrap_or_else(|e|{
eprintln!("Error {}: {}",&cmd,e);
String::from("")
});
println!("{}",&res);
Ok(())
},
_ => Err(anyhow!("Provider {} not defined", &provider_name))
}
}
pub async fn on_notify(&self, chnl: &str, msg: String, args: &str, rule: &MonitorRule) -> Result<()> {
let notificator_path = envmnt::get_or("NOTIFICATOR_BIN","");
if notificator_path.is_empty() || ! Path::new(&notificator_path).exists() {
return Err(anyhow!("Error notificator:"));
}
let message=format!("Monitor {}: {} {}",&rule.id,&msg,&args);
let cmd = format!("{} {} {}",&notificator_path,&chnl,&message);
let _ = run_command(&cmd).unwrap_or_else(|e|{
eprintln!("Error {}: {}",&cmd,e);
String::from("")
});
Ok(())
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
pub enum RuleSchedule {
None,
Check,
OnDemand,
}
impl Default for RuleSchedule {
fn default() -> Self {
RuleSchedule::None
}
}
impl fmt::Display for RuleSchedule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RuleSchedule::None => write!(f,"none"),
RuleSchedule::Check=> write!(f,"chechk"),
RuleSchedule::OnDemand=> write!(f,"ondemand"),
}
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
pub enum RuleOperator {
None,
Equal,
NotEqual,
Contains,
NotContains,
}
impl Default for RuleOperator {
fn default() -> Self {
RuleOperator::None
}
}
impl RuleOperator {
pub fn on(&self, target: &str, value: &str) -> bool {
match self {
RuleOperator::None => true,
RuleOperator::Equal => target == value,
RuleOperator::NotEqual=> target != value,
RuleOperator::Contains => target.contains(value),
RuleOperator::NotContains => !target.contains(value),
}
}
}
impl fmt::Display for RuleOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RuleOperator::None => write!(f,"none"),
RuleOperator::Equal => write!(f,"equal"),
RuleOperator::NotEqual=> write!(f,"not_equal"),
RuleOperator::Contains => write!(f,"matches"),
RuleOperator::NotContains => write!(f,"not_matches"),
}
}
}
#[allow(clippy::missing_docs_in_private_items)]
#[allow(non_snake_case)]
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct MonitorStates {
pub when: String,
pub id: String,
pub data: String, // to save JSON info
}
impl MonitorStates {
pub fn load(root: &str, path: &str, format: &str) -> Self {
println!("Load Montior States ...");
let data_rules = load_fs_content(&root, &path, &format);
let res: MonitorStates = serde_json::from_str(&data_rules).unwrap_or_else(|e|{
eprintln!("Error loading MonitorStates {}/{}.{}: {}",&root,&path,&format,e);
MonitorStates::default()
});
res
}
pub fn dump(&self, root: &str, path: &str, format: &str) -> Result<()> {
println!("Dump Montior States ...");
let data_str = serde_json::to_string(&self).unwrap_or_else(|e| {
eprintln!("Error dump MonitorRules {}/{}.{}: {}",&root,&path,&format,e);
String::from("")
});
if ! data_str.is_empty() {
println!("Dump MonitorStates to {}/{}.{}",&root,&path,&format);
let output_path = format!("{}/{}.{}",&root,&path,&format);
write_str_data(output_path, data_str, "Monitor states dump".to_string())?;
}
Ok(())
}
}
fn deserialize_rulecontext<'de, D>(deserializer: D) -> Result<RuleContext, D::Error>
where D: Deserializer<'de> {
let buf = String::deserialize(deserializer)?;
// let res = String::from_str(&buf).unwrap_or_else(|e|{
// eprintln!("deserialize rulecontext error: {}",e);
// String::from("")
// });
// println!("buf: {}",&buf);
let v: Vec<&str> = buf.split(':').collect();
if v.len() > 1 {
Ok(match v[0] {
"Service" => RuleContext::Service(v[1].to_owned()),
"Server" => RuleContext::Server(v[1].to_owned()),
"Ip" => RuleContext::Ip(v[1].to_owned()),
_ => RuleContext::None
})
} else {
Ok(RuleContext::None)
}
}
fn deserialize_ruleactions<'de, D>(deserializer: D) -> Result<Vec<MonitorAction>, D::Error>
where D: Deserializer<'de> {
let buf = Vec::deserialize(deserializer)?;
let mut actions = Vec::new();
buf.iter().for_each(|it: &String| {
let v: Vec<&str> = it.split(':').collect();
if v.len() > 1 {
let arg: Vec<&str> = v[1].split(',').collect();
if arg.len() > 1 {
actions.push(match v[0] {
"Start" => MonitorAction::Start(arg[0].to_owned(),arg[1].to_owned(),arg[2].to_owned()),
"Stop" => MonitorAction::Stop(arg[0].to_owned(),arg[1].to_owned(),arg[2].to_owned()),
"Restart" => MonitorAction::Restart(arg[0].to_owned(),arg[1].to_owned(),arg[2].to_owned()),
"Notify" => MonitorAction::Notify(arg[0].to_owned(),arg[1].to_owned(),arg[2].to_owned()),
_ => MonitorAction::None
})
}
}
});
Ok(actions)
}
#[allow(clippy::missing_docs_in_private_items)]
#[allow(non_snake_case)]
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct MonitorRule {
pub id: String,
pub name: String,
pub description: String,
#[serde(deserialize_with = "deserialize_rulecontext")]
pub context: RuleContext,
pub command: String,
pub selector: HashMap<String,String>,
pub operator: RuleOperator,
pub value: String,
pub result: bool,
pub use_in_state: bool,
pub wait_checks: isize,
pub schedule: RuleSchedule,
#[serde(deserialize_with = "deserialize_ruleactions")]
pub actions: Vec<MonitorAction>,
}
impl MonitorRule {
pub fn on_selector(&self) -> Result<String> {
let ctx_val = self.context.get_value();
let target: Vec<&str> = ctx_val.split("/").collect();
let srv_path = match target.len() {
2 => String::from(target[1]),
_ => String::from(""),
};
let mut cmd = match &self.context {
RuleContext::Server(v) => String::from(v),
RuleContext::Service(v) =>
if v.contains("kubernetes") {
String::from("kubectl")
} else {
String::from(v)
}
,
RuleContext::Ip(v) => String::from(v),
RuleContext::None => String::from(""),
};
if let Some(ctxpath) = self.selector.get("ctxpath") {
cmd = format!("{}{} {}",&ctxpath,&srv_path,&cmd);
}
if let Some(run) = self.selector.get("run") {
cmd = format!("{} {}",&run,&cmd);
}
if let Some(typ) = self.selector.get("type") {
cmd = format!("{} get {}",&cmd,&typ);
}
if let Some(ns) = self.selector.get("ns") {
cmd = format!("{} -n {}",&cmd,&ns);
}
if let Some(custom) = self.selector.get("custom") {
cmd = format!("{} {}",&cmd,&custom);
}
if let Some(pat) = self.selector.get("match") {
cmd = format!("{} | grep {}",&cmd,&pat);
}
if let Some(awk) = self.selector.get("awk") {
cmd = format!("{} | awk '{}'",&cmd,&awk);
}
if let Some(sort) = self.selector.get("sort") {
cmd = format!("{} | sort {}",&cmd,&sort);
}
if cmd.len() > 0 {
run_command(&cmd)
} else {
Err(anyhow!("Selector: failed:\n {}",&self.id))
}
}
pub async fn log_state(&self, app_env: &AppEnv, env_name: &str, env_state: isize, msg: &str, out_cmd: &str) -> Result<()> {
let now = format!("{}", chrono::Utc::now().timestamp());
let log_entry = AppLogs {
id: format!("{}_{}", self.id,&now),
name: format!("self: {}", self.name),
when: String::from("run_monitor_self"),
source: format!("{}", &env_name),
target: format!("{}", &out_cmd),
state: format!("{}", &env_state),
msg: format!("{}", &msg),
};
if app_env.config.logs_store == DataStore::File {
log_entry.write_applogs_entry(format!("{}/monitor.log",&app_env.config.logs_path).as_str()).await?;
}
Ok(())
}
pub async fn on_server(&self,_srvr: &str, _out_cmd: &str) -> Result<()> {
Ok(())
}
pub async fn on_service(&self, srvc: &str, out_cmd: &str) -> Result<()> {
println!("Service: {} -> {}",&srvc,&out_cmd);
if !out_cmd.is_empty() {
let mut targets = out_cmd.split_whitespace();
while let Some(target) = targets.next() {
// println!("target: {}",&target);
for action in &self.actions {
action.on(target.to_owned(), self).await?;
}
};
}
Ok(())
}
pub async fn on_ip(&self,_ip: &str, _out_cmd: &str) -> Result<()> {
// tokio::spawn(async move {run_on_rule(rule.to_owned()).await; });
Ok(())
}
pub async fn on_context(&self, out_cmd: &str) -> String {
let target: String;
let res = match &self.context {
RuleContext::Server(v) => {
target = v.to_owned();
self.on_server(&v, &out_cmd).await
},
RuleContext::None => return String::from(""),
RuleContext::Service(v) => {
target = v.to_owned();
self.on_service(&v, &out_cmd).await
},
RuleContext::Ip(v) => {
target = v.to_owned();
self.on_ip(&v, &out_cmd).await
},
};
match res {
Ok(_) => String::from(""),
Err(e) => {
format!("Error rule {} on {}: {}",&self.id,target,e)
}
}
}
pub async fn notify(&self, msg: String) {
for item in self.actions.iter() {
if item.is_notify() {
let _ = item.on(msg.to_owned(),&self).await;
}
}
}
pub async fn run(&self,monitor_rules: &MonitorRules, idx: usize, app_env: &AppEnv) -> Result<()> {
if envmnt::get_isize("DEBUG",0) > 0 {
println!("{} [{}] {}: {}",&idx,&self.id,&self.name,&self.description);
}
let out_cmd: String;
if self.command.is_empty() {
out_cmd=self.on_selector().unwrap_or_else(|e| {
eprintln!("Error on selector {}: {}",&self.id,e);
String::from("?")
});
} else {
out_cmd=run_command(&self.command).unwrap_or_else(|e| {
eprintln!("Error on command {}: {}",&self.command,e);
String::from("?")
});
}
if out_cmd.as_str() == "?" {
return Ok(());
}
let monitor_name = format!("{}_MONITOR",&monitor_rules.name.to_uppercase().replace(" ","_"));
let monitor_name_value = envmnt::get_or(&monitor_name,"");
let env_name = format!("MONITOR_{}",&self.id);
let env_state = envmnt::get_isize(&env_name,0);
if ! &self.operator.on(&out_cmd, &self.value) {
if env_state > 0 {
let msg = format!("{} Ok in state: {}",&env_name,&env_state);
println!("{}",&msg);
envmnt::set_isize(&env_name, 0);
self.log_state(&app_env, &env_name, env_state, &msg, &out_cmd).await?;
self.notify(format!("Ok state from -> {}",&env_state)).await;
if monitor_name_value == self.id {
envmnt::set(&monitor_name,"");
}
}
return Ok(());
}
if ! monitor_name_value.is_empty() {
let msg = format!("Monitor {}: rule {} is active, waiting for {}",&monitor_rules.name,&monitor_name_value,&self.id);
println!("{}",&msg);
self.log_state(&app_env, &env_name, env_state, &msg, &out_cmd).await?;
return Ok(());
}
if env_state > 0 && env_state < self.wait_checks {
let msg = format!("{} is already set: {} increment counter",&env_name,&env_state);
envmnt::increment(&env_name);
println!("{}",&msg);
self.log_state(&app_env, &env_name, env_state, &msg, &out_cmd).await?;
return Ok(());
}
let result_rule_context = self.on_context(&out_cmd).await;
let msg = match result_rule_context.is_empty() {
true =>
if self.operator == RuleOperator::None {
format!("{}: done",&env_name)
} else {
format!("{}: 1 done",&env_name)
},
false => format!("{} {}",&env_name,&result_rule_context),
};
if self.operator == RuleOperator::None {
envmnt::set(&monitor_name,"");
} else {
envmnt::set(&monitor_name,format!("{}",&self.id));
envmnt::set_isize(&env_name,1);
}
println!("{}",&msg);
self.log_state(&app_env, &env_name, env_state, &msg, &out_cmd).await?;
Ok(())
}
}
#[allow(clippy::missing_docs_in_private_items)]
#[allow(non_snake_case)]
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct MonitorRules {
pub name: String,
pub description: String,
pub rules: Vec<MonitorRule>,
}
impl MonitorRules {
pub fn new(name: String, description: String, rules: Vec<MonitorRule> ) -> Self {
Self {
name,
description,
rules,
}
}
pub fn load(root: &str, path: &str, format: &str) -> Self {
let data_rules = load_fs_content(&root, &path, &format);
let err_load = |e|{
eprintln!("Error loading MonitorRules {}/{}.{}: {}",&root,&path,&format,e);
MonitorRules::default()
};
let res: MonitorRules;
if data_rules.is_empty() {
res = MonitorRules::default();
} else {
// println!("Parse Montior Rules as: {}",&format);
res = match format {
"json" => serde_json::from_str(&data_rules).unwrap_or_else(|e| err_load(format!("{}",e))),
"yaml" => serde_yaml::from_str(&data_rules).unwrap_or_else(|e| err_load(format!("{}",e))),
"toml" => toml::from_str(&data_rules).unwrap_or_else(|e| err_load(format!("{}",e))),
_ => MonitorRules::default(),
};
}
res
}
pub fn dump(&self, root: &str, path: &str, format: &str) -> Result<()> {
let err_load = |e| {
eprintln!("Error dump MonitorRules {}/{}.{}: {}",&root,&path,&format,e);
String::from("")
};
let data_str = match format {
"json" => serde_json::to_string(&self).unwrap_or_else(|e| err_load(format!("{}",e))),
"yaml" => serde_yaml::to_string(&self).unwrap_or_else(|e| err_load(format!("{}",e))),
"toml" => toml::to_string(&self).unwrap_or_else(|e| err_load(format!("{}",e))),
_ => String::from(""),
};
if ! data_str.is_empty() {
println!("Dump loading MonitorRules to {}/{}.{}",&root,&path,&format);
let output_path = format!("{}/{}.{}",&root,&path,&format);
write_str_data(output_path, data_str, "Monitor rules dump".to_string())?;
}
Ok(())
}
pub async fn run(&self, _cloud: Cloud, app_env: AppEnv) -> Result<()> {
// println!("Run {}: {}",&monitor_rules.name,&monitor_rules.description);
for (idx,rule) in self.rules.iter().enumerate() {
match rule.schedule {
RuleSchedule::Check => {
// dbg!(&rule);
let mut err = String::from("");
rule.run(&self,idx,&app_env).await.unwrap_or_else(|e| {
err = format!("Error rule {}: {}",&rule.id,e);
eprintln!("{}",&err)
});
match err.is_empty() {
true => continue,
false => rule.log_state(&app_env, &rule.name, -1, &err, "").await.unwrap_or_else(|e|
eprintln!("Unable to log monitor entry: {}",e)
),
};
},
RuleSchedule::OnDemand => continue,
RuleSchedule::None => continue,
};
}
Ok(())
}
}

47
src/monitor/utils.rs Normal file
View File

@ -0,0 +1,47 @@
use anyhow::{anyhow,Result};
use std::{fs}; //,io};
use std::fs::OpenOptions;
use std::path::Path;
use std::io::{Write};
use std::process::{Command};
use std::str;
pub fn write_str_data(output_path: String, data_str: String, msg: String) -> Result<()> {
if Path::new(&output_path).exists() {
fs::remove_file(&output_path)?;
}
let mut file = OpenOptions::new().write(true).create(true).open(&output_path)?;
file.write_all(data_str.as_bytes())?;
if ! msg.is_empty() {
let now = chrono::Utc::now().timestamp();
let out = format!("{}: [{}] -> {}\n",&now,&msg,&output_path);
println!("{}",&out);
}
Ok(())
}
pub fn run_command(str_cmd: &str) -> Result<String> {
let mut args: Vec<&str> = str_cmd.split(" ").collect();
let cmd = args[0].to_owned();
args.remove(0);
// println!("cmd: {}, args: {:?}",&cmd,&args);
let result = Command::new(&cmd)
.args(&args)
.output()?;
// dbg!(&result);
match &result.status.code() {
Some(code) =>
if format!("{}",code) == "0" {
let mut res=String::from(str::from_utf8(&result.stdout).unwrap_or_else(|_| ""));
if res.ends_with("\n") {
res.pop();
}
res = res.replace("\n"," ").replace("<none>","");
Ok(res)
} else {
let err = str::from_utf8(&result.stderr).unwrap_or_else(|_| "");
Err(anyhow!("Running {}: failed:\n {}",&cmd,&err))
}
None => Ok(String::from(""))
}
}