use anyhow::{anyhow,Result}; use std::str; use std::fs; //, io}; use std::fs::OpenOptions; use std::io::{Write}; // use std::io::prelude::*; use std::net::TcpStream; use std::path::Path; use std::process::{Command}; use crate::defs::{SSHAccess,Cntrllr}; use crate::clouds::defs::{Cloud}; pub fn parse_yaml_value(value: serde_yaml::Value, key: String, ctx: &mut tera::Context ) { if let Some(v) = value.as_str() { ctx.insert(key.to_owned(),&v); } else if let Some(v) = value.as_bool() { ctx.insert(key.to_owned(),&v); } else if value.is_mapping() { if let Some(map) = value.as_mapping() { ctx.insert(key.to_owned(),&map); } } else if value.is_sequence() { if let Some(seq) = value.as_sequence() { // let mut data_seq: Vec = Vec::new(); // for (line, elem) in seq.iter().enumerate() { // if let Some(s) = elem.as_str() { // data_seq.push(s.to_owned()); // } else { // println!("Error {} line {}",&key,&line); // } // } // ctx.insert(key.to_owned(),&data_seq); ctx.insert(key.to_owned(),&seq); } } else { ctx.insert(key.to_owned(),&value); } } pub async fn write_log(output_path: &str, hostname: &str, name: &str, msg: &str) -> Result<()> { let now = chrono::Utc::now().timestamp(); let out = format!("{}: [{}] {} -> {}\n",&now,&hostname, &name,&msg); if Path::new(&output_path).exists() { let mut file = OpenOptions::new().append(true).open(&output_path)?; // out = out.replace("/", "/"); file.write(out.as_bytes())?; } else { 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(out.as_bytes())?; } Ok(()) } pub fn source_host() -> String { // TODO a better way by finding cntrllr in config definition let running_host = envmnt::get_or("HOSTNAME", "localhost"); let cntrllr_host = envmnt::get_or("CNTRLLR_HOST", ""); if running_host == cntrllr_host { running_host.to_owned() } else { cntrllr_host.to_owned() } } pub async fn liveness_check(source: &str,cntrllrs: &Vec,serverstring: &str,tsk_name: &str) -> Result<()> { let debug=envmnt::get_isize("DEBUG",0); // let serverstring = format!("{}:22",&hostname); let mut check_cntrllr = Cntrllr::default(); for cntrllr in cntrllrs { let serverstring = format!("{}:{}",&cntrllr.sshaccess.host,&cntrllr.sshaccess.port); match TcpStream::connect(&serverstring) { Ok(_serverstream) => { check_cntrllr = cntrllr.to_owned(); break; // handle_input(serverstream); }, Err(e) => { println!("Error: {}:{} -> {}",&cntrllr.sshaccess.host,&cntrllr.sshaccess.port,e); } } } // Some Ips in serverstring can be private so we need to know from where are we checking ... if !check_cntrllr.sshaccess.host.is_empty() && envmnt::get_or("HOSTNAME", "localhost") == check_cntrllr.sshaccess.host { if debug > 1 { println!("Checking connection to {} for {} -> {}",&serverstring,&source,&tsk_name); } match TcpStream::connect(&serverstring) { Ok(_serverstream) => { Ok(()) // handle_input(serverstream); }, Err(e) => { Err(anyhow!("Source {}: Connection to '{}' for tsksrvc '{}' failed: {}",&source,&serverstring,&tsk_name,&e)) } } } else { if debug > 1 { println!("Remote checking connection from {} to {} for {} -> {}",&check_cntrllr.sshaccess.host,&serverstring,&source,&tsk_name); } let vec_serverstring: Vec<&str> = serverstring.split(':').collect(); let output = Command::new("ssh") .arg("-o") .arg("StrictHostKeyChecking=accept-new") .arg("-p") .arg(format!("{}",check_cntrllr.sshaccess.port)) .arg(format!("{}@{}",check_cntrllr.sshaccess.user,&check_cntrllr.sshaccess.host)) .arg("nc") .arg("-zv") .arg(format!("{}",vec_serverstring[0])) .arg(format!("{}",vec_serverstring[1])) .output()?; match &output.status.code() { Some(code) => if format!("{}",code) == "0" { Ok(()) } else { let err = str::from_utf8(&output.stderr).unwrap_or_else(|_| ""); Err(anyhow!("Source {}: Connection to '{}' ({}) for tsksrvc '{}' failed:\n {}",&source,serverstring,&check_cntrllr.sshaccess.host,&tsk_name,&err)) } None => Ok(()) } } } pub async fn host_ssh_is_alive(cloud: &Cloud, cmd: &str, hostname: &str,tsk_name: &str, ssh_access: &SSHAccess) -> Result<()> { let debug=envmnt::get_isize("DEBUG",0); if debug > 1 { println!("Checking ssh connection to {}@{} on {} for {} -> {}",ssh_access.user,ssh_access.host,ssh_access.port,&cloud.env.source,&tsk_name); } let output = Command::new("ssh") .arg("-o") .arg("StrictHostKeyChecking=accept-new") .arg("-p") .arg(format!("{}",ssh_access.port)) .arg(format!("{}@{}",ssh_access.user,ssh_access.host)) .arg("ls") .output()?; // dbg!(&output); if !&output.status.success() { let err = str::from_utf8(&output.stderr).unwrap_or_else(|_| ""); return Err(anyhow!("Source {}: Connection to '{}' for tsksrvc '{}' cmd '{}' failed: {}",&cloud.env.source,&hostname,&tsk_name,&cmd,&err)); } Ok(()) }