This commit is contained in:
2026-07-07 17:19:25 +03:00
parent 14c0009030
commit fbef11dfe7
4 changed files with 74 additions and 10 deletions

View File

@@ -7,6 +7,8 @@
* permission. * permission.
*/ */
use std::net::IpAddr;
use clap::ArgGroup; use clap::ArgGroup;
use serde::de; use serde::de;
@@ -25,9 +27,13 @@ pub struct CliArgs {
pub config_file: camino::Utf8PathBuf, pub config_file: camino::Utf8PathBuf,
/// Output more information in logs /// Output more information in logs
#[arg(long = "verbose", short = 'v')] #[arg(long = "verbose", short = 'v', conflicts_with="quiet")]
pub verbose: bool, pub verbose: bool,
/// Output more information in logs
#[arg(long = "quiet", short = 'q', conflicts_with="verbose")]
pub quiet: bool,
#[command(subcommand)] #[command(subcommand)]
pub command: CliCommand, pub command: CliCommand,
} }
@@ -96,7 +102,28 @@ pub enum CliCommand {
pub enum CliDeviceCommand { pub enum CliDeviceCommand {
/// List devices /// List devices
List, List,
Add {
/// Device name
#[arg(short='n', long)]
name: String,
/// Device address, ipv4 or ipv6
#[arg(short='H', long)]
host: IpAddr,
/// Device Port (skip for default)
#[arg(short='p', long)]
port: Option<u16>,
/// Username (skip for default)
#[arg(short='U', long)]
username: Option<String>,
/// Password (skip for default)
#[arg(short='P', long)]
password: Option<String>
},
Remove {
/// Name of the device
#[arg(short, long)]
name: String,
},
#[default] #[default]
#[clap(skip)] #[clap(skip)]
None, None,

View File

@@ -71,6 +71,11 @@ impl Config {
slf.cli = cli; slf.cli = cli;
Ok(slf) Ok(slf)
} }
pub fn save(&self) -> anyhow::Result<()> {
let data = toml::to_string_pretty(self)?;
std::fs::write(&self.cli.config_file, data)?;
Ok(())
}
} }
impl ConfigHost { impl ConfigHost {

View File

@@ -29,19 +29,21 @@ mod mt_commander;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let cfg = config::Config::parse()?; let mut cfg = config::Config::parse()?;
//dbg!(&cfg); //dbg!(&cfg);
env_logger::builder() env_logger::builder()
.parse_default_env()
.filter_module( .filter_module(
"wara", "wara",
if cfg.cli.verbose { if cfg.cli.verbose {
LevelFilter::Debug LevelFilter::Debug
} else if cfg.cli.quiet {
LevelFilter::Error
} else { } else {
LevelFilter::Info LevelFilter::Info
}, },
) )
.parse_default_env()
.init(); .init();
mt_commander::MtCommander::init(cfg.clone())?; mt_commander::MtCommander::init(cfg.clone())?;
@@ -71,12 +73,27 @@ async fn main() -> anyhow::Result<()> {
tokio::time::sleep(Duration::from_secs_f32(*interval)).await; tokio::time::sleep(Duration::from_secs_f32(*interval)).await;
} }
} }
CliCommand::Device { command } => match command { CliCommand::Device { command } => match command.clone() {
CliDeviceCommand::List => { CliDeviceCommand::List => {
let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg)); let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg));
let table = tabled::Table::new(devices); let table = tabled::Table::new(devices);
println!("{}", table); println!("{}", table);
},
CliDeviceCommand::Add { name, host, port, username, password } => {
cfg.hosts.push(ConfigHost { name, ip: host, port, username, password });
cfg.save()?;
}
CliDeviceCommand::Remove { name } => {
for (i, host) in cfg.hosts.iter().enumerate() {
if host.name == name {
cfg.hosts.remove(i);
cfg.save()?;
return Ok(());
}
}
log::error!("Unable to find host with name '{name}' to remove");
return Ok(());
} }
CliDeviceCommand::None => unreachable!(), CliDeviceCommand::None => unreachable!(),
}, },

View File

@@ -14,6 +14,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use anyhow::bail;
use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice}; use mikrotik_rs::{Command, CommandBuilder, Event, MikrotikDevice};
use regex::Regex; use regex::Regex;
@@ -112,23 +113,36 @@ impl MtCommanderInternal {
host: &ConfigHost, host: &ConfigHost,
cmd: &str, cmd: &str,
) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> { ) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> {
let dev = MikrotikDevice::connect( let addr = format!("{}:{}", &host.ip, host.port(&self.cfg()));
format!("{}:{}", &host.ip, host.port(&self.cfg())), let dev_res = MikrotikDevice::connect(
&addr,
&host.username(self.cfg()), &host.username(self.cfg()),
Some(&host.password(self.cfg())), Some(&host.password(self.cfg())),
) )
.await?; .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);
}
};
let mut rx = dev let mut rx = dev
.send_command(CommandBuilder::new().command(cmd).build()) .send_command(CommandBuilder::new().command(cmd).build())
.await?; .await?;
let mut res = Vec::new(); let mut res = Vec::new();
let mut size = 0;
while let Some(event) = rx.recv().await { while let Some(event) = rx.recv().await {
match event { match event {
Event::Reply { response, .. } => { 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>();
res.push(response.attributes); res.push(response.attributes);
} }
Event::Done { .. } => { Event::Done { .. } => {
@@ -140,6 +154,7 @@ impl MtCommanderInternal {
other => println!("{other:?}"), other => println!("{other:?}"),
} }
} }
log::info!("({addr}) Command finished, read {size} bytes");
Ok(res) Ok(res)
} }
} }