Owo
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
23
src/main.rs
23
src/main.rs
@@ -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!(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user