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.
*/
use std::net::IpAddr;
use clap::ArgGroup;
use serde::de;
@@ -25,9 +27,13 @@ pub struct CliArgs {
pub config_file: camino::Utf8PathBuf,
/// Output more information in logs
#[arg(long = "verbose", short = 'v')]
#[arg(long = "verbose", short = 'v', conflicts_with="quiet")]
pub verbose: bool,
/// Output more information in logs
#[arg(long = "quiet", short = 'q', conflicts_with="verbose")]
pub quiet: bool,
#[command(subcommand)]
pub command: CliCommand,
}
@@ -96,7 +102,28 @@ pub enum CliCommand {
pub enum CliDeviceCommand {
/// List devices
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]
#[clap(skip)]
None,

View File

@@ -71,6 +71,11 @@ impl Config {
slf.cli = cli;
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 {

View File

@@ -29,19 +29,21 @@ mod mt_commander;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cfg = config::Config::parse()?;
let mut cfg = config::Config::parse()?;
//dbg!(&cfg);
env_logger::builder()
.parse_default_env()
.filter_module(
"wara",
if cfg.cli.verbose {
LevelFilter::Debug
} else if cfg.cli.quiet {
LevelFilter::Error
} else {
LevelFilter::Info
},
)
.parse_default_env()
.init();
mt_commander::MtCommander::init(cfg.clone())?;
@@ -71,12 +73,27 @@ async fn main() -> anyhow::Result<()> {
tokio::time::sleep(Duration::from_secs_f32(*interval)).await;
}
}
CliCommand::Device { command } => match command {
CliCommand::Device { command } => match command.clone() {
CliDeviceCommand::List => {
let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg));
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 });
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!(),
},

View File

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