Add threaded mt fetching, initial db tuff

This commit is contained in:
2026-08-14 18:58:22 +03:00
parent fbef11dfe7
commit c7d2af0fd0
11 changed files with 505 additions and 81 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/target /target
config.toml config.toml
/.env

324
Cargo.lock generated
View File

@@ -116,6 +116,12 @@ version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.12.0" version = "1.12.0"
@@ -178,7 +184,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -193,6 +199,41 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" 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]] [[package]]
name = "defmt" name = "defmt"
version = "1.1.0" version = "1.1.0"
@@ -213,7 +254,7 @@ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -225,6 +266,79 @@ dependencies = [
"thiserror", "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]] [[package]]
name = "env_filter" name = "env_filter"
version = "2.0.0" version = "2.0.0"
@@ -282,6 +396,94 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" 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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.17" version = "0.2.17"
@@ -321,6 +523,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.0" version = "2.14.0"
@@ -365,7 +573,7 @@ checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -401,6 +609,27 @@ version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" 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]] [[package]]
name = "mikrotik-proto" name = "mikrotik-proto"
version = "0.2.0" version = "0.2.0"
@@ -516,6 +745,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pkg-config"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.13.1" version = "1.13.1"
@@ -531,6 +766,17 @@ dependencies = [
"portable-atomic", "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]] [[package]]
name = "proc-macro-error-attr2" name = "proc-macro-error-attr2"
version = "2.0.0" version = "2.0.0"
@@ -550,7 +796,7 @@ dependencies = [
"proc-macro-error-attr2", "proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -695,7 +941,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -736,6 +982,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@@ -775,6 +1027,17 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "tabled" name = "tabled"
version = "0.21.0" version = "0.21.0"
@@ -798,7 +1061,7 @@ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -828,7 +1091,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -856,7 +1119,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@@ -869,6 +1132,19 @@ dependencies = [
"tokio", "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]] [[package]]
name = "toml" name = "toml"
version = "1.1.2+spec-1.1.0" version = "1.1.2+spec-1.1.0"
@@ -878,10 +1154,19 @@ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime 1.1.1+spec-1.1.0",
"toml_parser", "toml_parser",
"toml_writer", "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]] [[package]]
@@ -899,7 +1184,7 @@ version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [ dependencies = [
"winnow", "winnow 1.0.3",
] ]
[[package]] [[package]]
@@ -941,6 +1226,12 @@ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
] ]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "vte" name = "vte"
version = "0.14.1" version = "0.14.1"
@@ -958,7 +1249,10 @@ dependencies = [
"anyhow", "anyhow",
"camino", "camino",
"clap", "clap",
"diesel",
"diesel_migrations",
"env_logger", "env_logger",
"futures",
"lazy_static", "lazy_static",
"log", "log",
"mikrotik-rs", "mikrotik-rs",
@@ -967,7 +1261,7 @@ dependencies = [
"serde_json", "serde_json",
"tabled", "tabled",
"tokio", "tokio",
"toml", "toml 1.1.2+spec-1.1.0",
] ]
[[package]] [[package]]
@@ -1064,6 +1358,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "1.0.3" version = "1.0.3"

View File

@@ -11,7 +11,10 @@ authors = [
anyhow = "1.0.103" anyhow = "1.0.103"
camino = { version = "1.2.4", features = ["serde1"] } camino = { version = "1.2.4", features = ["serde1"] }
clap = { version = "4.6.1", features = ["derive"] } clap = { version = "4.6.1", features = ["derive"] }
diesel = { version = "2.3.12", features = ["postgres"] }
diesel_migrations = "2.3.2"
env_logger = "0.11.11" env_logger = "0.11.11"
futures = "0.3.34"
lazy_static = "1.5.0" lazy_static = "1.5.0"
log = "0.4.33" log = "0.4.33"
mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] } mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] }

View File

@@ -1,3 +1,5 @@
db_url="postgres://my_database_user:password@localhost/my_database"
[default_logins] [default_logins]
username="monitor" username="monitor"
password="HewoWorld" password="HewoWorld"

9
diesel.toml Normal file
View File

@@ -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"

0
migrations/.keep Normal file
View File

View File

