From c7d2af0fd034d1f355cc342082c8260b6b373424 Mon Sep 17 00:00:00 2001 From: Gvidas Juknevicius Date: Fri, 14 Aug 2026 18:58:22 +0300 Subject: [PATCH] Add threaded mt fetching, initial db tuff --- .gitignore | 1 + Cargo.lock | 324 ++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 + config.default.toml | 2 + diesel.toml | 9 ++ migrations/.keep | 0 src/config/cli.rs | 17 ++- src/config/mod.rs | 11 ++ src/db/mod.rs | 17 +++ src/main.rs | 160 ++++++++++++++------ src/mt_commander/mod.rs | 42 ++++-- 11 files changed, 505 insertions(+), 81 deletions(-) create mode 100644 diesel.toml create mode 100644 migrations/.keep create mode 100644 src/db/mod.rs diff --git a/.gitignore b/.gitignore index 4a3b37d..1391f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target config.toml +/.env diff --git a/Cargo.lock b/Cargo.lock index 18c6bbc..a51cbb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,12 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.12.0" @@ -178,7 +184,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -193,6 +199,41 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.118", +] + [[package]] name = "defmt" version = "1.1.0" @@ -213,7 +254,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -225,6 +266,79 @@ dependencies = [ "thiserror", ] +[[package]] +name = "diesel" +version = "2.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715377c6e464cb44bb89bd8487584240516c8d5052bc645d6babc50bb8be46c3" +dependencies = [ + "bitflags 2.13.0", + "byteorder", + "diesel_derives", + "downcast-rs", + "itoa", + "pq-sys", +] + +[[package]] +name = "diesel_derives" +version = "2.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1817b7f4279b947fc4cafddec12b0e5f8727141706561ce3ac94a60bddd1cf5" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "diesel_migrations" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d0f4a98124ba6d4ca75da535f65984badec16a003b6e2f94a01e31a79490b8" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" +dependencies = [ + "syn 2.0.118", +] + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dsl_auto_type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + [[package]] name = "env_filter" version = "2.0.0" @@ -282,6 +396,94 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -321,6 +523,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "2.14.0" @@ -365,7 +573,7 @@ checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -401,6 +609,27 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[package]] +name = "migrations_internals" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "migrations_macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36fc5ac76be324cfd2d3f2cf0fdf5d5d3c4f14ed8aaebadb09e304ba42282703" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mikrotik-proto" version = "0.2.0" @@ -516,6 +745,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -531,6 +766,17 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "pq-sys" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574ddd6a267294433f140b02a726b0640c43cf7c6f717084684aaa3b285aba61" +dependencies = [ + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -550,7 +796,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -695,7 +941,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -736,6 +982,12 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.15.1" @@ -775,6 +1027,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tabled" version = "0.21.0" @@ -798,7 +1061,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -828,7 +1091,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -856,7 +1119,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -869,6 +1132,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.15", +] + [[package]] name = "toml" version = "1.1.2+spec-1.1.0" @@ -878,10 +1154,19 @@ dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", ] [[package]] @@ -899,7 +1184,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] @@ -941,6 +1226,12 @@ dependencies = [ "getrandom 0.4.3", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vte" version = "0.14.1" @@ -958,7 +1249,10 @@ dependencies = [ "anyhow", "camino", "clap", + "diesel", + "diesel_migrations", "env_logger", + "futures", "lazy_static", "log", "mikrotik-rs", @@ -967,7 +1261,7 @@ dependencies = [ "serde_json", "tabled", "tokio", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -1064,6 +1358,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + [[package]] name = "winnow" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index 2df8555..9cfa9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,10 @@ authors = [ anyhow = "1.0.103" camino = { version = "1.2.4", features = ["serde1"] } clap = { version = "4.6.1", features = ["derive"] } +diesel = { version = "2.3.12", features = ["postgres"] } +diesel_migrations = "2.3.2" env_logger = "0.11.11" +futures = "0.3.34" lazy_static = "1.5.0" log = "0.4.33" mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] } diff --git a/config.default.toml b/config.default.toml index 0c1f3ef..7649ac9 100644 --- a/config.default.toml +++ b/config.default.toml @@ -1,3 +1,5 @@ +db_url="postgres://my_database_user:password@localhost/my_database" + [default_logins] username="monitor" password="HewoWorld" diff --git a/diesel.toml b/diesel.toml new file mode 100644 index 0000000..a0d61bf --- /dev/null +++ b/diesel.toml @@ -0,0 +1,9 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" +custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] + +[migrations_directory] +dir = "migrations" diff --git a/migrations/.keep b/migrations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/config/cli.rs b/src/config/cli.rs index 824c2d5..2b74cd2 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -27,11 +27,11 @@ pub struct CliArgs { pub config_file: camino::Utf8PathBuf, /// Output more information in logs - #[arg(long = "verbose", short = 'v', conflicts_with="quiet")] + #[arg(long = "verbose", short = 'v', conflicts_with = "quiet")] pub verbose: bool, /// Output more information in logs - #[arg(long = "quiet", short = 'q', conflicts_with="verbose")] + #[arg(long = "quiet", short = 'q', conflicts_with = "verbose")] pub quiet: bool, #[command(subcommand)] @@ -92,6 +92,7 @@ pub enum CliCommand { #[command(subcommand)] command: CliDeviceCommand, }, + BesWifi, #[default] #[clap(skip)] @@ -104,20 +105,20 @@ pub enum CliDeviceCommand { List, Add { /// Device name - #[arg(short='n', long)] + #[arg(short = 'n', long)] name: String, /// Device address, ipv4 or ipv6 - #[arg(short='H', long)] + #[arg(short = 'H', long)] host: IpAddr, /// Device Port (skip for default) - #[arg(short='p', long)] + #[arg(short = 'p', long)] port: Option, /// Username (skip for default) - #[arg(short='U', long)] + #[arg(short = 'U', long)] username: Option, /// Password (skip for default) - #[arg(short='P', long)] - password: Option + #[arg(short = 'P', long)] + password: Option, }, Remove { /// Name of the device diff --git a/src/config/mod.rs b/src/config/mod.rs index b337e57..dfdc517 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -24,6 +24,8 @@ pub struct Config { #[serde(rename = "host")] pub hosts: Vec, + pub db_url: String, + #[serde(skip)] pub cli: CliArgs, } @@ -42,6 +44,14 @@ pub struct ConfigHost { pub port: Option, pub username: Option, pub password: Option, + #[serde(default = "ConfigHost::default_tags")] + pub tags: Vec, +} + +impl ConfigHost { + fn default_tags() -> Vec { + Vec::default() + } } #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Tabled)] @@ -119,6 +129,7 @@ impl Default for ConfigHost { port: None, username: None, password: None, + tags: Vec::new(), } } } diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..35f9302 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,17 @@ +use diesel::pg::PgConnection; +use diesel::prelude::*; +use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; + +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); + +pub fn init_db(database_url: &str) -> PgConnection { + let mut connection = PgConnection::establish(database_url) + .unwrap_or_else(|e| panic!("Error connecting to database: {e}")); + + connection + .run_pending_migrations(MIGRATIONS) + .unwrap_or_else(|e| panic!("Error running migrations: {e}")); + + connection +} + diff --git a/src/main.rs b/src/main.rs index abea903..4ea50ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ use crate::config::{ }; mod config; +mod db; mod mt_commander; #[tokio::main] @@ -49,6 +50,7 @@ async fn main() -> anyhow::Result<()> { mt_commander::MtCommander::init(cfg.clone())?; match &cfg.cli.command { + CliCommand::BesWifi => {} CliCommand::Run { all, devices, @@ -79,9 +81,22 @@ async fn main() -> anyhow::Result<()> { let table = tabled::Table::new(devices); println!("{}", table); - }, - CliDeviceCommand::Add { name, host, port, username, password } => { - cfg.hosts.push(ConfigHost { name, ip: host, port, username, password }); + } + CliDeviceCommand::Add { + name, + host, + port, + username, + password, + } => { + cfg.hosts.push(ConfigHost { + name, + ip: host, + port, + username, + password, + tags: Vec::new(), + }); cfg.save()?; } CliDeviceCommand::Remove { name } => { @@ -131,7 +146,7 @@ fn get_devices(devices: &Vec, all: bool, cfg: &Config) -> anyhow::Result fn print_output( cfg: &Config, - output: &HashMap>>>, + output: &HashMap>>>>, format: &OutputFormat, ) -> anyhow::Result<()> { match *format { @@ -140,29 +155,50 @@ fn print_output( 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); + match out { + Ok(out) => { + 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(), "Error".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(); + table.push(row); + top_row_added = true; } + let mut row = vec![ + host.name.clone(), + host.ip.clone().to_string(), + String::from("None"), + ]; + + 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); } - row.push(val.clone()); } - table.push(row); + Err(e) => { + let mut row = vec![ + host.name.clone(), + host.ip.clone().to_string(), + e.to_string(), + ]; + + for wanted_col in &wanted_cols { + row.push(String::new()); + } + table.push(row); + } } } let mut table_escaped = Vec::new(); @@ -188,28 +224,47 @@ fn print_output( 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); + match out { + Ok(out) => { + 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(), + String::from("Error"), + ]; + 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(); + builder.push_record(row); + top_row_added = true; } + let mut row = vec![ + host.name.clone(), + host.ip.clone().to_string(), + String::from("None"), + ]; + 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); } - row.push(val.clone()); } - builder.push_record(row); + Err(e) => { + let mut row = vec![ + host.name.clone(), + host.ip.clone().to_string(), + e.to_string(), + ]; + } } } let table = builder.build(); @@ -219,11 +274,24 @@ fn print_output( let mut res = Vec::new(); for (host, out) in output { - let val = json!({ - "host": host.display_tabled(&cfg), - "response": out, - }); - res.push(val); + match out { + Ok(out) => { + let val = json!({ + "host": host.display_tabled(&cfg), + "e": "None", + "response": out, + }); + res.push(val); + } + Err(e) => { + let val = json!({ + "host": host.display_tabled(&cfg), + "error": e.to_string(), + "response": [], + }); + res.push(val); + } + } } let out_text = serde_json::to_string_pretty(&res)?; println!("{out_text}"); diff --git a/src/mt_commander/mod.rs b/src/mt_commander/mod.rs index 65486ad..938701b 100644 --- a/src/mt_commander/mod.rs +++ b/src/mt_commander/mod.rs @@ -15,6 +15,7 @@ use std::{ }; use anyhow::bail; +use futures::future::join_all; use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice}; use regex::Regex; @@ -25,6 +26,7 @@ lazy_static::lazy_static! { } pub struct MtCommander; +#[derive(Debug, Clone)] struct MtCommanderInternal { cfg: Option, } @@ -39,19 +41,26 @@ impl MtCommander { pub async fn run_command_on_hosts( hosts: &[ConfigHost], command: &str, - ) -> anyhow::Result>>>> { - let mut res_map = HashMap::new(); - for host in hosts { - let res = Self::run_command_on_host(host, command).await?; - res_map.insert(host.clone(), res); - } + ) -> anyhow::Result>>>>> + { + let results = join_all(hosts.iter().map(|host| async move { + let res = Self::run_command_on_host(host, command).await; + (host.clone(), res) + })) + .await; + + let res_map: HashMap<_, _> = results.into_iter().collect(); Ok(res_map) } 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()))?; + let mtc = { + MTC.lock() + .map_err(|e| anyhow::anyhow!(e.to_string()))? + .clone() + }; mtc.run_command(host, &command).await } @@ -114,18 +123,16 @@ impl MtCommanderInternal { cmd: &str, ) -> anyhow::Result>>> { let addr = format!("{}:{}", &host.ip, host.port(&self.cfg())); - let dev_res = MikrotikDevice::connect( - &addr, - &host.username(self.cfg()), - Some(&host.password(self.cfg())), - ) - .await; + let dev_res = MikrotikDevice::builder(&addr) + .credentials(&host.username(self.cfg()), Some(&host.password(self.cfg()))) + .connect() + .await; let dev = match dev_res { Ok(dev) => { log::info!("({addr}) Connected successfully!"); dev - }, + } Err(e) => { log::error!("Failed to connect to {addr}: {e}"); bail!(e); @@ -142,7 +149,12 @@ impl MtCommanderInternal { match event { Event::Reply { response, tag: _ } => { log::debug!("({}) {:?}", &host.ip, response.attributes); - size += response.attributes.values().flatten().map(|v| v.len()).sum::(); + size += response + .attributes + .values() + .flatten() + .map(|v| v.len()) + .sum::(); res.push(response.attributes); } Event::Done { .. } => {