diff --git a/src/clouds.rs b/src/clouds.rs index aeedb47..1366a63 100644 --- a/src/clouds.rs +++ b/src/clouds.rs @@ -1,3 +1,4 @@ pub mod defs; -pub mod on_clouds; +pub mod utils; +pub mod on_req; pub mod upcloud; \ No newline at end of file diff --git a/src/clouds/on_clouds.rs b/src/clouds/on_clouds.rs deleted file mode 100644 index 7558875..0000000 --- a/src/clouds/on_clouds.rs +++ /dev/null @@ -1,855 +0,0 @@ -use anyhow::{anyhow,Result,Context, Error}; -use std::{fs}; //,io}; -use std::fs::OpenOptions; -use std::io::{Write}; -use std::path::Path; -use rfm::mkdir; -use std::str; -use std::process::{Command}; -// use std::ffi::OsString; - -use reqenv::ReqEnv; -use crate::utils::{liveness_check}; -use crate::clouds::defs::{ - CloudEnv, - Cloud, - Provider, - TskSrvc, - App, - MainResourcesConfig, -}; -use crate::defs::{ - IsCritical, - KloudHome, - KloudCheckHome, - SSHAccess, - Cntrllr, - CloudGroup, - CloudCheckGroup, - CloudItem, - CloudCheckItem -}; -use crate::clouds::defs::{TsksrvcInfo,AppsrvcInfo,SrvcsHostInfo}; - -use crate::clouds::upcloud::{get_upcloud_info,run_on_upcloud_info,run_on_upcloud_check}; - -/// On_cloud -/// load __`item`__ form `envmnt` with `dflt` -/// Check if path from `source` exits for `item` or fallback to check from `root` path. -/// __`item`__ path is requiered to exist -pub async fn get_env_path(item: &str, dflt: &str, source: &str, root: &str, is_tpl: bool) -> Result { - let mut base = dflt.to_owned(); - if item.len() > 0 { - base=envmnt::get_or(item,dflt); - } - if Path::new(&base).has_root() { - if Path::new(&base).exists() { - return Ok(base); - } else { - return Err(anyhow!("Path {} not found", &base)); - } - } - #[allow(unused_assignments)] - let mut item_path= String::from(""); - if source.len() > 0 { - item_path = format!("{}/{}",&source,&base); - } else { - item_path = format!("{}",&base); - } - if ! Path::new(&item_path).exists() { - item_path=format!("{}/{}",root,&base); - if is_tpl && ! Path::new(&item_path).exists() { - item_path=format!("{}/{}",&source,&base); - if ! Path::new(&item_path).exists() { - item_path=format!("{}/{}",&root,&base); - } - } - if ! Path::new(&item_path).exists() { - return Err(anyhow!("Path '{}' not found in {} - {}", &base,&source,&root)); - } - } - // println!("Path: {} src {} root {} item {}", &base, &source, &root, item_path); - Ok(base) -} -/// env_cloud -/// Scanning environment from __CLOUD_PATH__ overloaded with __CLOUD_HOME__ `source` CliOpts argument (-s) in case -/// Load path from `envmnt` and __`get_env_path`___ -/// __`source`__ path is mandatory -pub async fn env_cloud(source: &str, cloud_env: &mut CloudEnv) -> Result<()> { - cloud_env.path = envmnt::get_or("ROOT_KLDS", ""); - if cloud_env.path.is_empty() { - return Err(anyhow!("Clouds Root Path {} not found", &cloud_env.source_path)); - } - cloud_env.home=get_env_path("KLDS_HOME","home", "", &cloud_env.path,false).await?; - cloud_env.monitor_run=envmnt::get_or("KLD_MONITOR_RUN","kloud_mon"); - cloud_env.source=source.to_owned(); - cloud_env.config_root = envmnt::get_or("KLD_CONFIG_ROOT", "config"); - if source == "*" { - cloud_env.config_path = envmnt::get_or("KLD_CONFIG", "config.yaml"); - cloud_env.config_json_path = envmnt::get_or("KLD_CONFIG_JSON", "config.json"); - return Ok(()) - } - let arr_source: Vec<&str> = source.split_terminator("/").collect(); - if let Some(name) = arr_source.get(0) { - cloud_env.cloud= format!("{}",&name); - } - if let Some(group) = arr_source.get(1) { - cloud_env.group = format!("{}",&group); - } - if let Some(target) = arr_source.get(2) { - cloud_env.target = format!("{}",&target); - } - cloud_env.source_path = format!("{}/{}",&cloud_env.home,&source); - if ! Path::new(&cloud_env.source_path).exists() { - return Err(anyhow!("Clouds Source Path {} not found", &cloud_env.source_path)); - } - cloud_env.config_path=get_env_path("KLDS_CONFIG","config.yaml", - format!("{}/{}",&cloud_env.source_path,&cloud_env.config_root).as_str(), - &cloud_env.path,false).await?; - cloud_env.config_json_path=get_env_path("KLDS_CONFIG_JSON","config.json", - format!("{}/{}",&cloud_env.source_path,&cloud_env.config_root).as_str(), - &cloud_env.path,false).await?; - if cloud_env.group.is_empty() { - cloud_env.provision=format!("{}/{}/{}/{}",&cloud_env.home,&cloud_env.cloud,envmnt::get_or("CLOUD_PROVISION","provision"),&cloud_env.target); - } else { - cloud_env.provision=format!("{}/{}/{}/{}/{}",&cloud_env.home,&cloud_env.cloud,&cloud_env.group,envmnt::get_or("CLOUD_PROVISION","provision"),&cloud_env.target); - } - if ! Path::new(&cloud_env.provision).exists() { - let dir_path_buf = Path::new(&cloud_env.provision).to_path_buf(); - let dirs: Vec<&std::path::PathBuf> = vec![&dir_path_buf]; - mkdir(&dirs).with_context(|| format!("\nFailed to create dir path {}", &cloud_env.provision))?; - if envmnt::get_isize("DEBUG",0) > 1 { - println!("{} created", &cloud_env.provision); - } - } - cloud_env.wk_path = envmnt::get_or("KLDS_WKDIR", "/tmp"); - cloud_env.tsksrvcs_path=get_env_path("KLDS_TSKSRVCS","tsksrvcs", &cloud_env.source_path,&cloud_env.path,false).await?; - cloud_env.versions=get_env_path("KLDS_VERSIONS","versions.yaml", &cloud_env.source_path,&cloud_env.path,false).await?; - Ok(()) -} -pub async fn clear_specs(source: &str) -> Result<()> { - let env_path = envmnt::get_or("ROOT_KLDS", "clouds"); - let env_home = get_env_path("KLDS_HOME","home", "", &env_path,false).await?; - let env_source=format!("{}/{}",&env_home,&source); - if ! Path::new(&env_source).exists() { - return Ok(()); - } - let env_provision=format!("{}/{}",&env_source,envmnt::get_or("KLDS_PROVISION","provision")); - let env_specs=format!("{}/specs",env_provision); - if Path::new(&env_specs).exists() { - if envmnt::get_isize("DEBUG",0) > 1 { - println!("Delete {}/specs",env_provision); - } - fs::remove_dir_all(&env_specs)?; - } - Ok(()) -} -pub async fn load_config_data(cloud: &Cloud, source: &str) -> Result { - // dbg!(&cloud); - let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&source,&cloud.env.config_root,&cloud.env.config_path); - let cfg_data = fs::read_to_string(&cfg_path).with_context(|| format!("Failed to read 'cfg_path' from {}", &cfg_path))?; - Ok(cfg_data) -} -pub async fn load_config_json_data(cloud: &Cloud, source: &str) -> Result { - let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&source,&cloud.env.config_root,&cloud.env.config_json_path); - let cfg_data = fs::read_to_string(&cfg_path).with_context(|| format!("Failed to read json 'cfg_path' from {}", &cfg_path))?; - Ok(cfg_data.replace("\n","")) -} -pub async fn load_cloud_env(cloud: &mut Cloud, source: &str) -> Result<()> { - env_cloud(source, &mut cloud.env).await?; - Ok(()) -} -pub async fn load_cloud_config(cloud: &mut Cloud, source: &str) -> Result<(KloudHome,Provider,String), Error> { - // dbg!(&cloud); - let cfg_data = load_config_data(&cloud,source).await?; - let cfg: KloudHome = serde_yaml::from_str(&cfg_data)?; - let cfg_provider = format!("{}",cfg.provider[0]); - let provider = cloud.providers.get(&cfg_provider).with_context(|| - format!("Provider '{}'' not defined", &cfg_provider))?; - Ok((cfg, provider.to_owned(), cfg_data)) -} -pub async fn load_cloud_check_config(cloud: &mut Cloud, source: &str) -> Result<(KloudCheckHome,Provider,String), Error> { - // dbg!(&cloud.env); - let cfg_data = load_config_data(&cloud,source).await?; - let cfg: KloudCheckHome = serde_yaml::from_str(&cfg_data)?; - let cfg_provider = format!("{}",cfg.provider[0]); - let provider = cloud.providers.get(&cfg_provider).with_context(|| format!("Provider '{}'' not defined", &cfg_provider))?; - Ok((cfg, provider.to_owned(), cfg_data)) -} -pub async fn load_cloud_name_config(cloud: &mut Cloud, source: &str) -> Result<(MainResourcesConfig,Provider,String), Error> { - // dbg!(&cloud.env); - let cfg_data = load_config_data(&cloud,source).await?; - let cfg: MainResourcesConfig = serde_yaml::from_str(&cfg_data)?; - let provider = cloud.providers.get(&cfg.provider).with_context(|| format!("Provider '{}'' not defined", &cfg.provider))?; - Ok((cfg, provider.to_owned(), cfg_data)) -} -pub async fn get_cloud_monitor_info(cloud: &mut Cloud, source: &str) -> Result { - let cloud_home_path = format!("{}/{}",&cloud.env.home,&source); - let monitor_path = format!("{}/{}",&cloud_home_path,&cloud.env.monitor_run); - if Path::new(&monitor_path).exists() { - let output = Command::new("bash") - .arg(format!("{}",&monitor_path)) - .arg("-o") - .arg("json") - .arg(format!("{}",&source)) - .output()?; - if !&output.status.success() { - return Err(anyhow!("Run {} for {} failed: {}",&cloud.env.monitor_run,&source,&output.status)); - } - return Ok(str::from_utf8(&output.stdout).unwrap_or_else(|_| "").to_owned()); - } - Ok("".to_owned()) -} -pub async fn get_cloud_home_list(cloud: &Cloud) -> Result> { - let kloud_files: Vec = fs::read_dir(&cloud.env.home)? - .filter_map(|res| - match res.map(|e| e.file_name()) { - Ok(entry) => { - // for e.path() - // let file_path = entry.as_path().display().to_string(); - let file_path = format!("{}",entry.to_owned().into_string().unwrap_or_else(|_|String::from(""))); - let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&file_path,&cloud.env.config_root,&cloud.env.config_path); - let first_char = file_path.chars().next().unwrap_or_default().to_string(); - if first_char.as_str() != "_" && first_char.as_str() != "." && Path::new(&cfg_path).exists() { - Some(file_path) - } else { - None - } - }, - Err(e) => { - eprintln!("Error filter_map {}",e); - None - } - } - ) - .collect(); - Ok(kloud_files.to_owned()) -} - -pub fn run_ssh_on_srvr(hostname: &str,tsksrvc_name: &str, tsksrvc_cmd: &str, ssh_access: &SSHAccess) -> Result { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("Checking connection to {}@{} on {} for {} ",ssh_access.user,ssh_access.host,ssh_access.port,&tsksrvc_name); - println!("ssh {} /var/lib/klouds/bin/{}_info.sh yaml",&hostname,&tsksrvc_name); - } - let output = Command::new("ssh") - .arg("-q") - .arg("-o") - .arg("StrictHostKeyChecking=accept-new") - .arg("-p") - .arg(format!("{}",ssh_access.port)) - .arg(format!("{}@{}",ssh_access.user,ssh_access.host)) - .arg("sudo") - .arg(format!("/var/lib/klouds/bin/{}_info.sh",&tsksrvc_name)) - .arg("yaml") - .arg(format!("{}",&tsksrvc_cmd)) -// .arg(format!("ssh {} /var/lib/klouds/bin/{}_info.sh yaml",&hostname,&tsksrvc_name)) - .output()?; - //dbg!(&output); - if !&output.status.success() { - return Err(anyhow!("Connection to '{}' for tsksrvc '{}' failed: {}",&hostname,&tsksrvc_name,&output.status)); - } - let res = str::from_utf8(&output.stdout).unwrap_or_else(|_| ""); - let info: String = serde_yaml::from_str(&res) - .unwrap_or_else(|e| { - eprintln!("serde_yaml: {}",e); - String::from("") - }); - Ok(info.to_owned()) -} -pub async fn parse_srvr_tsksrvcs(hostname: &str, sshaccess: &SSHAccess, tsksrvcs: &Vec,req_tsksrvcs: &str) -> Vec { - let mut tsksrvcs_info: Vec = Vec::new(); - for tsk in req_tsksrvcs.split(",") { - match format!("{}",&tsk).as_str() { - "os" => { - let os_name = String::from("os"); - tsksrvcs_info.push( TsksrvcInfo { - name: format!("{}",&os_name), - info: run_ssh_on_srvr(&hostname, &os_name, "", &sshaccess) - .unwrap_or_else(|e| { - eprintln!("run_ssh_on_srvr os: {}",e); - String::from("") - }), - srvc: TskSrvc::default(), - }); - }, - "floatip" => { - let floatip_name = String::from("floatip"); - tsksrvcs_info.push( TsksrvcInfo { - name: format!("{}",&floatip_name), - info: run_ssh_on_srvr(&hostname, &floatip_name, "", &sshaccess) - .unwrap_or_else(|e| { - eprintln!("run_ssh_on_srvr floatip: {}",e); - String::from("") - }), - srvc: TskSrvc::default(), - }); - }, - "kubernetes_pods" => { - let k8_name = String::from("kubernetes"); - tsksrvcs_info.push(TsksrvcInfo { - name: format!("{}_pods",&k8_name), - info: run_ssh_on_srvr(&hostname, &k8_name, "pods", &sshaccess) - .unwrap_or_else(|e| { - eprintln!("run_ssh_on_srvr kubernetes_pods: {}",e); - String::from("") - }), - srvc: TskSrvc::default(), - }); - }, - _ => { continue; } - }; - } - for tsksrvc in tsksrvcs.iter() { - match format!("{}",&tsksrvc.name).as_str() { - "pause" => continue, - "scale" => continue, - "systemfix" => continue, - "os" => continue, - _ => { - let name = format!("{}",&tsksrvc.name); - if req_tsksrvcs == "all" || req_tsksrvcs.contains(&name) { - // TODO ssh &srv.hostname to get &name in "yaml" - //println!("{} {} {}",&hostname,sshaccess.user,&tksrvc.name); - tsksrvcs_info.push(TsksrvcInfo { - name: format!("{}",&tsksrvc.name), - info: run_ssh_on_srvr(&hostname, &name, "", &sshaccess) - .unwrap_or_else(|e| { - eprintln!("run_ssh_on_srvr for {}: {}",&tsksrvc.name,e); - String::from("") - }), - srvc: tsksrvc.to_owned(), - }); - } - } - }; - } - tsksrvcs_info.to_owned() -} -pub async fn liveness_srvr_tsksrvcs(source: &str, cntrllrs: &Vec, tsksrvcs: &Vec, req_tsksrvc: &str) -> Vec { - let mut tsksrvcs_info: Vec = Vec::new(); - let debug=envmnt::get_isize("DEBUG",0); - for tsksrvc in tsksrvcs.iter() { - match format!("{}",&tsksrvc.name).as_str() { - "pause" => continue, - "scale" => continue, - "systemfix" => continue, - _ => { - let name = format!("{}",&tsksrvc.name); - if tsksrvc.liveness.is_empty() || (req_tsksrvc != "" && !req_tsksrvc.contains(&name)) { - continue; - } - let serverstring = format!("{}",&tsksrvc.liveness); - if debug > 2 { - println!("livenes: {} -> {}",&tsksrvc.name,&serverstring); - } - let res_info = match liveness_check(&source,&cntrllrs,&serverstring,"",&name).await { - Ok(_) => "ok", - Err(e) => { - if tsksrvc.critical == IsCritical::yes { - let monitor_name = "WUJI_MONITOR"; - println!("{} critical livenes: {} -> {}",envmnt::get_or(&monitor_name,""),&tsksrvc.name,&serverstring); - } - if debug > 0 { - eprint!("liveness_check error: {}",e); - } - "err" - }, - }; - // println!("{} info: {}",&tsksrvc.name,&res_info); - tsksrvcs_info.push(TsksrvcInfo { - name: format!("{}",&tsksrvc.name), - info: serde_yaml::from_str(res_info).unwrap_or_else(|e| { - eprintln!("Serde liveness Error: {} {} -> {}",&source,&serverstring,e); - String::from("") - }), - srvc: tsksrvc.to_owned(), - }); - }, - } - } - tsksrvcs_info.to_owned() -} -pub async fn parse_srvr_appsrvcs(hostname: &str, sshaccess: &SSHAccess, appsrvcs: &Vec,req_tsksrvcs: &str) -> Vec { - let mut appsrvcs_info: Vec = Vec::new(); - for appsrvc in appsrvcs.iter() { - let name = format!("{}",&appsrvc.name); - if req_tsksrvcs == "all" || req_tsksrvcs.contains(&name) { - // TODO ssh &srv.hostname to get &name in "yaml" - //println!("{} {} {}",&hostname,sshaccess.user,&tksrvc.name); - appsrvcs_info.push(AppsrvcInfo { - name: format!("{}",&appsrvc.name), - info: run_ssh_on_srvr(&hostname, &name, "", &sshaccess) - .unwrap_or_else(|e| { - eprintln!("run_ssh_on_srvr for {}: {}",&appsrvc.name,e); - String::from("") - }), - srvc: appsrvc.to_owned(), - }); - } - } - appsrvcs_info.to_owned() -} -pub async fn liveness_srvr_appsrvcs(source: &str, cntrllrs: &Vec, appsrvcs: &Vec, req_tsksrvc: &str) -> Vec { - let mut appsrvcs_info: Vec = Vec::new(); - let debug=envmnt::get_isize("DEBUG",0); - for appsrvc in appsrvcs.iter() { - // match format!("{}",&appsrvc.name).as_str() { - // "pause" => continue, - // "scale" => continue, - // "systemfix" => continue, - // _ => { - let name = format!("{}",&appsrvc.name); - if appsrvc.liveness.is_empty() || (req_tsksrvc != "" && !req_tsksrvc.contains(&name)) { - continue; - } - let serverstring = format!("{}",&appsrvc.liveness); - let live_req = format!("{}",&appsrvc.req); - if debug > 2 { - println!("livenes: {} -> {}",&appsrvc.name,&serverstring); - } - let res_info = match liveness_check(&source,&cntrllrs,&serverstring,&live_req,&name).await { - Ok(_) => "ok", - Err(e) => { - if appsrvc.critical == IsCritical::yes { - let monitor_name = "WUJI_MONITOR"; - println!("{} critical livenes: {} -> {}",envmnt::get_or(&monitor_name,""),&appsrvc.name,&serverstring); - } - if debug > 0 { - eprint!("liveness_check error: {}",e); - } - "err" - }, - }; - // println!("{} info: {}",&appsrvc.name,&res_info); - appsrvcs_info.push(AppsrvcInfo { - name: format!("{}",&appsrvc.name), - info: serde_yaml::from_str(res_info).unwrap_or_else(|e| { - eprintln!("Serde liveness Error: {} {} -> {}",&source,&serverstring,e); - String::from("") - }), - srvc: appsrvc.to_owned(), - }); - // }, - // } - } - appsrvcs_info.to_owned() -} -pub async fn get_provider_info(provider: &str, hostname: &str, cmd: &str , cfg_path: &str) -> String { - match provider { - "upcloud" => { - get_upcloud_info(hostname,cmd,cfg_path).await - }, - _ => String::from("") - } -} - -pub async fn run_on_provider_info(reqname: &str, req_tsksrvc: &str, req_srvrs: &str, provider: Provider, source: &str, cfg_data: String, env_cloud: &Cloud) -> String { - match provider.name.as_str() { - "upcloud" => { - // TODO clean SSH keys or encrypt content - // dbg!(&cloud_config); - run_on_upcloud_info(reqname,req_tsksrvc,req_srvrs, source, cfg_data, env_cloud).await - }, - _ => { - let result = format!("Errors on {} provider {} not found",&source,&provider.name); - if envmnt::get_isize("DEBUG",0) > 1 { - println!("{}",&result); - } - result - } - } -} -pub async fn run_on_provider_check(reqname: &str, req_tsksrvc: &str, req_srvrs: &str, provider: Provider, source: &str, cfg_data: String, env_cloud: &Cloud) -> Vec { - match provider.name.as_str() { - "upcloud" => { - // TODO clean SSH keys or encrypt content - // dbg!(&cloud_config); - run_on_upcloud_check(reqname,req_tsksrvc,req_srvrs, source, cfg_data, env_cloud).await - }, - _ => { - if envmnt::get_isize("DEBUG",0) > 1 { - println!("Errors on {} provider {} not found",&source,&provider.name); - } - Vec::new() - } - } -} -pub async fn on_cloud_name_req_info(reqname: &str,env_cloud: &Cloud,_reqenv: &ReqEnv,req_tsksrvc: &str, req_srvrs: &str, source: &str) -> String { - let mut cloud = env_cloud.to_owned(); - load_cloud_env(&mut cloud, &source).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_env: {}",e); - }); - let (cfg,provider,cfg_data) = load_cloud_name_config(&mut cloud, &source).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_name_config: {}",e); - (MainResourcesConfig::default(),Provider::default(),String::from("")) - }); - if cfg.mainName.is_empty() || cfg_data.is_empty() { - let result = format!("Errors loading {}",&source); - if envmnt::get_isize("DEBUG",0) > 1 { - println!("{}",&result); - } - return result; - } - run_on_provider_info(&reqname,&req_tsksrvc,&req_srvrs,provider,&source,cfg_data,&cloud).await -} -pub async fn on_cloud_name_req_check(reqname: &str,env_cloud: &Cloud,_reqenv: &ReqEnv,req_tsksrvc: &str, req_srvrs: &str, source: &str) -> Vec { - let mut cloud = env_cloud.to_owned(); - load_cloud_env(&mut cloud, &source).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_env: {}",e); - }); - let (cfg,provider,cfg_data) = load_cloud_name_config(&mut cloud, &source).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_name_config: {}",e); - (MainResourcesConfig::default(),Provider::default(),String::from("")) - }); - if cfg.mainName.is_empty() || cfg_data.is_empty() { - if envmnt::get_isize("DEBUG",0) > 1 { - println!("Errors loading {}",&source); - } - return Vec::new(); - } - run_on_provider_check(&reqname,&req_tsksrvc,&req_srvrs,provider,&source,cfg_data,&cloud).await -} -pub async fn create_cloud_config(reqname: &str,req_tsksrvcs: &str,reqenv: &ReqEnv, entries: Vec,mut cloud: Cloud) -> String { - let config = reqenv.config(); - let debug = envmnt::get_isize("DEBUG",0); - let check_path = format!("{}/clouds.json",&config.check_path); - let mut check_entries: Vec = Vec::new(); - let mut no_check_entries = true; - if reqname != "check_job" { - if Path::new(&check_path).exists() { - // Load & Parse reuse liveness and monitor - let check_data = fs::read_to_string(&check_path).unwrap_or_else(|e|{ - eprintln!("Failed to read 'check_path' from {}: {}", &check_path,e); - String::from("") - }); - if !check_data.is_empty() { - check_entries = serde_json::from_str(&check_data).unwrap_or_else(|e| { - eprintln!("Error loading check_entries ({}): {}",&check_path,e); - Vec::new() - }); - no_check_entries=false; - if debug> 0 { - println!("Using check_entries from {}",&check_path); - } - // dbg!("{:#?}",&check_entries); - } - } - } - let mut entries_cfgs: Vec = Vec::new(); - for (idx, entry) in entries.iter().enumerate() { - let (mut cfg,_provider,cfg_data) = load_cloud_config(&mut cloud, &entry).await - .unwrap_or_else(|e| { - eprintln!("Load_cloud_config: {}",e); - (KloudHome::default(),Provider::default(),String::from("")) - }); - if cfg.name.is_empty() || cfg_data.is_empty() { - let result = format!("Errors loading {}",&entry); - if debug > 0 { - println!("{}",&result); - } - continue; - } - if req_tsksrvcs.contains("monitor") { - if no_check_entries { - cfg.monitor_info = Some(get_cloud_monitor_info(&mut cloud, &entry).await - .unwrap_or_else(|e| { - eprintln!("Error {} monitor_info {} -> {}",&entry,&cloud.env.monitor_run,e); - String::from("") - })); - } else if check_entries.len() > 0 && check_entries.len() < idx { - cfg.monitor_info = check_entries[idx].monitor_info.to_owned(); - } - } - if reqname.contains("provision") || req_tsksrvcs.contains("resources") || req_tsksrvcs.contains("resources") || req_tsksrvcs.contains("liveness") { - let mut groups: Vec = Vec::new(); - // cfg.groups = cfg.groups.map(|grp| grp.with_resources(grp.path.to_owned())).collect(); - for (grp_idx,grp) in cfg.groups.iter().enumerate() { - let mut items: Vec = Vec::new(); - for (itm_idx,itm) in grp.items.iter().enumerate() { - let resources: Option; - let liveness: Vec; - let provision: Option; - if req_tsksrvcs.contains("liveness") { - if no_check_entries { - liveness = on_cloud_name_req_check("liveness",&cloud,&reqenv,"","",&itm.path).await; - } else if check_entries.len() > 0 && check_entries.len() < idx { - liveness = check_entries[idx].groups[grp_idx].items[itm_idx].liveness.to_owned(); - } else { - liveness = Vec::new(); - } - } else { - liveness = itm.liveness.to_owned(); - } - if reqname.contains("provision") || req_tsksrvcs.contains("provision") { - provision = Some(on_cloud_name_req_info("provision",&cloud,&reqenv,"","",&itm.path).await); - } else { - provision = itm.provision.to_owned(); - } - if req_tsksrvcs.contains("resources") { - resources = Some(load_config_json_data(&cloud,&itm.path).await - .unwrap_or_else(|e| { - eprintln!("Error loading resources -> {}",e); - String::from("") - })); - } else { - resources = itm.resources.to_owned(); - } - items.push(CloudItem { - name: itm.name.to_owned(), - info: itm.info.to_owned(), - path: itm.path.to_owned(), - resources, - liveness, - provision, - graph: itm.graph.to_owned(), - critical: itm.critical.to_owned(), - }); - } - groups.push(CloudGroup { - name: grp.name.to_owned(), - info: grp.info.to_owned(), - path: grp.path.to_owned(), - // TODO check this for group - resources: grp.resources.to_owned(), - liveness: grp.liveness.to_owned(), - provision: grp.provision.to_owned(), - items, - graph: grp.graph.to_owned(), - prices: grp.prices.to_owned(), - }); - } - cfg.groups=groups; - } - entries_cfgs.push(cfg.to_owned()); - } - serde_json::to_string(&entries_cfgs).unwrap_or_else(|_| String::from("")).replace("\n","") -} - -pub async fn create_cloud_check(req_tsksrvcs: &str,reqenv: &ReqEnv,entries: Vec,mut cloud: Cloud) -> String { - let mut cfg_entries: Vec = Vec::new(); - for entry in entries.iter() { - let (mut cfg,_provider,cfg_data) = load_cloud_check_config(&mut cloud, &entry).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_check_config: {}",e); - (KloudCheckHome::default(),Provider::default(),String::from("")) - }); - if cfg.name.is_empty() || cfg_data.is_empty() { - let result = format!("Errors loading {}",&entry); - if envmnt::get_isize("DEBUG",0) > 0 { - println!("{}",&result); - } - continue; - } - if req_tsksrvcs.contains("monitor") { - cfg.monitor_info = Some(get_cloud_monitor_info(&mut cloud, &entry).await - .unwrap_or_else(|e| { - eprintln!("Error {} monitor_info {} -> {}",&entry,&cloud.env.monitor_run,e); - String::from("") - })); - } - if req_tsksrvcs.contains("liveness") || req_tsksrvcs.contains("apps") { - let mut groups: Vec = Vec::new(); - // cfg.groups = cfg.groups.map(|grp| grp.with_resources(grp.path.to_owned())).collect(); - for grp in cfg.groups.iter() { - let mut items: Vec = Vec::new(); - for itm in grp.items.iter() { - let liveness: Vec; - if req_tsksrvcs.contains("liveness") { - liveness = on_cloud_name_req_check("liveness",&cloud,&reqenv,"","",&itm.path).await; - } else if req_tsksrvcs.contains("apps") { - liveness = on_cloud_name_req_check("apps",&cloud,&reqenv,"","",&itm.path).await; - } else { - liveness = itm.liveness.to_owned(); - } - items.push(CloudCheckItem { - name: itm.name.to_owned(), - info: itm.info.to_owned(), - path: itm.path.to_owned(), - liveness, - critical: itm.critical.to_owned(), - }); - } - groups.push(CloudCheckGroup { - name: grp.name.to_owned(), - info: grp.info.to_owned(), - path: grp.path.to_owned(), - // TODO check this for group - liveness: grp.liveness.to_owned(), - items, - }); - } - cfg.groups=groups; - } - cfg_entries.push(cfg.to_owned()); - } - serde_json::to_string(&cfg_entries).unwrap_or_else(|_| String::from("")).replace("\n","") -} -pub async fn on_cloud_req(reqname: &str,env_cloud: &Cloud,reqenv: &ReqEnv,req_tsksrvcs: &str,_req_srvrs: &str, source: &str) -> String { - //println!("{}",&reqname); - let config = reqenv.config(); - // let lock_path = format!("{}/{}_{}{}",&config.cache_lock_path,&reqname,&req_tsksrvcs.replace(",","_"),&config.cache_lock_ext); - // if Path::new(&lock_path).exists() || reqname.ends_with("_job") { - if ! reqname.ends_with("_job") { - let output_path = format!("{}/{}_{}.json",&config.cache_path,&reqname,&req_tsksrvcs.replace(",","_")); - if Path::new(&output_path).exists() { - if envmnt::get_isize("DEBUG",0) > 0 { - println!("Using cache: {} at {}",&output_path,envmnt::get_or(format!("LAST_CACHE_{}",&output_path), "")); - } - let output_data = fs::read_to_string(&output_path).with_context(|| format!("Failed to read json cache 'outut_path' from {}", &output_path)) - .unwrap_or_else(|e| { - eprintln!("read file {}: {}",&output_path,e); - String::from("") - }); - return output_data; - } - } - let mut cloud = env_cloud.to_owned(); - load_cloud_env(&mut cloud, &source).await - .unwrap_or_else(|e| { - eprintln!("load_cloud_env {}",e); - }); - let entries: Vec; - if source == "*" { - entries = get_cloud_home_list(&cloud).await - .unwrap_or_else(|e| { - eprintln!("get_cloud_home_list: {}",e); - Vec::new() - }); - } else { - entries = vec!(source.to_string()); - } - match reqname { - "check_job" => - create_cloud_check(req_tsksrvcs,reqenv,entries,cloud).await, - "apps_check_job" => - create_cloud_check(req_tsksrvcs,reqenv,entries,cloud).await, - _ => - create_cloud_config(reqname,req_tsksrvcs,reqenv,entries,cloud).await, - } -} -pub async fn get_cloud_cache_req(reqenv: &ReqEnv,cloud: &Cloud, reqname: &str, reqname_job: &str) -> Result<()> { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("cloud cache {} ... {:?} ",reqname,chrono::Utc::now()); - } - let config = reqenv.config(); - let lock_path = format!("{}/{}_{}.{}",&config.cache_lock_path,&reqname,&config.cache_items.replace(",","_"),&config.cache_lock_ext); - let output_path = format!("{}/{}_{}.json",&config.cache_path,&reqname,&config.cache_items.replace(",","_")); - if Path::new(&lock_path).exists() { - if envmnt::get_or(format!("LAST_CACHE_{}",&output_path),"") != "" { - if debug > 0 { - println!("Lock found {} ",&lock_path); - } - // return Err(anyhow!("Lock found {} ",&lock_path)); - return Ok(()) - } else { - println!("Not LAST_CACHE environment found for lock: {} ",&lock_path); - } - } - // println!("Lock NOT found {} ",&lock_path); - let now = chrono::Utc::now().timestamp(); - envmnt::set(format!("LAST_CACHE_{}",&output_path), format!("{}",&now)); - let result = on_cloud_req(&reqname_job,&cloud,&reqenv,&config.cache_items,"","*").await; - 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(result.as_bytes())?; - if debug > 0 { - println!("{}: [cloud config] -> {}\n",&now,&output_path); - } - Ok(()) -} -pub async fn make_cloud_cache(reqenv: &ReqEnv,cloud: &Cloud) -> Result<()> { - if envmnt::get_isize("DEBUG",0) > 0 { - println!("Making cloud cache {:?} ... ",chrono::Utc::now()); - } - get_cloud_cache_req(reqenv, cloud, "config", "config_job").await.unwrap_or_else(|e| println!("Error cache config: {}",e)); - Ok(()) -} -pub async fn run_clouds_check(reqenv: &ReqEnv,cloud: &Cloud) -> Result<()> { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("cloud check ... {:?} ",chrono::Utc::now()); - } - let config = reqenv.config(); - let output_path = format!("{}/clouds.json",&config.check_path); - let now = chrono::Utc::now().timestamp(); - envmnt::set(format!("LAST_CHECK{}",&output_path), format!("{}",&now)); - let result = on_cloud_req("check_job",&cloud,&reqenv,"monitor,liveness","","*").await; - // println!("{}",&output_path); - 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(result.as_bytes())?; - if debug > 0 { - println!("{}: [cloud check] -> {}\n",&now,&output_path); - } - Ok(()) -} -// pub async fn get_cloud_check(reqenv: &ReqEnv,cloud: &Cloud, reqname: &str, reqname_job: &str) -> Result<()> { -pub async fn get_cloud_check(reqenv: &ReqEnv) -> String { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("cloud check ... {:?} ",chrono::Utc::now()); - } - let config = reqenv.config(); - let output_path = format!("{}/clouds.json",&config.check_path); - let output_data = fs::read_to_string(&output_path).with_context(|| format!("Failed to read json check 'outut_path' from {}", &output_path)) - .unwrap_or_else(|e| { - eprintln!("read file {}: {}",&output_path,e); - String::from("") - }); - output_data - // let result = on_cloud_req(&reqname_job,&cloud,&reqenv,&config.cache_items,"","*").await; - // 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(result.as_bytes())?; - // if debug > 0 { - // println!("{}: [cloud check] -> {}\n",&now,&output_path); - // } - // Ok(()) -} -pub async fn run_apps_check(reqenv: &ReqEnv,cloud: &Cloud) -> Result<()> { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("apps check ... {:?} ",chrono::Utc::now()); - } - let config = reqenv.config(); - let output_path = format!("{}/apps.json",&config.check_path); - let now = chrono::Utc::now().timestamp(); - envmnt::set(format!("LAST_APPS_CHECK{}",&output_path), format!("{}",&now)); - let result = on_cloud_req("apps_check_job",&cloud,&reqenv,"apps","","*").await; - // println!("{}",&output_path); - 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(result.as_bytes())?; - if debug > 0 { - println!("{}: [apps check] -> {}\n",&now,&output_path); - } - Ok(()) -} -pub async fn get_apps_check(reqenv: &ReqEnv) -> String { - let debug = envmnt::get_isize("DEBUG",0); - if debug > 0 { - println!("apps check ... {:?} ",chrono::Utc::now()); - } - let config = reqenv.config(); - let output_path = format!("{}/apps.json",&config.check_path); - let output_data = fs::read_to_string(&output_path).with_context(|| format!("Failed to read json apps check 'outut_path' from {}", &output_path)) - .unwrap_or_else(|e| { - eprintln!("read file {}: {}",&output_path,e); - String::from("") - }); - output_data -} \ No newline at end of file diff --git a/src/clouds/on_req.rs b/src/clouds/on_req.rs new file mode 100644 index 0000000..e02d4e3 --- /dev/null +++ b/src/clouds/on_req.rs @@ -0,0 +1,283 @@ +use anyhow::{Result,Context}; +use std::{fs}; //,io}; +use std::fs::OpenOptions; +use std::io::{Write}; +use std::path::Path; +use std::str; +// use std::ffi::OsString; + +use reqenv::ReqEnv; +use crate::clouds::defs::{ + Cloud, + Provider, + MainResourcesConfig, + HostInfo, +}; +use crate::clouds::defs::{SrvcsHostInfo}; +use crate::clouds::upcloud::{run_on_upcloud_info,run_on_upcloud_check}; +use crate::liveness::{ create_cloud_check }; +use crate::clouds::utils::{ + load_cloud_env, + load_cloud_name_config, + get_cloud_home_list, + create_cloud_config, + cloud_info_json, + write_cache_file, +}; + +pub async fn run_on_provider_info( + reqname: &str, reqenv: &ReqEnv, req_tsksrvc: &str, req_srvrs: &str, + provider: Provider, source: &str, cfg_data: String, + cld_name: &str, + env_cloud: &Cloud +) -> Vec { + match provider.name.as_str() { + "upcloud" => { + // TODO clean SSH keys or encrypt content + // dbg!(&cloud_config); + run_on_upcloud_info(reqname,&reqenv,req_tsksrvc,req_srvrs, source, cfg_data, &cld_name,env_cloud).await + }, + _ => { + let result = format!("Errors on {} provider {} not found",&source,&provider.name); + if envmnt::get_isize("DEBUG",0) > 1 { + println!("{}",&result); + } + Vec::new() + } + } +} +pub async fn run_on_provider_check( + reqname: &str, req_tsksrvc: &str, req_srvrs: &str, + provider: Provider, source: &str, cfg_data: String, + env_cloud: &Cloud +) -> Vec { + match provider.name.as_str() { + "upcloud" => { + // TODO clean SSH keys or encrypt content + // dbg!(&cloud_config); + run_on_upcloud_check(reqname,req_tsksrvc,req_srvrs, source, cfg_data, env_cloud).await + }, + _ => { + if envmnt::get_isize("DEBUG",0) > 1 { + println!("Errors on {} provider {} not found",&source,&provider.name); + } + Vec::new() + } + } +} +pub async fn on_cloud_name_req_info( + reqname: &str,env_cloud: &Cloud,reqenv: &ReqEnv, + req_tsksrvc: &str, req_srvrs: &str, cld_name: &str, source: &str +) -> Vec { + let mut cloud = env_cloud.to_owned(); + load_cloud_env(&mut cloud, &source).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_env: {}",e); + }); + let (cfg,provider,cfg_data) = load_cloud_name_config(&mut cloud, &source).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_name_config: {}",e); + (MainResourcesConfig::default(),Provider::default(),String::from("")) + }); + if cfg.mainName.is_empty() || cfg_data.is_empty() { + let result = format!("Errors loading {}",&source); + if envmnt::get_isize("DEBUG",0) > 1 { + println!("{}",&result); + } + return Vec::new(); + } + run_on_provider_info(&reqname,&reqenv,&req_tsksrvc,&req_srvrs,provider,&source,cfg_data,&cld_name,&cloud).await +} +pub async fn on_cloud_name_req_check( + reqname: &str,env_cloud: &Cloud,_reqenv: &ReqEnv, + req_tsksrvc: &str, req_srvrs: &str, source: &str +) -> Vec { + let mut cloud = env_cloud.to_owned(); + load_cloud_env(&mut cloud, &source).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_env: {}",e); + }); + let (cfg,provider,cfg_data) = load_cloud_name_config(&mut cloud, &source).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_name_config: {}",e); + (MainResourcesConfig::default(),Provider::default(),String::from("")) + }); + if cfg.mainName.is_empty() || cfg_data.is_empty() { + if envmnt::get_isize("DEBUG",0) > 1 { + println!("Errors loading {}",&source); + } + return Vec::new(); + } + run_on_provider_check(&reqname,&req_tsksrvc,&req_srvrs,provider,&source,cfg_data,&cloud).await +} + +pub async fn on_cloud_req( + reqname: &str,env_cloud: &Cloud,reqenv: &ReqEnv, + req_tsksrvcs: &str,_req_srvrs: &str, source: &str +) -> String { + let config = reqenv.config(); + let debug = envmnt::get_isize("DEBUG",0); + // let lock_path = format!("{}/{}_{}{}",&config.cache_lock_path,&reqname,&req_tsksrvcs.replace(",","_"),&config.cache_lock_ext); + // if Path::new(&lock_path).exists() || reqname.ends_with("_job") { + let output_path = format!("{}/{}_{}.json",&config.cache_path,&reqname,&req_tsksrvcs.replace(",","_")); + if ! reqname.ends_with("_job") { + println!("{}",&output_path); + if Path::new(&output_path).exists() { + if debug > 0 { + println!("Using cache: {} at {}",&output_path,envmnt::get_or(format!("LAST_CACHE_{}",&output_path), "")); + } + return fs::read_to_string(&output_path).with_context(|| format!("Failed to read json cache 'outut_path' from {}", &output_path)) + .unwrap_or_else(|e| { + eprintln!("read file {}: {}",&output_path,e); + String::from("") + }).to_owned(); + } + } + let lock_path = format!("{}/{}_{}.{}",&config.cache_lock_path,&reqname,&config.cache_items.replace(",","_"),&config.cache_lock_ext); + if Path::new(&lock_path).exists() { + if envmnt::get_or(format!("LAST_CACHE_{}",&output_path),"") != "" { + if debug > 0 { + println!("{}: lock found {} ",&reqname,&lock_path); + } + // return Err(anyhow!("Lock found {} ",&lock_path)); + return fs::read_to_string(&output_path).with_context(|| format!("Failed to read json cache 'outut_path' from {}", &output_path)) + .unwrap_or_else(|e| { + eprintln!("read file {}: {}",&output_path,e); + String::from("") + }).to_owned(); + // } else { + // println!("Not LAST_CACHE environment found for lock: {} ",&lock_path); + } + } + let mut cloud = env_cloud.to_owned(); + load_cloud_env(&mut cloud, &source).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_env {}",e); + }); + let entries: Vec; + if source == "*" { + entries = get_cloud_home_list(&cloud).await + .unwrap_or_else(|e| { + eprintln!("get_cloud_home_list: {}",e); + Vec::new() + }); + } else { + entries = vec!(source.to_string()); + } + match reqname { + "liveness_job" | "apps_job" | "status_job" => { + serde_json::to_string( + &create_cloud_check(req_tsksrvcs,reqenv,entries,cloud).await + ).unwrap_or_else(|_| String::from("")).replace("\n","") + } + _ => { + let result = cloud_info_json(create_cloud_config(reqname,req_tsksrvcs,reqenv,entries,cloud.to_owned()).await); + if !result.is_empty() { + let _ = write_cache_file(&output_path.replace("_job",""),&result).unwrap_or_else(|e|{println!("Error writing {}:{}",&output_path,e)}); + } + result + } + } +} +// ) -> BTreeMap { +// let mut output_data = BTreeMap::new(); +// let mut req_tsksrvcs_items = req_tsksrvcs.split(','); +// for it in req_tsksrvcs_items.by_ref() { +// }; +// } +pub async fn get_cloud_cache_req(reqenv: &ReqEnv,cloud: &Cloud, reqname: &str, reqname_job: &str) -> Result<()> { + let debug = envmnt::get_isize("DEBUG",0); + if debug > 0 { + println!("cloud cache {} ... {:?} ",reqname,chrono::Utc::now()); + } + let config = reqenv.config(); + let lock_path = format!("{}/{}_{}.{}",&config.cache_lock_path,&reqname,&config.cache_items.replace(",","_"),&config.cache_lock_ext); + let output_path = format!("{}/{}_{}.json",&config.cache_path,&reqname,&config.cache_items.replace(",","_")); + if Path::new(&lock_path).exists() { + if envmnt::get_or(format!("LAST_CACHE_{}",&output_path),"") != "" { + if debug > 0 { + println!("Lock found {} ",&lock_path); + } + // return Err(anyhow!("Lock found {} ",&lock_path)); + return Ok(()) + } else { + println!("Not LAST_CACHE environment found for lock: {} ",&lock_path); + } + } + let now = chrono::Utc::now().timestamp(); + envmnt::set(format!("LAST_CACHE_{}",&output_path), format!("{}",&now)); + let result = on_cloud_req(&reqname_job,&cloud,&reqenv,&config.cache_items,"","*").await; + 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(result.as_bytes())?; + if debug > 0 { + println!("{}: [cloud config] -> {}\n",&now,&output_path); + } + Ok(()) +} +pub async fn make_cloud_cache(reqenv: &ReqEnv,cloud: &Cloud) -> Result<()> { + if envmnt::get_isize("DEBUG",0) > 0 { + println!("Making cloud cache {:?} ... ",chrono::Utc::now()); + } + get_cloud_cache_req(reqenv, cloud, "config", "config_job").await.unwrap_or_else(|e| println!("Error cache config: {}",e)); + Ok(()) +} +pub async fn run_cache_data( + task: &str,reqenv: &ReqEnv,cloud: &Cloud, + req_tsksrvcs: &str, source: &str +) -> Result<()> { + // let out_data = get_cache_data(task,reqenv); + // if out_data.is_empty() { + let result = on_cloud_req( + format!("{}_job",&task).as_str(),&cloud,&reqenv, + task,req_tsksrvcs,source + ).await; + write_cache_data(task,&result,reqenv,cloud).await +} +pub fn get_cache_info(task: &str,reqenv: &ReqEnv,debug: isize) -> String { + if debug > 0 { + println!("{} cache ... {:?} ",&task,chrono::Utc::now()); + } + let config = reqenv.config(); + match task { + "liveness" | "apps" | "status" => format!("{}/{}.json",&config.check_path,&task), + _ => format!("{}/{}.json",&config.check_path,&task), + } +} +pub async fn get_cache_data(task: &str,reqenv: &ReqEnv) -> String { + let debug = envmnt::get_isize("DEBUG",0); + let output_path = get_cache_info(task,reqenv,debug); + let output_data = fs::read_to_string(&output_path).with_context(|| format!("Failed to read json {} 'outut_path' from {}",&task,&output_path)) + .unwrap_or_else(|e| { + eprintln!("read file {}: {}",&output_path,e); + String::from("") + }); + if debug > 0 && !output_data.is_empty() { + println!("{}: [{} cache] -> {}",chrono::Utc::now(),&task,&output_path); + } + output_data +} +pub async fn write_cache_data(task: &str,result: &str,reqenv: &ReqEnv,_cloud: &Cloud) -> Result<()> { + let debug = envmnt::get_isize("DEBUG",0); + let output_path = get_cache_info(task,reqenv,debug); + envmnt::set(format!("LAST_{}_CACHE{}",&task,&output_path), format!("{}",chrono::Utc::now())); + //let result_map = on_cloud_req(format!("{}_job",&task).as_str(),&cloud,&reqenv,task,"","*").await; + //if let Some(result) = result_map.get(task) { + // let result = on_cloud_req(format!("{}_job",&task).as_str(),&cloud,&reqenv,task,"","*").await; + if !result.is_empty() { + // println!("{}",&output_path); + 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(result.as_bytes())?; + } else { + println!("{}: get data to write error",&task); + } + if debug > 0 { + println!("{}: [{} cache] -> {}\n",chrono::Utc::now(),&task,&output_path); + } + Ok(()) +} \ No newline at end of file diff --git a/src/clouds/upcloud.rs b/src/clouds/upcloud.rs index 5d284c2..7f5c3b6 100644 --- a/src/clouds/upcloud.rs +++ b/src/clouds/upcloud.rs @@ -3,11 +3,18 @@ use std::str; use std::process::{Command}; use std::path::Path; +use reqenv::ReqEnv; use crate::clouds::defs::{Cloud}; use crate::providers::defs::upcloud::{ResourcesConfig,ConfigResources}; use crate::providers::upcloud::{parse_resources_cfg,make_config_resources}; use crate::clouds::defs::{SrvcsHostInfo,HostInfo}; -use crate::clouds::on_clouds::{parse_srvr_tsksrvcs,liveness_srvr_tsksrvcs,liveness_srvr_appsrvcs,parse_srvr_appsrvcs}; +use crate::clouds::utils::{write_cache_file}; +use crate::liveness::{ + parse_srvr_tsksrvcs, + liveness_srvr_tsksrvcs, + liveness_srvr_appsrvcs, + parse_srvr_appsrvcs +}; pub async fn load_upcloud_config(cfg_data: String) -> Result { let mut res_cfg: ResourcesConfig = serde_yaml::from_str(&cfg_data)?; @@ -64,46 +71,64 @@ pub async fn get_upcloud_info(hostname: &str, cmd: &str,cfg_path: &str) -> Stri _ => { String::from("")} } } -pub async fn run_on_upcloud_info(reqname: &str,req_tsksrvc: &str, req_srvrs: &str, source: &str, cfg_data: String, env_cloud: &Cloud) -> String { +pub async fn run_on_upcloud_info( + reqname: &str,reqenv: &ReqEnv,req_tsksrvc: &str, + req_srvrs: &str, source: &str, cfg_data: String, + cld_name: &str,env_cloud: &Cloud +) -> Vec { + let config = &reqenv.config(); let cloud_config = load_upcloud_config(cfg_data).await .unwrap_or_else(|e| { eprintln!("load_upcloud_config: {}",e); ConfigResources::default() }); + let cfg_path= format!("{}/{}/config.yaml",&env_cloud.env.home,&source); + let mut hosts_info: Vec = Vec::new(); match reqname { "config" => { - serde_json::to_string(&cloud_config) - .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) + let config_cache_path = format!("{}/{}/{}_config.json",&config.cache_path,&cld_name,&cloud_config.mainName); + let config_info = get_upcloud_info(&cloud_config.servers[0].hostname,&req_tsksrvc,&cfg_path).await; + write_cache_file(&config_cache_path,&config_info).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&cld_name,&config_cache_path,e)); + hosts_info.push(HostInfo { + hostname: "config".to_owned(), + info: config_cache_path, + }); + // serde_json::to_string(&cloud_config) + // .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) }, "provision" => { - let mut hosts_info: Vec = Vec::new(); - let cfg_path= format!("{}/{}/config.yaml",&env_cloud.env.home,&source); match req_tsksrvc { "floatip" => { + let floatip_cache_path = format!("{}/{}/{}_floatip.json",&config.cache_path,&cld_name,&cloud_config.mainName); + let floatip_info = get_upcloud_info(&cloud_config.servers[0].hostname,&req_tsksrvc,&cfg_path).await; + write_cache_file(&floatip_cache_path,&floatip_info).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&cld_name,&floatip_cache_path,e)); hosts_info.push(HostInfo { hostname: "floatip".to_owned(), - info: get_upcloud_info(&cloud_config.servers[0].hostname,&req_tsksrvc,&cfg_path).await, + info: floatip_cache_path, }); }, _ => { for srvr in cloud_config.servers.iter() { if req_srvrs == "" || req_srvrs.contains(&srvr.hostname) { + let srvr_cache_path = format!("{}/{}/{}_{}.json",&config.cache_path,&cld_name,&cloud_config.mainName,&srvr.hostname); + let srvr_info = get_upcloud_info(&srvr.hostname,"server",&cfg_path).await; + write_cache_file(&srvr_cache_path,&srvr_info).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&cld_name,&srvr_cache_path,e)); hosts_info.push(HostInfo { hostname: srvr.hostname.to_owned(), - info: get_upcloud_info(&srvr.hostname,"server",&cfg_path).await, + info: srvr_cache_path, }); } }; } }; - serde_json::to_string(&hosts_info) - .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) }, _ => { - serde_json::to_string(&cloud_config) - .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) + println!("Rename {} for {} undefined",&reqname,&cld_name); }, } + // serde_json::to_string(&hosts_info) + // .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) + hosts_info } pub async fn run_on_upcloud_check(reqname: &str,req_tsksrvc: &str, req_srvrs: &str, source: &str, cfg_data: String, _env_cloud: &Cloud) -> Vec { @@ -112,82 +137,67 @@ pub async fn run_on_upcloud_check(reqname: &str,req_tsksrvc: &str, req_srvrs: &s eprintln!("load_upcloud_config: {}",e); ConfigResources::default() }); - match reqname { - "status" => { - match req_tsksrvc { - _ => { - let mut srvcs_hosts_info: Vec = Vec::new(); - for srvr in cloud_config.servers.iter() { - if req_srvrs == "" || req_srvrs.contains(&srvr.hostname) { - // let srvr=&cloud_config.servers[0]; - // serde_json::to_string(&cloud_config).unwrap_or_else(|_| String::from("")) - // let str_host = format!("- hostname: {}\n tsksrvcs:\n",&srvr.hostname); - // res.push_str(&str_host); - // TODO check if is alive - // res.push_str( + match req_tsksrvc { + _ => { + let mut srvcs_hosts_info: Vec = Vec::new(); + for srvr in cloud_config.servers.iter() { + if req_srvrs == "" || req_srvrs.contains(&srvr.hostname) { + // let srvr=&cloud_config.servers[0]; + // serde_json::to_string(&cloud_config).unwrap_or_else(|_| String::from("")) + // let str_host = format!("- hostname: {}\n tsksrvcs:\n",&srvr.hostname); + // res.push_str(&str_host); + // TODO check if is alive + // res.push_str( + match reqname { + "status" => { srvcs_hosts_info.push(SrvcsHostInfo { hostname: srvr.hostname.to_owned(), - tsksrvcs: parse_srvr_tsksrvcs(&srvr.hostname, &srvr.sshAccess, &srvr.tsksrvcs,&req_tsksrvc).await, - appsrvcs: parse_srvr_appsrvcs(&srvr.hostname, &srvr.sshAccess, &srvr.apps,&req_tsksrvc).await, - }); - // ); - // res.push('\n'); - } - }; - // serde_json::to_string(&cloud_config).unwrap_or_else(|_| String::from("")) - // res.to_owned() - // println!("{}",&res); - // let tsks_yaml: Vec = serde_yaml::from_str(&res) - // .unwrap_or_else(|e| { eprintln!("{}",e); Vec::new() }); - - // serde_json::to_string(&srvcs_hosts_info) - // .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) - srvcs_hosts_info - } - } - } - "liveness" => { - match req_tsksrvc { - _ => { - let mut srvcs_hosts_info: Vec = Vec::new(); - for srvr in cloud_config.servers.iter() { - if req_srvrs == "" || req_srvrs.contains(&srvr.hostname) { + tsksrvcs: liveness_srvr_tsksrvcs(&source,&cloud_config.cntrllrs,&srvr.tsksrvcs,&req_tsksrvc).await, + appsrvcs: liveness_srvr_appsrvcs(&source,&cloud_config.cntrllrs,&srvr.apps,&req_tsksrvc).await, + }); + }, + "liveness" => { srvcs_hosts_info.push(SrvcsHostInfo { hostname: srvr.hostname.to_owned(), tsksrvcs: liveness_srvr_tsksrvcs(&source,&cloud_config.cntrllrs,&srvr.tsksrvcs,&req_tsksrvc).await, appsrvcs: Vec::new(), }); - } - }; - // serde_json::to_string(&srvcs_hosts_info) - // .unwrap_or_else(|e| { eprintln!("serde liveness: {}",e); String::from("")}) - srvcs_hosts_info - } - } - }, - "apps" => { - match req_tsksrvc { - _ => { - let mut srvcs_hosts_info: Vec = Vec::new(); - for srvr in cloud_config.servers.iter() { - if req_srvrs == "" || req_srvrs.contains(&srvr.hostname) { + }, + "apps" => { srvcs_hosts_info.push(SrvcsHostInfo { hostname: srvr.hostname.to_owned(), tsksrvcs: Vec::new(), appsrvcs: liveness_srvr_appsrvcs(&source,&cloud_config.cntrllrs,&srvr.apps,&req_tsksrvc).await, }); - } - }; - // serde_json::to_string(&srvcs_hosts_info) - // .unwrap_or_else(|e| { eprintln!("serde liveness: {}",e); String::from("")}) - srvcs_hosts_info + }, + _ => { + srvcs_hosts_info.push(SrvcsHostInfo { + hostname: srvr.hostname.to_owned(), + tsksrvcs: parse_srvr_tsksrvcs(&srvr.hostname, &srvr.sshAccess, &srvr.tsksrvcs,&req_tsksrvc).await, + appsrvcs: parse_srvr_appsrvcs(&srvr.hostname, &srvr.sshAccess, &srvr.apps,&req_tsksrvc).await, + // tsksrvcs: Vec::new(), + // appsrvcs: Vec::new(), + }); + }, + } + // ); + // res.push('\n'); } - } - }, - _ => { - // serde_json::to_string(&cloud_config) + }; + // serde_json::to_string(&cloud_config).unwrap_or_else(|_| String::from("")) + // res.to_owned() + // println!("{}",&res); + // let tsks_yaml: Vec = serde_yaml::from_str(&res) + // .unwrap_or_else(|e| { eprintln!("{}",e); Vec::new() }); + + // serde_json::to_string(&srvcs_hosts_info) // .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) - Vec::new() + srvcs_hosts_info }, + // _ => { + // // serde_json::to_string(&cloud_config) + // // .unwrap_or_else(|e| { eprintln!("{}",e); String::from("")}) + // Vec::new() + // }, } } diff --git a/src/clouds/utils.rs b/src/clouds/utils.rs new file mode 100644 index 0000000..d5f30a9 --- /dev/null +++ b/src/clouds/utils.rs @@ -0,0 +1,485 @@ +use anyhow::{anyhow,Result,Context, Error}; +use std::{fs}; //,io}; +use std::path::Path; +use std::fs::OpenOptions; +use std::io::{Write}; +use rfm::mkdir; +use std::str; +use std::process::{Command}; +// use std::ffi::OsString; +use crate::clouds::defs::{ + CloudEnv, + Cloud, + Provider, + SrvcsHostInfo, + HostInfo, + MainResourcesConfig, +}; +use reqenv::ReqEnv; +use crate::defs::{ + KloudHome, + KloudCheckHome, + CloudGroup, + CloudItem, + SSHAccess, +}; +use crate::clouds::upcloud::{get_upcloud_info}; +use crate::monitor::get_cloud_monitor_info; +use crate::clouds::on_req::{ + on_cloud_name_req_check, + on_cloud_name_req_info, +}; + +/// On_cloud +/// load __`item`__ form `envmnt` with `dflt` +/// Check if path from `source` exits for `item` or fallback to check from `root` path. +/// __`item`__ path is requiered to exist +pub async fn get_env_path(item: &str, dflt: &str, source: &str, root: &str, is_tpl: bool) -> Result { + let mut base = dflt.to_owned(); + if item.len() > 0 { + base=envmnt::get_or(item,dflt); + } + if Path::new(&base).has_root() { + if Path::new(&base).exists() { + return Ok(base); + } else { + return Err(anyhow!("Path {} not found", &base)); + } + } + #[allow(unused_assignments)] + let mut item_path= String::from(""); + if source.len() > 0 { + item_path = format!("{}/{}",&source,&base); + } else { + item_path = format!("{}",&base); + } + if ! Path::new(&item_path).exists() { + item_path=format!("{}/{}",root,&base); + if is_tpl && ! Path::new(&item_path).exists() { + item_path=format!("{}/{}",&source,&base); + if ! Path::new(&item_path).exists() { + item_path=format!("{}/{}",&root,&base); + } + } + if ! Path::new(&item_path).exists() { + return Err(anyhow!("Path '{}' not found in {} - {}", &base,&source,&root)); + } + } + // println!("Path: {} src {} root {} item {}", &base, &source, &root, item_path); + Ok(base) +} +/// env_cloud +/// Scanning environment from __CLOUD_PATH__ overloaded with __CLOUD_HOME__ `source` CliOpts argument (-s) in case +/// Load path from `envmnt` and __`get_env_path`___ +/// __`source`__ path is mandatory +pub async fn env_cloud(source: &str, cloud_env: &mut CloudEnv) -> Result<()> { + cloud_env.path = envmnt::get_or("ROOT_KLDS", ""); + if cloud_env.path.is_empty() { + return Err(anyhow!("Clouds Root Path {} not found", &cloud_env.source_path)); + } + cloud_env.home=get_env_path("KLDS_HOME","home", "", &cloud_env.path,false).await?; + cloud_env.monitor_run=envmnt::get_or("KLD_MONITOR_RUN","kloud_mon"); + cloud_env.source=source.to_owned(); + cloud_env.config_root = envmnt::get_or("KLD_CONFIG_ROOT", "config"); + if source == "*" { + cloud_env.config_path = envmnt::get_or("KLD_CONFIG", "config.yaml"); + cloud_env.config_json_path = envmnt::get_or("KLD_CONFIG_JSON", "config.json"); + return Ok(()) + } + let arr_source: Vec<&str> = source.split_terminator("/").collect(); + if let Some(name) = arr_source.get(0) { + cloud_env.cloud= format!("{}",&name); + } + if let Some(group) = arr_source.get(1) { + cloud_env.group = format!("{}",&group); + } + if let Some(target) = arr_source.get(2) { + cloud_env.target = format!("{}",&target); + } + cloud_env.source_path = format!("{}/{}",&cloud_env.home,&source); + if ! Path::new(&cloud_env.source_path).exists() { + return Err(anyhow!("Clouds Source Path {} not found", &cloud_env.source_path)); + } + cloud_env.config_path=get_env_path("KLDS_CONFIG","config.yaml", + format!("{}/{}",&cloud_env.source_path,&cloud_env.config_root).as_str(), + &cloud_env.path,false).await?; + cloud_env.config_json_path=get_env_path("KLDS_CONFIG_JSON","config.json", + format!("{}/{}",&cloud_env.source_path,&cloud_env.config_root).as_str(), + &cloud_env.path,false).await?; + if cloud_env.group.is_empty() { + cloud_env.provision=format!("{}/{}/{}/{}",&cloud_env.home,&cloud_env.cloud,envmnt::get_or("CLOUD_PROVISION","provision"),&cloud_env.target); + } else { + cloud_env.provision=format!("{}/{}/{}/{}/{}",&cloud_env.home,&cloud_env.cloud,&cloud_env.group,envmnt::get_or("CLOUD_PROVISION","provision"),&cloud_env.target); + } + if ! Path::new(&cloud_env.provision).exists() { + let dir_path_buf = Path::new(&cloud_env.provision).to_path_buf(); + let dirs: Vec<&std::path::PathBuf> = vec![&dir_path_buf]; + mkdir(&dirs).with_context(|| format!("\nFailed to create dir path {}", &cloud_env.provision))?; + if envmnt::get_isize("DEBUG",0) > 1 { + println!("{} created", &cloud_env.provision); + } + } + cloud_env.wk_path = envmnt::get_or("KLDS_WKDIR", "/tmp"); + cloud_env.tsksrvcs_path=get_env_path("KLDS_TSKSRVCS","tsksrvcs", &cloud_env.source_path,&cloud_env.path,false).await?; + cloud_env.versions=get_env_path("KLDS_VERSIONS","versions.yaml", &cloud_env.source_path,&cloud_env.path,false).await?; + Ok(()) +} +pub async fn clear_specs(source: &str) -> Result<()> { + let env_path = envmnt::get_or("ROOT_KLDS", "clouds"); + let env_home = get_env_path("KLDS_HOME","home", "", &env_path,false).await?; + let env_source=format!("{}/{}",&env_home,&source); + if ! Path::new(&env_source).exists() { + return Ok(()); + } + let env_provision=format!("{}/{}",&env_source,envmnt::get_or("KLDS_PROVISION","provision")); + let env_specs=format!("{}/specs",env_provision); + if Path::new(&env_specs).exists() { + if envmnt::get_isize("DEBUG",0) > 1 { + println!("Delete {}/specs",env_provision); + } + fs::remove_dir_all(&env_specs)?; + } + Ok(()) +} +pub async fn load_config_data(cloud: &Cloud, source: &str) -> Result { + // dbg!(&cloud); + let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&source,&cloud.env.config_root,&cloud.env.config_path); + let debug = envmnt::get_isize("DEBUG",0); + if debug > 1 { + println!("load_config: {}",&cfg_path); + } + let cfg_data = fs::read_to_string(&cfg_path).with_context(|| format!("Failed to read 'cfg_path' from {}", &cfg_path))?; + Ok(cfg_data) +} +pub async fn load_config_json_data(cloud: &Cloud, source: &str) -> Result { + let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&source,&cloud.env.config_root,&cloud.env.config_json_path); + let cfg_data = fs::read_to_string(&cfg_path).with_context(|| format!("Failed to read json 'cfg_path' from {}", &cfg_path))?; + Ok(cfg_data.replace("\n","")) +} +pub async fn load_cloud_env(cloud: &mut Cloud, source: &str) -> Result<()> { + env_cloud(source, &mut cloud.env).await?; + Ok(()) +} +pub async fn load_cloud_config(cloud: &mut Cloud, source: &str) -> Result<(KloudHome,Provider,String), Error> { + // dbg!(&cloud); + let cfg_data = load_config_data(&cloud,source).await?; + let cfg: KloudHome = serde_yaml::from_str(&cfg_data)?; + let cfg_provider = format!("{}",cfg.provider[0]); + let provider = cloud.providers.get(&cfg_provider).with_context(|| + format!("Provider '{}'' not defined", &cfg_provider))?; + Ok((cfg, provider.to_owned(), cfg_data)) +} +pub async fn load_cloud_check_config(cloud: &mut Cloud, source: &str) -> Result<(KloudCheckHome,Provider,String), Error> { + // dbg!(&cloud.env); + let cfg_data = load_config_data(&cloud,source).await?; + let cfg: KloudCheckHome = serde_yaml::from_str(&cfg_data)?; + let cfg_provider = format!("{}",cfg.provider[0]); + let provider = cloud.providers.get(&cfg_provider).with_context(|| format!("Provider '{}'' not defined", &cfg_provider))?; + Ok((cfg, provider.to_owned(), cfg_data)) +} +pub async fn load_cloud_name_config(cloud: &mut Cloud, source: &str) -> Result<(MainResourcesConfig,Provider,String), Error> { + // dbg!(&cloud.env); + let cfg_data = load_config_data(&cloud,source).await?; + let cfg: MainResourcesConfig = serde_yaml::from_str(&cfg_data)?; + let provider = cloud.providers.get(&cfg.provider).with_context(|| format!("Provider '{}'' not defined", &cfg.provider))?; + Ok((cfg, provider.to_owned(), cfg_data)) +} + +pub async fn get_cloud_home_list(cloud: &Cloud) -> Result> { + let kloud_files: Vec = fs::read_dir(&cloud.env.home)? + .filter_map(|res| + match res.map(|e| e.file_name()) { + Ok(entry) => { + // for e.path() + // let file_path = entry.as_path().display().to_string(); + let file_path = format!("{}",entry.to_owned().into_string().unwrap_or_else(|_|String::from(""))); + let cfg_path = format!("{}/{}/{}/{}",&cloud.env.home,&file_path,&cloud.env.config_root,&cloud.env.config_path); + let first_char = file_path.chars().next().unwrap_or_default().to_string(); + if first_char.as_str() != "_" && first_char.as_str() != "." && Path::new(&cfg_path).exists() { + Some(file_path) + } else { + None + } + }, + Err(e) => { + eprintln!("Error filter_map {}",e); + None + } + } + ) + .collect(); + Ok(kloud_files.to_owned()) +} + +pub fn run_ssh_on_srvr(hostname: &str,tsksrvc_name: &str, tsksrvc_cmd: &str, ssh_access: &SSHAccess) -> Result { + let debug = envmnt::get_isize("DEBUG",0); + if debug > 0 { + println!("Checking connection to {}@{} on {} for {} ",ssh_access.user,ssh_access.host,ssh_access.port,&tsksrvc_name); + println!("ssh {} /var/lib/klouds/bin/{}_info.sh yaml",&hostname,&tsksrvc_name); + } + let output = Command::new("ssh") + .arg("-q") + .arg("-o") + .arg("StrictHostKeyChecking=accept-new") + .arg("-p") + .arg(format!("{}",ssh_access.port)) + .arg(format!("{}@{}",ssh_access.user,ssh_access.host)) + .arg("sudo") + .arg(format!("/var/lib/klouds/bin/{}_info.sh",&tsksrvc_name)) + .arg("yaml") + .arg(format!("{}",&tsksrvc_cmd)) +// .arg(format!("ssh {} /var/lib/klouds/bin/{}_info.sh yaml",&hostname,&tsksrvc_name)) + .output()?; + //dbg!(&output); + if !&output.status.success() { + return Err(anyhow!("Connection to '{}' for tsksrvc '{}' failed: {}",&hostname,&tsksrvc_name,&output.status)); + } + let res = str::from_utf8(&output.stdout).unwrap_or_else(|_| ""); + let info: String = serde_yaml::from_str(&res) + .unwrap_or_else(|e| { + eprintln!("serde_yaml: {}",e); + String::from("") + }); + Ok(info.to_owned()) +} + +pub async fn get_provider_info(provider: &str, hostname: &str, cmd: &str , cfg_path: &str) -> String { + match provider { + "upcloud" => { + get_upcloud_info(hostname,cmd,cfg_path).await + }, + _ => String::from("") + } +} +pub fn write_cache_file(output_path: &str,output_data: &str) -> 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(output_data.as_bytes())?; + Ok(()) +} +pub fn read_cache_file(source_path: &str) -> Result { + let cache_data = fs::read_to_string(&source_path).with_context(|| format!("Failed to read cache from {}",&source_path))?; + Ok(cache_data) +} +pub async fn create_cloud_config( + reqname: &str,req_tsksrvcs: &str,reqenv: &ReqEnv, + entries: Vec,mut cloud: Cloud +) -> Vec { + let config = reqenv.config(); + let debug = envmnt::get_isize("DEBUG",0); + let check_path = format!("{}/clouds.json",&config.check_path); + let mut check_entries: Vec = Vec::new(); + let mut no_check_entries = true; + if reqname != "check_job" { + if Path::new(&check_path).exists() { + // Load & Parse reuse liveness and monitor + let check_data = fs::read_to_string(&check_path).unwrap_or_else(|e|{ + eprintln!("Failed to read 'check_path' from {}: {}", &check_path,e); + String::from("") + }); + if !check_data.is_empty() { + check_entries = serde_json::from_str(&check_data).unwrap_or_else(|e| { + eprintln!("Error loading check_entries ({}): {}",&check_path,e); + Vec::new() + }); + no_check_entries=false; + if debug> 0 { + println!("Using check_entries from {}",&check_path); + } + // dbg!("{:#?}",&check_entries); + } + } + } + let mut entries_cfgs: Vec = Vec::new(); + for (idx, entry) in entries.iter().enumerate() { + let (mut cfg,_provider,cfg_data) = load_cloud_config(&mut cloud, &entry).await + .unwrap_or_else(|e| { + eprintln!("Load_cloud_config {}:",e); + (KloudHome::default(),Provider::default(),String::from("")) + }); + if cfg.name.is_empty() || cfg_data.is_empty() { + let result = format!("Errors loading {}",&entry); + if debug > 0 { + println!("{}",&result); + } + continue; + } + let entry_cache_path = format!("{}/{}",&config.cache_path,&entry); + if ! Path::new(&entry_cache_path).exists() { + let dir_path_buf = Path::new(&entry_cache_path).to_path_buf(); + let dirs: Vec<&std::path::PathBuf> = vec![&dir_path_buf]; + mkdir(&dirs).unwrap_or_else(|e| println!("\nFailed to create dir path {}: {}", &entry_cache_path,e)); + } + if req_tsksrvcs.contains("monitor") { + if no_check_entries { + let monitor_cache_path = format!("{}/monitor.json",entry_cache_path); + let monitor_data = get_cloud_monitor_info(&mut cloud, &entry).await + .unwrap_or_else(|e| { + eprintln!("Error {} monitor_info {} -> {}",&entry,&cloud.env.monitor_run,e); + String::from("") + }); + write_cache_file(&monitor_cache_path,&monitor_data).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&entry,&monitor_cache_path,e)); + cfg.monitor_info = Some(monitor_cache_path); + } else if check_entries.len() > 0 && check_entries.len() < idx { + cfg.monitor_info = check_entries[idx].monitor_info.to_owned(); + } + } + if reqname.contains("provision") || req_tsksrvcs.contains("resources") || req_tsksrvcs.contains("resources") || req_tsksrvcs.contains("liveness") || req_tsksrvcs.contains("") { + let mut groups: Vec = Vec::new(); + // cfg.groups = cfg.groups.map(|grp| grp.with_resources(grp.path.to_owned())).collect(); + for (grp_idx,grp) in cfg.groups.iter().enumerate() { + let mut items: Vec = Vec::new(); + for (itm_idx,itm) in grp.items.iter().enumerate() { + let resources: Option; + let liveness: Vec; + let provision: Option>; + if req_tsksrvcs.contains("liveness") { + if no_check_entries { + liveness = on_cloud_name_req_check("liveness",&cloud,&reqenv,"","",&itm.path).await; + } else if check_entries.len() > 0 && check_entries.len() < idx { + liveness = check_entries[idx].groups[grp_idx].items[itm_idx].liveness.to_owned(); + } else { + liveness = Vec::new(); + } + } else { + liveness = itm.liveness.to_owned(); + } + if reqname.contains("provision") || req_tsksrvcs.contains("provision") { + provision = Some(on_cloud_name_req_info("provision",&cloud,&reqenv,"","",&entry,&itm.path).await); + } else { + provision = itm.provision.to_owned(); + } + if req_tsksrvcs.contains("resources") { + let resources_cache_path = format!("{}/{}_{}_resources.json",entry_cache_path,&grp.name,&itm.name); + let resources_data = load_config_json_data(&cloud,&itm.path).await + .unwrap_or_else(|e| { + eprintln!("Error loading resources -> {}",e); + String::from("") + }); + write_cache_file(&resources_cache_path,&resources_data).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&entry,&resources_cache_path,e)); + resources = Some(resources_cache_path); + } else { + resources = itm.resources.to_owned(); + } + items.push(CloudItem { + name: itm.name.to_owned(), + info: itm.info.to_owned(), + path: itm.path.to_owned(), + resources, + liveness, + provision, + graph: itm.graph.to_owned(), + critical: itm.critical.to_owned(), + }); + } + groups.push(CloudGroup { + name: grp.name.to_owned(), + info: grp.info.to_owned(), + path: grp.path.to_owned(), + // TODO check this for group + resources: grp.resources.to_owned(), + liveness: grp.liveness.to_owned(), + items, + graph: grp.graph.to_owned(), + prices: grp.prices.to_owned(), + }); + } + cfg.groups=groups; + } + let config_cache_path = format!("{}/cloud_config.json",&entry_cache_path); + let config_json = serde_json::to_string(&cfg).unwrap_or_else(|e|{ + println!("Error creating config_json:{}",e); + String::from("") + }); + write_cache_file(&config_cache_path,&config_json).unwrap_or_else(|e| println!("Error writing cache {} to {}: {}",&entry,&config_cache_path,e)); + entries_cfgs.push(cfg.to_owned()); + } + entries_cfgs +} +pub fn cloud_info_json(clouds: Vec) -> String { + let res = "#RES#"; + let mon = "#MON#"; + let res_pat = format!("\"{}\"",&res); + let mon_pat = format!("\"{}\"",&mon); + let mut out_clds = String::from(""); + for cld in clouds.iter() { + let mut out_grps = String::from(""); + let mut cld_data = cld.to_owned(); + for (grpidx, grp) in cld.groups.iter().enumerate() { + let mut group_data = grp.to_owned(); + let mut out_items = String::from(""); + for itm in grp.items.iter() { + let resources_data: String; + let mut itm_data = itm.to_owned(); + if let Some(res_data) = &itm.resources { + resources_data = read_cache_file(&res_data).unwrap_or_else(|e|{ + println!("Error reading cache {}: {}",&res_data,e); + String::from("\"\"") + }); + } else { + resources_data = String::from("\"\"").replace("\\\"","\""); + } + itm_data.resources = Some(res.to_owned()); + let mut provision_data = String::from(""); + if let Some(itm_prov) = &itm.provision { + for (idx,prov) in itm_prov.iter().enumerate() { + let prov_data = read_cache_file(&prov.info).unwrap_or_else(|e|{ + println!("Error reading cache {}: {}",&prov.info,e); + String::from("\"\"") + }); + if idx > 0 { + provision_data = format!("{},",&provision_data); + } + let json_data = format!("{}:{}{}{}{}{}",r#"{"hostname""#,r#"""#,&prov.hostname,r#"","data":["#,&prov_data,r#"]}"#); + provision_data = format!("{}{}",&provision_data,&json_data); + } + } else { + provision_data = String::from("\"\"").replace("\\\"","\""); + } + itm_data.provision = Some(Vec::new()); + itm_data.info = String::from(""); + out_items = format!("{}{}",&out_items, serde_json::to_string(&itm_data).unwrap_or_else(|_| String::from("")) + .replace("\\\"","\"") + .replace("\n","") + .replace("\"provision\":[]",&format!("\"provision\":[{}]",&provision_data)) + .replace(&res_pat,&resources_data) + ); + } + if grpidx > 0 { + out_grps = format!("{},",&out_grps); + } + // println!("{}",&out_items); + group_data.items = Vec::new(); + group_data.info = String::from(""); + out_grps = format!("{}{}",&out_grps, serde_json::to_string(&group_data).unwrap_or_else(|_| String::from("")) + .replace("\"items\":[]",&format!("\"items\":[{}]",&out_items)) + ); + } + let mut monitor_data: String; + if let Some(mon_data) = &cld_data.monitor_info { + monitor_data = read_cache_file(&mon_data).unwrap_or_else(|e|{ + println!("Error reading cache {}: {}",&mon_data,e); + String::from("\"\"") + }); + } else { + monitor_data = String::from("\"\""); + } + if monitor_data.is_empty() { + monitor_data = String::from("\"\""); + } + cld_data.monitor_info = Some(format!("{}",&mon.to_owned())); + cld_data.groups = Vec::new(); + if !out_clds.is_empty() { + out_clds = format!("{},",&out_clds); + } + out_clds = format!("{}{}",&out_clds, serde_json::to_string(&cld_data).unwrap_or_else(|_| String::from("")) + .replace(&mon_pat,&format!("{}",&monitor_data)) + .replace("\"groups\":[]",&format!("\"groups\":[{}]",&out_grps)) + .replace("\\\"","\"") + ); + } + out_clds = format!("[{}]",&out_clds).replace("}{","},{"); + out_clds +} \ No newline at end of file diff --git a/src/defs.rs b/src/defs.rs index 4d60efa..40fdb4e 100644 --- a/src/defs.rs +++ b/src/defs.rs @@ -2,8 +2,8 @@ use serde::{Serialize, Deserialize, Deserializer}; use std::fmt; use app_env::appenv::{Policy,Rol}; -use crate::clouds::on_clouds::load_config_data; -use crate::clouds::defs::{Cloud,SrvcsHostInfo}; +use crate::clouds::utils::load_config_data; +use crate::clouds::defs::{Cloud,SrvcsHostInfo,HostInfo}; #[allow(non_snake_case)] #[derive(Clone, Debug, Serialize, Deserialize, Default)] @@ -556,7 +556,7 @@ pub struct CloudItem { pub resources: Option, #[serde(default = "default_liveness")] pub liveness: Vec, - pub provision: Option, + pub provision: Option>, pub critical: IsCritical, pub graph: GraphNode, } @@ -568,7 +568,7 @@ impl Default for CloudItem { info: String::from(""), resources: Some(String::from("")), liveness: Vec::new(), - provision: Some(String::from("")), + provision: Some(Vec::new()), critical: IsCritical::no, graph: GraphNode::default(), } @@ -609,7 +609,6 @@ pub struct CloudGroup { pub resources: Option, #[serde(default = "default_liveness")] pub liveness: Vec, - pub provision: Option, pub items: Vec, pub graph: GraphNode, pub prices: Vec, @@ -622,7 +621,6 @@ impl Default for CloudGroup { info: String::from(""), resources: Some(String::from("")), liveness: Vec::new(), - provision: Some(String::from("")), items: Vec::new(), graph: GraphNode::default(), prices: Vec::new(), @@ -648,7 +646,6 @@ impl CloudGroup { info: self.info.to_owned(), resources: Some(self.load_resources(cloud).await.unwrap_or_else(|_|String::from(""))), liveness: self.liveness.to_owned(), - provision: self.provision.to_owned(), items: self.items.to_owned(), graph: self.graph.to_owned(), prices: self.prices.to_owned(), @@ -685,6 +682,7 @@ impl Default for KloudHome { } } } + #[allow(non_snake_case)] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CloudCheckItem { diff --git a/src/lib.rs b/src/lib.rs index 550cdaf..5c9c632 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ pub mod cmds; pub mod utils; pub mod monitor; pub mod status; +pub mod liveness; diff --git a/src/liveness.rs b/src/liveness.rs new file mode 100644 index 0000000..9ee925d --- /dev/null +++ b/src/liveness.rs @@ -0,0 +1,269 @@ +use std::str; +use crate::utils::{liveness_check}; +use crate::clouds::defs::{ + Cloud, + Provider, + TskSrvc, + App, +}; +use reqenv::ReqEnv; +use crate::defs::{ + IsCritical, + KloudCheckHome, + SSHAccess, + Cntrllr, + CloudCheckGroup, + CloudCheckItem +}; +use crate::clouds::defs::{TsksrvcInfo,AppsrvcInfo,SrvcsHostInfo}; + +use crate::clouds::utils::{ + run_ssh_on_srvr, + load_cloud_check_config, +}; +use crate::clouds::on_req::{ + on_cloud_name_req_check, +}; + +use crate::monitor::get_cloud_monitor_info; + +pub async fn parse_srvr_tsksrvcs(hostname: &str, sshaccess: &SSHAccess, tsksrvcs: &Vec,req_tsksrvcs: &str) -> Vec { + let mut tsksrvcs_info: Vec = Vec::new(); + for tsk in req_tsksrvcs.split(",") { + match format!("{}",&tsk).as_str() { + "os" => { + let os_name = String::from("os"); + tsksrvcs_info.push( TsksrvcInfo { + name: format!("{}",&os_name), + info: run_ssh_on_srvr(&hostname, &os_name, "", &sshaccess) + .unwrap_or_else(|e| { + eprintln!("run_ssh_on_srvr os: {}",e); + String::from("") + }), + srvc: TskSrvc::default(), + }); + }, + "floatip" => { + let floatip_name = String::from("floatip"); + tsksrvcs_info.push( TsksrvcInfo { + name: format!("{}",&floatip_name), + info: run_ssh_on_srvr(&hostname, &floatip_name, "", &sshaccess) + .unwrap_or_else(|e| { + eprintln!("run_ssh_on_srvr floatip: {}",e); + String::from("") + }), + srvc: TskSrvc::default(), + }); + }, + "kubernetes_pods" => { + let k8_name = String::from("kubernetes"); + tsksrvcs_info.push(TsksrvcInfo { + name: format!("{}_pods",&k8_name), + info: run_ssh_on_srvr(&hostname, &k8_name, "pods", &sshaccess) + .unwrap_or_else(|e| { + eprintln!("run_ssh_on_srvr kubernetes_pods: {}",e); + String::from("") + }), + srvc: TskSrvc::default(), + }); + }, + _ => { continue; } + }; + } + for tsksrvc in tsksrvcs.iter() { + match format!("{}",&tsksrvc.name).as_str() { + "pause" => continue, + "scale" => continue, + "systemfix" => continue, + "os" => continue, + _ => { + let name = format!("{}",&tsksrvc.name); + if req_tsksrvcs == "all" || req_tsksrvcs.contains(&name) { + // TODO ssh &srv.hostname to get &name in "yaml" + //println!("{} {} {}",&hostname,sshaccess.user,&tksrvc.name); + tsksrvcs_info.push(TsksrvcInfo { + name: format!("{}",&tsksrvc.name), + info: run_ssh_on_srvr(&hostname, &name, "", &sshaccess) + .unwrap_or_else(|e| { + eprintln!("run_ssh_on_srvr for {}: {}",&tsksrvc.name,e); + String::from("") + }), + srvc: tsksrvc.to_owned(), + }); + } + } + }; + } + tsksrvcs_info.to_owned() +} +pub async fn liveness_srvr_tsksrvcs(source: &str, cntrllrs: &Vec, tsksrvcs: &Vec, req_tsksrvc: &str) -> Vec { + let mut tsksrvcs_info: Vec = Vec::new(); + let debug=envmnt::get_isize("DEBUG",0); + for tsksrvc in tsksrvcs.iter() { + match format!("{}",&tsksrvc.name).as_str() { + "pause" => continue, + "scale" => continue, + "systemfix" => continue, + _ => { + let name = format!("{}",&tsksrvc.name); + if tsksrvc.liveness.is_empty() || (req_tsksrvc != "" && !req_tsksrvc.contains(&name)) { + continue; + } + let serverstring = format!("{}",&tsksrvc.liveness); + if debug > 2 { + println!("livenes: {} -> {}",&tsksrvc.name,&serverstring); + } + let res_info = match liveness_check(&source,&cntrllrs,&serverstring,"",&name).await { + Ok(_) => "ok", + Err(e) => { + if tsksrvc.critical == IsCritical::yes { + let monitor_name = "WUJI_MONITOR"; + println!("{} critical livenes: {} -> {}",envmnt::get_or(&monitor_name,""),&tsksrvc.name,&serverstring); + } + if debug > 0 { + eprint!("liveness_check error: {}",e); + } + "err" + }, + }; + // println!("{} info: {}",&tsksrvc.name,&res_info); + tsksrvcs_info.push(TsksrvcInfo { + name: format!("{}",&tsksrvc.name), + info: serde_yaml::from_str(res_info).unwrap_or_else(|e| { + eprintln!("Serde liveness Error: {} {} -> {}",&source,&serverstring,e); + String::from("") + }), + srvc: tsksrvc.to_owned(), + }); + }, + } + } + tsksrvcs_info.to_owned() +} +pub async fn parse_srvr_appsrvcs(hostname: &str, sshaccess: &SSHAccess, appsrvcs: &Vec,req_tsksrvcs: &str) -> Vec { + let mut appsrvcs_info: Vec = Vec::new(); + for appsrvc in appsrvcs.iter() { + let name = format!("{}",&appsrvc.name); + if req_tsksrvcs == "all" || req_tsksrvcs.contains(&name) { + // TODO ssh &srv.hostname to get &name in "yaml" + //println!("{} {} {}",&hostname,sshaccess.user,&tksrvc.name); + appsrvcs_info.push(AppsrvcInfo { + name: format!("{}",&appsrvc.name), + info: run_ssh_on_srvr(&hostname, &name, "", &sshaccess) + .unwrap_or_else(|e| { + eprintln!("run_ssh_on_srvr for {}: {}",&appsrvc.name,e); + String::from("") + }), + srvc: appsrvc.to_owned(), + }); + } + } + appsrvcs_info.to_owned() +} +pub async fn liveness_srvr_appsrvcs(source: &str, cntrllrs: &Vec, appsrvcs: &Vec, req_tsksrvc: &str) -> Vec { + let mut appsrvcs_info: Vec = Vec::new(); + let debug=envmnt::get_isize("DEBUG",0); + for appsrvc in appsrvcs.iter() { + // match format!("{}",&appsrvc.name).as_str() { + // "pause" => continue, + // "scale" => continue, + // "systemfix" => continue, + // _ => { + let name = format!("{}",&appsrvc.name); + if appsrvc.liveness.is_empty() || (req_tsksrvc != "" && !req_tsksrvc.contains(&name)) { + continue; + } + let serverstring = format!("{}",&appsrvc.liveness); + let live_req = format!("{}",&appsrvc.req); + if debug > 2 { + println!("livenes: {} -> {}",&appsrvc.name,&serverstring); + } + let res_info = match liveness_check(&source,&cntrllrs,&serverstring,&live_req,&name).await { + Ok(_) => "ok", + Err(e) => { + if appsrvc.critical == IsCritical::yes { + let monitor_name = "WUJI_MONITOR"; + println!("{} critical livenes: {} -> {}",envmnt::get_or(&monitor_name,""),&appsrvc.name,&serverstring); + } + if debug > 0 { + eprint!("liveness_check error: {}",e); + } + "err" + }, + }; + // println!("{} info: {}",&appsrvc.name,&res_info); + appsrvcs_info.push(AppsrvcInfo { + name: format!("{}",&appsrvc.name), + info: serde_yaml::from_str(res_info).unwrap_or_else(|e| { + eprintln!("Serde liveness Error: {} {} -> {}",&source,&serverstring,e); + String::from("") + }), + srvc: appsrvc.to_owned(), + }); + // }, + // } + } + appsrvcs_info.to_owned() +} + +pub async fn create_cloud_check(req_tsksrvcs: &str,reqenv: &ReqEnv,entries: Vec,mut cloud: Cloud) -> Vec { + let mut cfg_entries: Vec = Vec::new(); + for entry in entries.iter() { + let (mut cfg,_provider,cfg_data) = load_cloud_check_config(&mut cloud, &entry).await + .unwrap_or_else(|e| { + eprintln!("load_cloud_check_config: {}",e); + (KloudCheckHome::default(),Provider::default(),String::from("")) + }); + if cfg.name.is_empty() || cfg_data.is_empty() { + let result = format!("Errors loading {}",&entry); + if envmnt::get_isize("DEBUG",0) > 0 { + println!("{}",&result); + } + continue; + } + if req_tsksrvcs.contains("monitor") { + cfg.monitor_info = Some(get_cloud_monitor_info(&mut cloud, &entry).await + .unwrap_or_else(|e| { + eprintln!("Error {} monitor_info {} -> {}",&entry,&cloud.env.monitor_run,e); + String::from("") + })); + } + if req_tsksrvcs.contains("liveness") || req_tsksrvcs.contains("apps") || req_tsksrvcs.contains("status") { + let mut groups: Vec = Vec::new(); + // cfg.groups = cfg.groups.map(|grp| grp.with_resources(grp.path.to_owned())).collect(); + for grp in cfg.groups.iter() { + let mut items: Vec = Vec::new(); + for itm in grp.items.iter() { + let liveness: Vec; + if req_tsksrvcs.contains("liveness") { + liveness = on_cloud_name_req_check("liveness",&cloud,&reqenv,"","",&itm.path).await; + } else if req_tsksrvcs.contains("apps") { + liveness = on_cloud_name_req_check("apps",&cloud,&reqenv,"","",&itm.path).await; + } else if req_tsksrvcs.contains("status") { + liveness = on_cloud_name_req_check("status",&cloud,&reqenv,"","",&itm.path).await; + } else { + liveness = itm.liveness.to_owned(); + } + items.push(CloudCheckItem { + name: itm.name.to_owned(), + info: itm.info.to_owned(), + path: itm.path.to_owned(), + liveness, + critical: itm.critical.to_owned(), + }); + } + groups.push(CloudCheckGroup { + name: grp.name.to_owned(), + info: grp.info.to_owned(), + path: grp.path.to_owned(), + // TODO check this for group + liveness: grp.liveness.to_owned(), + items, + }); + } + cfg.groups=groups; + } + cfg_entries.push(cfg.to_owned()); + } + cfg_entries +} \ No newline at end of file diff --git a/src/monitor.rs b/src/monitor.rs index 61f32b6..5b9a4d2 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,2 +1,28 @@ pub mod utils; pub mod defs; +use anyhow::{anyhow,Result}; +use std::path::Path; +use std::str; +use std::process::{Command}; + +use crate::clouds::defs::{ + Cloud, +}; + +pub async fn get_cloud_monitor_info(cloud: &mut Cloud, source: &str) -> Result { + let cloud_home_path = format!("{}/{}",&cloud.env.home,&source); + let monitor_path = format!("{}/{}",&cloud_home_path,&cloud.env.monitor_run); + if Path::new(&monitor_path).exists() { + let output = Command::new("bash") + .arg(format!("{}",&monitor_path)) + .arg("-o") + .arg("json") + .arg(format!("{}",&source)) + .output()?; + if !&output.status.success() { + return Err(anyhow!("Run {} for {} failed: {}",&cloud.env.monitor_run,&source,&output.status)); + } + return Ok(str::from_utf8(&output.stdout).unwrap_or_else(|_| "").to_owned()); + } + Ok("".to_owned()) +} \ No newline at end of file diff --git a/src/tsksrvcs.rs b/src/tsksrvcs.rs index 152a4bf..95a6d0c 100644 --- a/src/tsksrvcs.rs +++ b/src/tsksrvcs.rs @@ -305,4 +305,4 @@ pub async fn run_tsksrvcs_on_providers(provider: &Provider, cfg_data: &str, clou _ => Err(anyhow!("Provider '{}' undefined",&provider.name)), } -} \ No newline at end of file +}