@@ -92,6 +92,7 @@ pub enum CliCommand {
#[command(subcommand)] #[command(subcommand)]
command: CliDeviceCommand, command: CliDeviceCommand,
}, },
BesWifi,
#[default] #[default]
#[clap(skip)] #[clap(skip)]
@@ -117,7 +118,7 @@ pub enum CliDeviceCommand {
username: Option<String>, username: Option<String>,
/// Password (skip for default) /// Password (skip for default)
#[arg(short = 'P', long)] #[arg(short = 'P', long)]
password: Option<String> password: Option<String>,
}, },
Remove { Remove {
/// Name of the device /// Name of the device

View File

@@ -24,6 +24,8 @@ pub struct Config {
#[serde(rename = "host")] #[serde(rename = "host")]
pub hosts: Vec<ConfigHost>, pub hosts: Vec<ConfigHost>,
pub db_url: String,
#[serde(skip)] #[serde(skip)]
pub cli: CliArgs, pub cli: CliArgs,
} }
@@ -42,6 +44,14 @@ pub struct ConfigHost {
pub port: Option<u16>, pub port: Option<u16>,
pub username: Option<String>, pub username: Option<String>,
pub password: Option<String>, pub password: Option<String>,
#[serde(default = "ConfigHost::default_tags")]
pub tags: Vec<String>,
}
impl ConfigHost {
fn default_tags() -> Vec<String> {
Vec::default()
}
} }
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Tabled)] #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Tabled)]
@@ -119,6 +129,7 @@ impl Default for ConfigHost {
port: None, port: None,
username: None, username: None,
password: None, password: None,
tags: Vec::new(),
} }
} }
} }

17
src/db/mod.rs Normal file
View File

@@ -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
}

View File

