diff --git a/Cargo.lock b/Cargo.lock index b9bb0c9..18c6bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,25 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ansi-str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060de1453b69f46304b28274f382132f4e72c55637cf362920926a70d090890d" +dependencies = [ + "ansitok", +] + +[[package]] +name = "ansitok" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a8acea8c2f1c60f0a92a8cd26bf96ca97db56f10bbcab238bbe0cceba659ee" +dependencies = [ + "nom", + "vte", +] + [[package]] name = "anstream" version = "1.0.0" @@ -73,6 +92,12 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + [[package]] name = "bitflags" version = "1.3.2" @@ -85,6 +110,12 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytes" version = "1.12.0" @@ -230,7 +261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -239,6 +270,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.2.0" @@ -300,6 +337,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "jiff" version = "0.2.31" @@ -392,6 +435,12 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "1.2.1" @@ -403,6 +452,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -415,6 +474,19 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "papergrid" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0984e668274d34691bc2b262ef0d115de5fa9973bcdee7ae32213f93099153e" +dependencies = [ + "ansi-str", + "ansitok", + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -626,6 +698,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -690,6 +775,42 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tabled" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5dc662e6da844ad6e428ad16b57967c9d33c82e16bb1c258326c0c078605dff" +dependencies = [ + "ansi-str", + "ansitok", + "papergrid", + "tabled_derive", + "testing_table", +] + +[[package]] +name = "tabled_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea5d1b13ca6cff1f9231ffd62f15eefd72543dab5e468735f1a456728a02846" +dependencies = [ + "heck", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "testing_table" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8daae29995a24f65619e19d8d31dea5b389f3d853d8bf297bbf607cd0014cc" +dependencies = [ + "ansitok", + "unicode-width", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -793,6 +914,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "untrusted" version = "0.9.0" @@ -814,6 +941,16 @@ dependencies = [ "getrandom 0.4.3", ] +[[package]] +name = "vte" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "arrayvec", + "memchr", +] + [[package]] name = "wara" version = "0.1.0" @@ -827,6 +964,8 @@ dependencies = [ "mikrotik-rs", "regex", "serde", + "serde_json", + "tabled", "tokio", "toml", ] @@ -936,3 +1075,9 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 9a5db20..e84b9aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,7 @@ log = "0.4.33" mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] } regex = "1.12.4" serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.150" +tabled = { version = "0.21.0", features = ["ansi"] } tokio = { version = "1.52.3", features = ["full"] } toml = "1.1.2" diff --git a/config.default.toml b/config.default.toml index 2e9276b..0c1f3ef 100644 --- a/config.default.toml +++ b/config.default.toml @@ -1,6 +1,7 @@ [default_logins] username="monitor" password="HewoWorld" +port=8782 [[host]] name="Example1" @@ -9,5 +10,6 @@ ip="127.0.0.1" [[host]] name="Example2" ip="127.0.0.2" +port=8728 username="admin" password="password123" diff --git a/out.csv b/out.csv new file mode 100644 index 0000000..62a40f9 --- /dev/null +++ b/out.csv @@ -0,0 +1,5 @@ +Name,IP,.id,ap,bridge,bytes,distance,frame-bytes,frames,interface,last-activity,last-ip,mac-address,packets,radio-name,routeros-version,rx-ccq,rx-rate,signal-strength,signal-strength-ch0,signal-strength-ch1,signal-to-noise,strength-at-rates,tdma-retx,tdma-rx-size,tdma-timing-offset,tdma-tx-size,tdma-winfull,tx-ccq,tx-rate,tx-rate-set,tx-signal-strength,tx-signal-strength-ch0,tx-signal-strength-ch1,uptime,wds +KLI_SPURGA,10.60.17.23,*1,false,true,"2557633336,128708034",1,"2557852248,129123775","2188267,441115",wlan1,0ms,10.80.17.11,64:D1:54:91:EC:47,"2236186,552855",Sedos 34,6.49.18,66,104Mbps-20MHz/2S,-63,-66,-65,40,"-63@6Mbps 0ms,-61@9Mbps 15h29m13s760ms,-62@12Mbps 15h29m10s460ms,-61@18Mbps 15h29m10s450ms,-64@24Mbps 3m4s740ms,-65@36Mbps 3m2s60ms,-65@48Mbps 17s490ms,-67@54Mbps 16s980ms",5,1520,7,496,0,90,144.4Mbps-20MHz/2S/SGI,"OFDM:6-54 BW:1x SGI:1x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-57,-59,-63,15h38m10s,false +KLI_SPURGA,10.60.17.23,*2,false,true,"950673764,218649231",1,"951713687,220908444","4234949,1016954",wlan1,0ms,10.80.17.21,64:D1:54:43:F6:35,"4414597,1456217",sedos 42,6.46.2,95,780Mbps-80MHz/2S/SGI,-46,-50,-48,57,"-46@6Mbps 0ms,-46@9Mbps 2h30m27s860ms,-46@12Mbps 2h30m27s850ms,-47@18Mbps 2h30m39s760ms,-50@54Mbps 8s530ms",1,496,1,4000,0,95,780Mbps-80MHz/2S/SGI,"OFDM:6-54 BW:1x-4x SGI:1x-4x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-44,-46,-46,15h38m9s,false +KLI_VENTOS_KRANTO,10.60.17.19,*27,false,true,"79571155703,2550226838",6,"79698231153,2427715753","63248407,20461337",wlan2,10ms,10.80.17.25,64:D1:54:20:C5:C1,"63248407,20459064",Kapu 15,6.49.19,75,6Mbps,-72@6Mbps,-73,-79,33,"-72@6Mbps 10ms,-73@9Mbps 9h58m8s680ms,-74@12Mbps 9h58m2s760ms,-75@18Mbps 3h55m21s430ms,-75@24Mbps 3h55m21s760ms,-75@36Mbps 2h33m49s190ms,-75@48Mbps 2h11m54s820ms,-77@54Mbps 2h11m54s930ms,-74@HT20-0 9h59m22s650ms,-74@HT20-1 9h59m22s910ms,-74@HT20-2 2h46m17s960ms,-74@HT20-3 12s300ms,-71@HT20-4 12s430ms,-74@HT20-5 11s950ms,-77@HT20-6 1m2s750ms,-78@HT20-7 2d7h31m37s420ms,-77@HT40-0 2h32m55s340ms,-77@HT40-1 2h31m53s970ms,-76@HT40-2 3m5s190ms,-76@HT40-3 12s330ms,-75@HT40-4 3s90ms,-79@HT40-5 11s960ms,-79@HT40-6 5d15h8m28s70ms",,,,,,74,180Mbps-40MHz/2S/SGI,OFDM:6-54 BW:1x-2x SGI:1x-2x HT:0-15,-59,-61,-63,4d16h34m,false +KLI_VENTOS_KRANTO,10.60.17.19,*2B,false,true,"12498473399,900601701",10,"12519902471,878865267","10455470,3627677",wlan2,20ms,10.60.17.23,64:D1:54:5F:3C:E5,"10455470,3627415",aerodromas,6.45.1,82,6Mbps,-67@6Mbps,-68,-75,38,"-67@6Mbps 20ms,-66@9Mbps 9h59m1s350ms,-67@12Mbps 9h57m56s490ms,-66@18Mbps 9h57m40s200ms,-64@24Mbps 57m14s230ms,-65@36Mbps 57m14s570ms,-66@48Mbps 57m15s540ms,-67@54Mbps 57m17s140ms",,,,,,87,195Mbps-80MHz/1S/SGI,"OFDM:6-54 BW:1x-4x SGI:1x-4x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-66,-74,-67,21h44m30s,false diff --git a/src/config/cli.rs b/src/config/cli.rs index fc63fbc..5114eb0 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -1,4 +1,5 @@ use clap::ArgGroup; +use serde::de; #[derive(Debug, Clone, clap::Parser, Default)] pub struct CliArgs { @@ -34,6 +35,9 @@ pub enum CliCommand { /// Command to run #[arg(long, short = 'c')] command: String, + /// Output format + #[arg(long, short = 'f')] + format: OutputFormat, }, /// Run command continuosly #[command( @@ -53,8 +57,11 @@ pub enum CliCommand { /// Command to run #[arg(long, short = 'c')] command: String, - #[arg(long, short = 'i')] + /// Output format + #[arg(long, short = 'f')] + format: OutputFormat, /// Interval to run the commands at + #[arg(long, short = 'i')] interval: f32, }, /// Manage devices @@ -77,3 +84,11 @@ pub enum CliDeviceCommand { #[clap(skip)] None, } + +#[derive(Clone, Debug, Default, clap::ValueEnum)] +pub enum OutputFormat { + #[default] + Csv, + Table, + Json, +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 8538dc5..850996d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,6 +2,7 @@ use std::net::{IpAddr, Ipv4Addr}; use clap::Parser; use serde::{Deserialize, Serialize}; +use tabled::Tabled; use crate::config::cli::{CliArgs, CliCommand}; @@ -23,16 +24,32 @@ pub struct Config { pub struct ConfigDefaultLogins { pub username: String, pub password: String, + pub port: u16, } #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct ConfigHost { pub name: String, pub ip: IpAddr, + pub port: Option, pub username: Option, pub password: Option, } +#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Tabled)] +pub struct HostDisplay { + #[tabled(rename = "Name")] + pub name: String, + #[tabled(rename = "IP")] + pub ip: IpAddr, + #[tabled(rename = "Port")] + pub port: u16, + #[tabled(rename = "Username")] + pub username: String, + #[tabled(rename = "Pasword")] + pub password: String, +} + impl Config { pub fn parse() -> anyhow::Result { let cli = cli::CliArgs::parse(); @@ -63,6 +80,22 @@ impl ConfigHost { &cfg.default_logins.password } } + pub fn port(&self, cfg: &Config) -> u16 { + if let Some(port) = self.port { + port + } else { + cfg.default_logins.port + } + } + pub fn display_tabled(&self, cfg: &Config) -> HostDisplay { + HostDisplay { + name: self.name.clone(), + ip: self.ip.clone(), + port: self.port(&cfg), + username: self.username(cfg).clone(), + password: self.password(cfg).clone(), + } + } } impl Default for ConfigHost { @@ -70,6 +103,7 @@ impl Default for ConfigHost { Self { name: String::default(), ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + port: None, username: None, password: None, } diff --git a/src/main.rs b/src/main.rs index 3d51a7b..e53118b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,19 @@ -use std::{net::IpAddr, str::FromStr}; +use std::{ + collections::{BTreeMap, HashMap}, + net::IpAddr, + str::FromStr, + time::Duration, +}; -use crate::config::cli::CliCommand; +use clap::builder; +use log::LevelFilter; +use serde_json::json; +use tabled::Table; + +use crate::config::{ + Config, ConfigHost, + cli::{CliCommand, CliDeviceCommand, OutputFormat}, +}; mod config; mod mt_commander; @@ -8,7 +21,19 @@ mod mt_commander; #[tokio::main] async fn main() -> anyhow::Result<()> { let cfg = config::Config::parse()?; - dbg!(&cfg); + //dbg!(&cfg); + + env_logger::builder() + .parse_default_env() + .filter_module( + "wara", + if cfg.cli.verbose { + LevelFilter::Debug + } else { + LevelFilter::Info + }, + ) + .init(); mt_commander::MtCommander::init(cfg.clone())?; @@ -17,65 +42,166 @@ async fn main() -> anyhow::Result<()> { all, devices, command, + format, } => { - let mut hosts = Vec::new(); - if *all { - hosts.clone_from(&cfg.hosts); - } else { - let mut errored = false; - for dev in devices { - let Ok(ip) = IpAddr::from_str(dev) else { - log::error!("Unable to parse {dev} as an IP"); - errored = true; - continue; - }; - let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::>(); - let Some(host) = found_hosts.first() else { - log::error!("Could not find host with IP {dev}"); - errored = true; - continue; - }; - hosts.push((**host).clone()); - } - if errored { - return Ok(()); - } - } - mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?; + let hosts = get_devices(devices, *all, &cfg)?; + let out = mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?; + print_output(&cfg, &out, format)?; } CliCommand::Monitor { all, devices, command, interval, + format, } => { - let mut hosts = Vec::new(); - if *all { - hosts.clone_from(&cfg.hosts); - } else { - let mut errored = false; - for dev in devices { - let Ok(ip) = IpAddr::from_str(dev) else { - log::error!("Unable to parse {dev} as an IP"); - errored = true; - continue; - }; - let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::>(); - let Some(host) = found_hosts.first() else { - log::error!("Could not find host with IP {dev}"); - errored = true; - continue; - }; - hosts.push((**host).clone()); - } - if errored { - return Ok(()); - } + let hosts = get_devices(devices, *all, &cfg)?; + loop { + let out = mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?; + print_output(&cfg, &out, format)?; + tokio::time::sleep(Duration::from_secs_f32(*interval)).await; } - mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?; } - CliCommand::Device { command } => {} + CliCommand::Device { command } => match command { + CliDeviceCommand::List => { + let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg)); + + let table = tabled::Table::new(devices); + println!("{}", table); + } + CliDeviceCommand::None => unreachable!(), + }, CliCommand::None => unreachable!(), } Ok(()) } + +fn get_devices(devices: &Vec, all: bool, cfg: &Config) -> anyhow::Result> { + let mut hosts = Vec::new(); + if all { + hosts.clone_from(&cfg.hosts); + } else { + let mut errored = false; + for dev in devices { + let Ok(ip) = IpAddr::from_str(dev) else { + log::error!("Unable to parse {dev} as an IP"); + errored = true; + continue; + }; + let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::>(); + let Some(host) = found_hosts.first() else { + log::error!("Could not find host with IP {dev}"); + errored = true; + continue; + }; + hosts.push((**host).clone()); + } + if errored { + anyhow::bail!(""); + } + } + Ok(hosts) +} + +fn print_output( + cfg: &Config, + output: &HashMap>>>, + format: &OutputFormat, +) -> anyhow::Result<()> { + match *format { + OutputFormat::Csv => { + let mut table = Vec::new(); + let mut wanted_cols = Vec::new(); + let mut top_row_added = false; + for (host, out) in output { + for out in out { + let out: BTreeMap<_, _> = out.clone().into_iter().collect(); + if !top_row_added { + let mut row = vec!["Name".to_string(), "IP".to_string()]; + let mut data_cols = out.keys().cloned().collect::>(); + wanted_cols = data_cols.clone(); + row.append(&mut data_cols); + + table.push(row); + top_row_added = true; + } + let mut row = vec![host.name.clone(), host.ip.clone().to_string()]; + + for wanted_col in &wanted_cols { + let mut val = String::new(); + if let Some(v) = out.get(wanted_col) { + if let Some(v) = v { + val = v.clone(); + } + } + row.push(val.clone()); + } + table.push(row); + } + } + let mut table_escaped = Vec::new(); + for line in table { + let mut row = Vec::new(); + for mut item in line { + if item.contains(',') { + if item.contains('"') { + item = item.replace('"', "\"\""); + } + item = format!("\"{item}\""); + } + row.push(item); + } + table_escaped.push(row.join(",")); + } + for row in table_escaped { + println!("{row}"); + } + } + OutputFormat::Table => { + let mut builder = tabled::builder::Builder::default(); + let mut wanted_cols = Vec::new(); + let mut top_row_added = false; + for (host, out) in output { + for out in out { + let out: BTreeMap<_, _> = out.clone().into_iter().collect(); + if !top_row_added { + let mut row = vec!["Name".to_string(), "IP".to_string()]; + let mut data_cols = out.keys().cloned().collect::>(); + wanted_cols = data_cols.clone(); + row.append(&mut data_cols); + + builder.push_record(row); + top_row_added = true; + } + let mut row = vec![host.name.clone(), host.ip.clone().to_string()]; + for wanted_col in &wanted_cols { + let mut val = String::new(); + if let Some(v) = out.get(wanted_col) { + if let Some(v) = v { + val = v.clone(); + } + } + row.push(val.clone()); + } + builder.push_record(row); + } + } + let table = builder.build(); + println!("{}", table); + } + OutputFormat::Json => { + let mut res = Vec::new(); + + for (host, out) in output { + let val = json!({ + "host": host.display_tabled(&cfg), + "response": out, + }); + res.push(val); + } + let out_text = serde_json::to_string_pretty(&res)?; + println!("{out_text}"); + } + } + Ok(()) +} diff --git a/src/mt_commander/mod.rs b/src/mt_commander/mod.rs index 01509f5..1d9e8fb 100644 --- a/src/mt_commander/mod.rs +++ b/src/mt_commander/mod.rs @@ -29,7 +29,7 @@ impl MtCommander { pub async fn run_command_on_hosts( hosts: &[ConfigHost], command: &str, - ) -> anyhow::Result> { + ) -> anyhow::Result>>>> { let mut res_map = HashMap::new(); for host in hosts { let res = Self::run_command_on_host(host, command).await?; @@ -37,12 +37,15 @@ impl MtCommander { } Ok(res_map) } - pub async fn run_command_on_host(host: &ConfigHost, command: &str) -> anyhow::Result { + pub async fn run_command_on_host( + host: &ConfigHost, + command: &str, + ) -> anyhow::Result>>> { let mtc = MTC.lock().map_err(|e| anyhow::anyhow!(e.to_string()))?; mtc.run_command(host, &command).await } - pub async fn run_command_on_hosts_and_parse_re( + /*pub async fn run_command_on_hosts_and_parse_re( hosts: &[ConfigHost], command: &str, parse_re: &Regex, @@ -53,9 +56,9 @@ impl MtCommander { res_map.insert(host.clone(), res); } Ok(res_map) - } + }*/ - pub async fn run_command_on_host_and_parse_re( + /*pub async fn run_command_on_host_and_parse_re( host: &ConfigHost, command: &str, parse_re: &Regex, @@ -67,7 +70,7 @@ impl MtCommander { .map(|s| s.as_str().to_string()) .collect(); Ok(out) - } + }*/ } impl MtCommanderInternal { @@ -95,9 +98,13 @@ impl MtCommanderInternal { tokio::spawn(watcher()); Ok(()) } - async fn run_command(&self, host: &ConfigHost, cmd: &str) -> anyhow::Result { + async fn run_command( + &self, + host: &ConfigHost, + cmd: &str, + ) -> anyhow::Result>>> { let dev = MikrotikDevice::connect( - format!("{}", &host.ip), + format!("{}:{}", &host.ip, host.port(&self.cfg())), &host.username(self.cfg()), Some(&host.password(self.cfg())), ) @@ -107,14 +114,17 @@ impl MtCommanderInternal { .send_command(CommandBuilder::new().command(cmd).build()) .await?; - let mut res = String::new(); + let mut res = Vec::new(); while let Some(event) = rx.recv().await { match event { Event::Reply { response, .. } => { log::debug!("({}) {:?}", &host.ip, response.attributes); + res.push(response.attributes); + } + Event::Done { .. } => { + break; } - Event::Done { .. } => break, Event::Trap { response, .. } => { log::error!("({}) {:?}", &host.ip, response.message); }