This commit is contained in:
2026-07-02 16:59:13 +03:00
parent cb142bff0e
commit 2752888e14
5 changed files with 222 additions and 2 deletions

1
.gitignore vendored
View File

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

92
Cargo.lock generated
View File

@@ -79,6 +79,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "bytes"
version = "1.12.0"
@@ -162,7 +168,7 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"defmt-macros",
]
@@ -217,6 +223,16 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -309,12 +325,27 @@ dependencies = [
"syn",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.33"
@@ -384,6 +415,29 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "pin-project-lite"
version = "0.2.17"
@@ -451,6 +505,15 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.13.0",
]
[[package]]
name = "regex"
version = "1.12.4"
@@ -527,6 +590,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
@@ -572,6 +641,22 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.6.4"
@@ -634,7 +719,9 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
@@ -735,9 +822,12 @@ dependencies = [
"camino",
"clap",
"env_logger",
"lazy_static",
"log",
"mikrotik-rs",
"regex",
"serde",
"tokio",
"toml",
]

View File

@@ -8,7 +8,10 @@ anyhow = "1.0.103"
camino = { version = "1.2.4", features = ["serde1"] }
clap = { version = "4.6.1", features = ["derive"] }
env_logger = "0.11.11"
lazy_static = "1.5.0"
log = "0.4.33"
mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] }
regex = "1.12.4"
serde = { version = "1.0.228", features = ["derive"] }
tokio = { version = "1.52.3", features = ["full"] }
toml = "1.1.2"

View File

@@ -25,7 +25,7 @@ pub struct ConfigDefaultLogins {
pub password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct ConfigHost {
pub name: String,
pub ip: IpAddr,

View File

@@ -0,0 +1,126 @@
use core::panic;
use std::{
collections::HashMap,
hash::Hash,
sync::{Arc, Mutex},
};
use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice};
use regex::Regex;
use crate::config::{Config, ConfigHost};
lazy_static::lazy_static! {
static ref MTC: Arc<Mutex<MtCommanderInternal>> = MtCommanderInternal::new_mutex();
}
pub struct MtCommander;
struct MtCommanderInternal {
cfg: Option<Config>,
}
impl MtCommander {
pub fn init(cfg: Config) -> anyhow::Result<()> {
let mut mtc = MTC.lock().map_err(|e| anyhow::anyhow!(e.to_string()))?;
mtc.cfg = Some(cfg);
Ok(())
}
pub async fn run_command_on_hosts(
hosts: &[ConfigHost],
command: &str,
) -> anyhow::Result<HashMap<ConfigHost, String>> {
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);
}
Ok(res_map)
}
pub async fn run_command_on_host(host: &ConfigHost, command: &str) -> anyhow::Result<String> {
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(
hosts: &[ConfigHost],
command: &str,
parse_re: &Regex,
) -> anyhow::Result<HashMap<ConfigHost, Vec<String>>> {
let mut res_map = HashMap::new();
for host in hosts {
let res = Self::run_command_on_host_and_parse_re(host, command, parse_re).await?;
res_map.insert(host.clone(), res);
}
Ok(res_map)
}
pub async fn run_command_on_host_and_parse_re(
host: &ConfigHost,
command: &str,
parse_re: &Regex,
) -> anyhow::Result<Vec<String>> {
let mtc = MTC.lock().map_err(|e| anyhow::anyhow!(e.to_string()))?;
let res = mtc.run_command(host, &command).await?;
let out = parse_re
.find_iter(&res)
.map(|s| s.as_str().to_string())
.collect();
Ok(out)
}
}
impl MtCommanderInternal {
fn new(cfg: Config) -> Self {
Self { cfg: Some(cfg) }
}
fn new_mutex() -> Arc<Mutex<Self>> {
Arc::new(Mutex::new(Self { cfg: None }))
}
fn init(&mut self, cfg: Config) -> anyhow::Result<()> {
self.cfg = Some(cfg);
self.spawn_watcher_thread()?;
Ok(())
}
fn cfg(&self) -> &Config {
if let Some(cfg) = &self.cfg {
cfg
} else {
panic!();
}
}
fn spawn_watcher_thread(&mut self) -> anyhow::Result<()> {
async fn watcher() {}
tokio::spawn(watcher());
Ok(())
}
async fn run_command(&self, host: &ConfigHost, cmd: &str) -> anyhow::Result<String> {
let dev = MikrotikDevice::connect(
format!("{}", &host.ip),
&host.username(self.cfg()),
Some(&host.password(self.cfg())),
)
.await?;
let mut rx = dev
.send_command(CommandBuilder::new().command(cmd).build())
.await?;
let mut res = String::new();
while let Some(event) = rx.recv().await {
match event {
Event::Reply { response, .. } => {
log::debug!("({}) {:?}", &host.ip, response.attributes);
}
Event::Done { .. } => break,
Event::Trap { response, .. } => {
log::error!("({}) {:?}", &host.ip, response.message);
}
other => println!("{other:?}"),
}
}
Ok(res)
}
}