@@ -25,6 +25,7 @@ use crate::config::{
}; };
mod config; mod config;
mod db;
mod mt_commander; mod mt_commander;
#[tokio::main] #[tokio::main]
@@ -49,6 +50,7 @@ async fn main() -> anyhow::Result<()> {
mt_commander::MtCommander::init(cfg.clone())?; mt_commander::MtCommander::init(cfg.clone())?;
match &cfg.cli.command { match &cfg.cli.command {
CliCommand::BesWifi => {}
CliCommand::Run { CliCommand::Run {
all, all,
devices, devices,
@@ -79,9 +81,22 @@ async fn main() -> anyhow::Result<()> {
let table = tabled::Table::new(devices); let table = tabled::Table::new(devices);
println!("{}", table); println!("{}", table);
}, }
CliDeviceCommand::Add { name, host, port, username, password } => { CliDeviceCommand::Add {
cfg.hosts.push(ConfigHost { name, ip: host, port, username, password }); name,
host,
port,
username,
password,
} => {
cfg.hosts.push(ConfigHost {
name,
ip: host,
port,
username,
password,
tags: Vec::new(),
});
cfg.save()?; cfg.save()?;
} }
CliDeviceCommand::Remove { name } => { CliDeviceCommand::Remove { name } => {
@@ -131,7 +146,7 @@ fn get_devices(devices: &Vec<String>, all: bool, cfg: &Config) -> anyhow::Result
fn print_output( fn print_output(
cfg: &Config, cfg: &Config,
output: &HashMap<ConfigHost, Vec<HashMap<String, Option<String>>>>, output: &HashMap<ConfigHost, anyhow::Result<Vec<HashMap<String, Option<String>>>>>,
format: &OutputFormat, format: &OutputFormat,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
match *format { match *format {
@@ -140,10 +155,13 @@ fn print_output(
let mut wanted_cols = Vec::new(); let mut wanted_cols = Vec::new();
let mut top_row_added = false; let mut top_row_added = false;
for (host, out) in output { for (host, out) in output {
match out {
Ok(out) => {
for out in out { for out in out {
let out: BTreeMap<_, _> = out.clone().into_iter().collect(); let out: BTreeMap<_, _> = out.clone().into_iter().collect();
if !top_row_added { if !top_row_added {
let mut row = vec!["Name".to_string(), "IP".to_string()]; let mut row =
vec!["Name".to_string(), "IP".to_string(), "Error".to_string()];
let mut data_cols = out.keys().cloned().collect::<Vec<String>>(); let mut data_cols = out.keys().cloned().collect::<Vec<String>>();
wanted_cols = data_cols.clone(); wanted_cols = data_cols.clone();
row.append(&mut data_cols); row.append(&mut data_cols);
@@ -151,7 +169,11 @@ fn print_output(
table.push(row); table.push(row);
top_row_added = true; top_row_added = true;
} }
let mut row = vec![host.name.clone(), host.ip.clone().to_string()]; let mut row = vec![
host.name.clone(),
host.ip.clone().to_string(),
String::from("None"),
];
for wanted_col in &wanted_cols { for wanted_col in &wanted_cols {
let mut val = String::new(); let mut val = String::new();
@@ -165,6 +187,20 @@ fn print_output(
table.push(row); 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(); let mut table_escaped = Vec::new();
for line in table { for line in table {
let mut row = Vec::new(); let mut row = Vec::new();
@@ -188,10 +224,16 @@ fn print_output(
let mut wanted_cols = Vec::new(); let mut wanted_cols = Vec::new();
let mut top_row_added = false; let mut top_row_added = false;
for (host, out) in output { for (host, out) in output {
match out {
Ok(out) => {
for out in out { for out in out {
let out: BTreeMap<_, _> = out.clone().into_iter().collect(); let out: BTreeMap<_, _> = out.clone().into_iter().collect();
if !top_row_added { if !top_row_added {
let mut row = vec!["Name".to_string(), "IP".to_string()]; let mut row = vec![
"Name".to_string(),
"IP".to_string(),
String::from("Error"),
];
let mut data_cols = out.keys().cloned().collect::<Vec<String>>(); let mut data_cols = out.keys().cloned().collect::<Vec<String>>();
wanted_cols = data_cols.clone(); wanted_cols = data_cols.clone();
row.append(&mut data_cols); row.append(&mut data_cols);
@@ -199,7 +241,11 @@ fn print_output(
builder.push_record(row); builder.push_record(row);
top_row_added = true; top_row_added = true;
} }
let mut row = vec![host.name.clone(), host.ip.clone().to_string()]; let mut row = vec![
host.name.clone(),
host.ip.clone().to_string(),
String::from("None"),
];
for wanted_col in &wanted_cols { for wanted_col in &wanted_cols {
let mut val = String::new(); let mut val = String::new();
if let Some(v) = out.get(wanted_col) { if let Some(v) = out.get(wanted_col) {
@@ -212,6 +258,15 @@ fn print_output(
builder.push_record(row); 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(); let table = builder.build();
println!("{}", table); println!("{}", table);
} }
@@ -219,12 +274,25 @@ fn print_output(
let mut res = Vec::new(); let mut res = Vec::new();
for (host, out) in output { for (host, out) in output {
match out {
Ok(out) => {
let val = json!({ let val = json!({
"host": host.display_tabled(&cfg), "host": host.display_tabled(&cfg),
"e": "None",
"response": out, "response": out,
}); });
res.push(val); 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)?; let out_text = serde_json::to_string_pretty(&res)?;
println!("{out_text}"); println!("{out_text}");
} }

View File

@@ -15,6 +15,7 @@ use std::{
}; };
use anyhow::bail; use anyhow::bail;
use futures::future::join_all;
use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice}; use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice};
use regex::Regex; use regex::Regex;
@@ -25,6 +26,7 @@ lazy_static::lazy_static! {
} }
pub struct MtCommander; pub struct MtCommander;
#[derive(Debug, Clone)]
struct MtCommanderInternal { struct MtCommanderInternal {
cfg: Option<Config>, cfg: Option<Config>,
} }
@@ -39,19 +41,26 @@ impl MtCommander {
pub async fn run_command_on_hosts( pub async fn run_command_on_hosts(
hosts: &[ConfigHost], hosts: &[ConfigHost],
command: &str, command: &str,
) -> anyhow::Result<HashMap<ConfigHost, Vec<HashMap<String, Option<String>>>>> { ) -> anyhow::Result<HashMap<ConfigHost, anyhow::Result<Vec<HashMap<String, Option<String>>>>>>
let mut res_map = HashMap::new(); {
for host in hosts { let results = join_all(hosts.iter().map(|host| async move {
let res = Self::run_command_on_host(host, command).await?; let res = Self::run_command_on_host(host, command).await;
res_map.insert(host.clone(), res); (host.clone(), res)
} }))
.await;
let res_map: HashMap<_, _> = results.into_iter().collect();
Ok(res_map) Ok(res_map)
} }
pub async fn run_command_on_host( pub async fn run_command_on_host(
host: &ConfigHost, host: &ConfigHost,
command: &str, command: &str,
) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> { ) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> {
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 mtc.run_command(host, &command).await
} }
@@ -114,18 +123,16 @@ impl MtCommanderInternal {
cmd: &str, cmd: &str,
) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> { ) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> {
let addr = format!("{}:{}", &host.ip, host.port(&self.cfg())); let addr = format!("{}:{}", &host.ip, host.port(&self.cfg()));
let dev_res = MikrotikDevice::connect( let dev_res = MikrotikDevice::builder(&addr)
&addr, .credentials(&host.username(self.cfg()), Some(&host.password(self.cfg())))
&host.username(self.cfg()), .connect()
Some(&host.password(self.cfg())),
)
.await; .await;
let dev = match dev_res { let dev = match dev_res {
Ok(dev) => { Ok(dev) => {
log::info!("({addr}) Connected successfully!"); log::info!("({addr}) Connected successfully!");
dev dev
}, }
Err(e) => { Err(e) => {
log::error!("Failed to connect to {addr}: {e}"); log::error!("Failed to connect to {addr}: {e}");
bail!(e); bail!(e);
@@ -142,7 +149,12 @@ impl MtCommanderInternal {
match event { match event {
Event::Reply { response, tag: _ } => { Event::Reply { response, tag: _ } => {
log::debug!("({}) {:?}", &host.ip, response.attributes); log::debug!("({}) {:?}", &host.ip, response.attributes);
size += response.attributes.values().flatten().map(|v| v.len()).sum::<usize>(); size += response
.attributes
.values()
.flatten()
.map(|v| v.len())
.sum::<usize>();
res.push(response.attributes); res.push(response.attributes);
} }
Event::Done { .. } => { Event::Done { .. } => {