Add printing, commands work
This commit is contained in:
147
Cargo.lock
generated
147
Cargo.lock
generated
@@ -17,6 +17,25 @@ version = "0.2.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi-str"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "060de1453b69f46304b28274f382132f4e72c55637cf362920926a70d090890d"
|
||||||
|
dependencies = [
|
||||||
|
"ansitok",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansitok"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0a8acea8c2f1c60f0a92a8cd26bf96ca97db56f10bbcab238bbe0cceba659ee"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
"vte",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -73,6 +92,12 @@ version = "1.0.103"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -85,6 +110,12 @@ version = "2.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytecount"
|
||||||
|
version = "0.6.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
@@ -230,7 +261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -239,6 +270,12 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -300,6 +337,12 @@ version = "1.70.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.31"
|
version = "0.2.31"
|
||||||
@@ -392,6 +435,12 @@ dependencies = [
|
|||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -403,6 +452,16 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -415,6 +474,19 @@ version = "1.70.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "papergrid"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0984e668274d34691bc2b262ef0d115de5fa9973bcdee7ae32213f93099153e"
|
||||||
|
dependencies = [
|
||||||
|
"ansi-str",
|
||||||
|
"ansitok",
|
||||||
|
"bytecount",
|
||||||
|
"fnv",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
@@ -626,6 +698,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.150"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@@ -690,6 +775,42 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tabled"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5dc662e6da844ad6e428ad16b57967c9d33c82e16bb1c258326c0c078605dff"
|
||||||
|
dependencies = [
|
||||||
|
"ansi-str",
|
||||||
|
"ansitok",
|
||||||
|
"papergrid",
|
||||||
|
"tabled_derive",
|
||||||
|
"testing_table",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tabled_derive"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ea5d1b13ca6cff1f9231ffd62f15eefd72543dab5e468735f1a456728a02846"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "testing_table"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f8daae29995a24f65619e19d8d31dea5b389f3d853d8bf297bbf607cd0014cc"
|
||||||
|
dependencies = [
|
||||||
|
"ansitok",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
@@ -793,6 +914,12 @@ version = "1.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -814,6 +941,16 @@ dependencies = [
|
|||||||
"getrandom 0.4.3",
|
"getrandom 0.4.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vte"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wara"
|
name = "wara"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -827,6 +964,8 @@ dependencies = [
|
|||||||
"mikrotik-rs",
|
"mikrotik-rs",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tabled",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
@@ -936,3 +1075,9 @@ name = "zeroize"
|
|||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ log = "0.4.33"
|
|||||||
mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] }
|
mikrotik-rs = { version = "0.8.0", features = ["tokio-tls"] }
|
||||||
regex = "1.12.4"
|
regex = "1.12.4"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
serde_json = "1.0.150"
|
||||||
|
tabled = { version = "0.21.0", features = ["ansi"] }
|
||||||
tokio = { version = "1.52.3", features = ["full"] }
|
tokio = { version = "1.52.3", features = ["full"] }
|
||||||
toml = "1.1.2"
|
toml = "1.1.2"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[default_logins]
|
[default_logins]
|
||||||
username="monitor"
|
username="monitor"
|
||||||
password="HewoWorld"
|
password="HewoWorld"
|
||||||
|
port=8782
|
||||||
|
|
||||||
[[host]]
|
[[host]]
|
||||||
name="Example1"
|
name="Example1"
|
||||||
@@ -9,5 +10,6 @@ ip="127.0.0.1"
|
|||||||
[[host]]
|
[[host]]
|
||||||
name="Example2"
|
name="Example2"
|
||||||
ip="127.0.0.2"
|
ip="127.0.0.2"
|
||||||
|
port=8728
|
||||||
username="admin"
|
username="admin"
|
||||||
password="password123"
|
password="password123"
|
||||||
|
|||||||
5
out.csv
Normal file
5
out.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Name,IP,.id,ap,bridge,bytes,distance,frame-bytes,frames,interface,last-activity,last-ip,mac-address,packets,radio-name,routeros-version,rx-ccq,rx-rate,signal-strength,signal-strength-ch0,signal-strength-ch1,signal-to-noise,strength-at-rates,tdma-retx,tdma-rx-size,tdma-timing-offset,tdma-tx-size,tdma-winfull,tx-ccq,tx-rate,tx-rate-set,tx-signal-strength,tx-signal-strength-ch0,tx-signal-strength-ch1,uptime,wds
|
||||||
|
KLI_SPURGA,10.60.17.23,*1,false,true,"2557633336,128708034",1,"2557852248,129123775","2188267,441115",wlan1,0ms,10.80.17.11,64:D1:54:91:EC:47,"2236186,552855",Sedos 34,6.49.18,66,104Mbps-20MHz/2S,-63,-66,-65,40,"-63@6Mbps 0ms,-61@9Mbps 15h29m13s760ms,-62@12Mbps 15h29m10s460ms,-61@18Mbps 15h29m10s450ms,-64@24Mbps 3m4s740ms,-65@36Mbps 3m2s60ms,-65@48Mbps 17s490ms,-67@54Mbps 16s980ms",5,1520,7,496,0,90,144.4Mbps-20MHz/2S/SGI,"OFDM:6-54 BW:1x SGI:1x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-57,-59,-63,15h38m10s,false
|
||||||
|
KLI_SPURGA,10.60.17.23,*2,false,true,"950673764,218649231",1,"951713687,220908444","4234949,1016954",wlan1,0ms,10.80.17.21,64:D1:54:43:F6:35,"4414597,1456217",sedos 42,6.46.2,95,780Mbps-80MHz/2S/SGI,-46,-50,-48,57,"-46@6Mbps 0ms,-46@9Mbps 2h30m27s860ms,-46@12Mbps 2h30m27s850ms,-47@18Mbps 2h30m39s760ms,-50@54Mbps 8s530ms",1,496,1,4000,0,95,780Mbps-80MHz/2S/SGI,"OFDM:6-54 BW:1x-4x SGI:1x-4x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-44,-46,-46,15h38m9s,false
|
||||||
|
KLI_VENTOS_KRANTO,10.60.17.19,*27,false,true,"79571155703,2550226838",6,"79698231153,2427715753","63248407,20461337",wlan2,10ms,10.80.17.25,64:D1:54:20:C5:C1,"63248407,20459064",Kapu 15,6.49.19,75,6Mbps,-72@6Mbps,-73,-79,33,"-72@6Mbps 10ms,-73@9Mbps 9h58m8s680ms,-74@12Mbps 9h58m2s760ms,-75@18Mbps 3h55m21s430ms,-75@24Mbps 3h55m21s760ms,-75@36Mbps 2h33m49s190ms,-75@48Mbps 2h11m54s820ms,-77@54Mbps 2h11m54s930ms,-74@HT20-0 9h59m22s650ms,-74@HT20-1 9h59m22s910ms,-74@HT20-2 2h46m17s960ms,-74@HT20-3 12s300ms,-71@HT20-4 12s430ms,-74@HT20-5 11s950ms,-77@HT20-6 1m2s750ms,-78@HT20-7 2d7h31m37s420ms,-77@HT40-0 2h32m55s340ms,-77@HT40-1 2h31m53s970ms,-76@HT40-2 3m5s190ms,-76@HT40-3 12s330ms,-75@HT40-4 3s90ms,-79@HT40-5 11s960ms,-79@HT40-6 5d15h8m28s70ms",,,,,,74,180Mbps-40MHz/2S/SGI,OFDM:6-54 BW:1x-2x SGI:1x-2x HT:0-15,-59,-61,-63,4d16h34m,false
|
||||||
|
KLI_VENTOS_KRANTO,10.60.17.19,*2B,false,true,"12498473399,900601701",10,"12519902471,878865267","10455470,3627677",wlan2,20ms,10.60.17.23,64:D1:54:5F:3C:E5,"10455470,3627415",aerodromas,6.45.1,82,6Mbps,-67@6Mbps,-68,-75,38,"-67@6Mbps 20ms,-66@9Mbps 9h59m1s350ms,-67@12Mbps 9h57m56s490ms,-66@18Mbps 9h57m40s200ms,-64@24Mbps 57m14s230ms,-65@36Mbps 57m14s570ms,-66@48Mbps 57m15s540ms,-67@54Mbps 57m17s140ms",,,,,,87,195Mbps-80MHz/1S/SGI,"OFDM:6-54 BW:1x-4x SGI:1x-4x HT:0-15 VHTMCS:SS1=0-9,SS2=0-9",-66,-74,-67,21h44m30s,false
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
use clap::ArgGroup;
|
use clap::ArgGroup;
|
||||||
|
use serde::de;
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::Parser, Default)]
|
#[derive(Debug, Clone, clap::Parser, Default)]
|
||||||
pub struct CliArgs {
|
pub struct CliArgs {
|
||||||
@@ -34,6 +35,9 @@ pub enum CliCommand {
|
|||||||
/// Command to run
|
/// Command to run
|
||||||
#[arg(long, short = 'c')]
|
#[arg(long, short = 'c')]
|
||||||
command: String,
|
command: String,
|
||||||
|
/// Output format
|
||||||
|
#[arg(long, short = 'f')]
|
||||||
|
format: OutputFormat,
|
||||||
},
|
},
|
||||||
/// Run command continuosly
|
/// Run command continuosly
|
||||||
#[command(
|
#[command(
|
||||||
@@ -53,8 +57,11 @@ pub enum CliCommand {
|
|||||||
/// Command to run
|
/// Command to run
|
||||||
#[arg(long, short = 'c')]
|
#[arg(long, short = 'c')]
|
||||||
command: String,
|
command: String,
|
||||||
#[arg(long, short = 'i')]
|
/// Output format
|
||||||
|
#[arg(long, short = 'f')]
|
||||||
|
format: OutputFormat,
|
||||||
/// Interval to run the commands at
|
/// Interval to run the commands at
|
||||||
|
#[arg(long, short = 'i')]
|
||||||
interval: f32,
|
interval: f32,
|
||||||
},
|
},
|
||||||
/// Manage devices
|
/// Manage devices
|
||||||
@@ -77,3 +84,11 @@ pub enum CliDeviceCommand {
|
|||||||
#[clap(skip)]
|
#[clap(skip)]
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, clap::ValueEnum)]
|
||||||
|
pub enum OutputFormat {
|
||||||
|
#[default]
|
||||||
|
Csv,
|
||||||
|
Table,
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::net::{IpAddr, Ipv4Addr};
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tabled::Tabled;
|
||||||
|
|
||||||
use crate::config::cli::{CliArgs, CliCommand};
|
use crate::config::cli::{CliArgs, CliCommand};
|
||||||
|
|
||||||
@@ -23,16 +24,32 @@ pub struct Config {
|
|||||||
pub struct ConfigDefaultLogins {
|
pub struct ConfigDefaultLogins {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
pub port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct ConfigHost {
|
pub struct ConfigHost {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub ip: IpAddr,
|
pub ip: IpAddr,
|
||||||
|
pub port: Option<u16>,
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Tabled)]
|
||||||
|
pub struct HostDisplay {
|
||||||
|
#[tabled(rename = "Name")]
|
||||||
|
pub name: String,
|
||||||
|
#[tabled(rename = "IP")]
|
||||||
|
pub ip: IpAddr,
|
||||||
|
#[tabled(rename = "Port")]
|
||||||
|
pub port: u16,
|
||||||
|
#[tabled(rename = "Username")]
|
||||||
|
pub username: String,
|
||||||
|
#[tabled(rename = "Pasword")]
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn parse() -> anyhow::Result<Self> {
|
pub fn parse() -> anyhow::Result<Self> {
|
||||||
let cli = cli::CliArgs::parse();
|
let cli = cli::CliArgs::parse();
|
||||||
@@ -63,6 +80,22 @@ impl ConfigHost {
|
|||||||
&cfg.default_logins.password
|
&cfg.default_logins.password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn port(&self, cfg: &Config) -> u16 {
|
||||||
|
if let Some(port) = self.port {
|
||||||
|
port
|
||||||
|
} else {
|
||||||
|
cfg.default_logins.port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn display_tabled(&self, cfg: &Config) -> HostDisplay {
|
||||||
|
HostDisplay {
|
||||||
|
name: self.name.clone(),
|
||||||
|
ip: self.ip.clone(),
|
||||||
|
port: self.port(&cfg),
|
||||||
|
username: self.username(cfg).clone(),
|
||||||
|
password: self.password(cfg).clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfigHost {
|
impl Default for ConfigHost {
|
||||||
@@ -70,6 +103,7 @@ impl Default for ConfigHost {
|
|||||||
Self {
|
Self {
|
||||||
name: String::default(),
|
name: String::default(),
|
||||||
ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
||||||
|
port: None,
|
||||||
username: None,
|
username: None,
|
||||||
password: None,
|
password: None,
|
||||||
}
|
}
|
||||||
|
|||||||
228
src/main.rs
228
src/main.rs
@@ -1,6 +1,19 @@
|
|||||||
use std::{net::IpAddr, str::FromStr};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
net::IpAddr,
|
||||||
|
str::FromStr,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::config::cli::CliCommand;
|
use clap::builder;
|
||||||
|
use log::LevelFilter;
|
||||||
|
use serde_json::json;
|
||||||
|
use tabled::Table;
|
||||||
|
|
||||||
|
use crate::config::{
|
||||||
|
Config, ConfigHost,
|
||||||
|
cli::{CliCommand, CliDeviceCommand, OutputFormat},
|
||||||
|
};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod mt_commander;
|
mod mt_commander;
|
||||||
@@ -8,7 +21,19 @@ mod mt_commander;
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let cfg = config::Config::parse()?;
|
let cfg = config::Config::parse()?;
|
||||||
dbg!(&cfg);
|
//dbg!(&cfg);
|
||||||
|
|
||||||
|
env_logger::builder()
|
||||||
|
.parse_default_env()
|
||||||
|
.filter_module(
|
||||||
|
"wara",
|
||||||
|
if cfg.cli.verbose {
|
||||||
|
LevelFilter::Debug
|
||||||
|
} else {
|
||||||
|
LevelFilter::Info
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.init();
|
||||||
|
|
||||||
mt_commander::MtCommander::init(cfg.clone())?;
|
mt_commander::MtCommander::init(cfg.clone())?;
|
||||||
|
|
||||||
@@ -17,65 +42,166 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
all,
|
all,
|
||||||
devices,
|
devices,
|
||||||
command,
|
command,
|
||||||
|
format,
|
||||||
} => {
|
} => {
|
||||||
let mut hosts = Vec::new();
|
let hosts = get_devices(devices, *all, &cfg)?;
|
||||||
if *all {
|
let out = mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?;
|
||||||
hosts.clone_from(&cfg.hosts);
|
print_output(&cfg, &out, format)?;
|
||||||
} else {
|
|
||||||
let mut errored = false;
|
|
||||||
for dev in devices {
|
|
||||||
let Ok(ip) = IpAddr::from_str(dev) else {
|
|
||||||
log::error!("Unable to parse {dev} as an IP");
|
|
||||||
errored = true;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::<Vec<_>>();
|
|
||||||
let Some(host) = found_hosts.first() else {
|
|
||||||
log::error!("Could not find host with IP {dev}");
|
|
||||||
errored = true;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
hosts.push((**host).clone());
|
|
||||||
}
|
|
||||||
if errored {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?;
|
|
||||||
}
|
}
|
||||||
CliCommand::Monitor {
|
CliCommand::Monitor {
|
||||||
all,
|
all,
|
||||||
devices,
|
devices,
|
||||||
command,
|
command,
|
||||||
interval,
|
interval,
|
||||||
|
format,
|
||||||
} => {
|
} => {
|
||||||
let mut hosts = Vec::new();
|
let hosts = get_devices(devices, *all, &cfg)?;
|
||||||
if *all {
|
loop {
|
||||||
hosts.clone_from(&cfg.hosts);
|
let out = mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?;
|
||||||
} else {
|
print_output(&cfg, &out, format)?;
|
||||||
let mut errored = false;
|
tokio::time::sleep(Duration::from_secs_f32(*interval)).await;
|
||||||
for dev in devices {
|
|
||||||
let Ok(ip) = IpAddr::from_str(dev) else {
|
|
||||||
log::error!("Unable to parse {dev} as an IP");
|
|
||||||
errored = true;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::<Vec<_>>();
|
|
||||||
let Some(host) = found_hosts.first() else {
|
|
||||||
log::error!("Could not find host with IP {dev}");
|
|
||||||
errored = true;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
hosts.push((**host).clone());
|
|
||||||
}
|
|
||||||
if errored {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mt_commander::MtCommander::run_command_on_hosts(&hosts, command).await?;
|
|
||||||
}
|
}
|
||||||
CliCommand::Device { command } => {}
|
CliCommand::Device { command } => match command {
|
||||||
|
CliDeviceCommand::List => {
|
||||||
|
let devices = cfg.hosts.iter().map(|h| h.display_tabled(&cfg));
|
||||||
|
|
||||||
|
let table = tabled::Table::new(devices);
|
||||||
|
println!("{}", table);
|
||||||
|
}
|
||||||
|
CliDeviceCommand::None => unreachable!(),
|
||||||
|
},
|
||||||
CliCommand::None => unreachable!(),
|
CliCommand::None => unreachable!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_devices(devices: &Vec<String>, all: bool, cfg: &Config) -> anyhow::Result<Vec<ConfigHost>> {
|
||||||
|
let mut hosts = Vec::new();
|
||||||
|
if all {
|
||||||
|
hosts.clone_from(&cfg.hosts);
|
||||||
|
} else {
|
||||||
|
let mut errored = false;
|
||||||
|
for dev in devices {
|
||||||
|
let Ok(ip) = IpAddr::from_str(dev) else {
|
||||||
|
log::error!("Unable to parse {dev} as an IP");
|
||||||
|
errored = true;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let found_hosts = cfg.hosts.iter().filter(|h| h.ip == ip).collect::<Vec<_>>();
|
||||||
|
let Some(host) = found_hosts.first() else {
|
||||||
|
log::error!("Could not find host with IP {dev}");
|
||||||
|
errored = true;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
hosts.push((**host).clone());
|
||||||
|
}
|
||||||
|
if errored {
|
||||||
|
anyhow::bail!("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(hosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_output(
|
||||||
|
cfg: &Config,
|
||||||
|
output: &HashMap<ConfigHost, Vec<HashMap<String, Option<String>>>>,
|
||||||
|
format: &OutputFormat,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match *format {
|
||||||
|
OutputFormat::Csv => {
|
||||||
|
let mut table = Vec::new();
|
||||||
|
let mut wanted_cols = Vec::new();
|
||||||
|
let mut top_row_added = false;
|
||||||
|
for (host, out) in output {
|
||||||
|
for out in out {
|
||||||
|
let out: BTreeMap<_, _> = out.clone().into_iter().collect();
|
||||||
|
if !top_row_added {
|
||||||
|
let mut row = vec!["Name".to_string(), "IP".to_string()];
|
||||||
|
let mut data_cols = out.keys().cloned().collect::<Vec<String>>();
|
||||||
|
wanted_cols = data_cols.clone();
|
||||||
|
row.append(&mut data_cols);
|
||||||
|
|
||||||
|
table.push(row);
|
||||||
|
top_row_added = true;
|
||||||
|
}
|
||||||
|
let mut row = vec![host.name.clone(), host.ip.clone().to_string()];
|
||||||
|
|
||||||
|
for wanted_col in &wanted_cols {
|
||||||
|
let mut val = String::new();
|
||||||
|
if let Some(v) = out.get(wanted_col) {
|
||||||
|
if let Some(v) = v {
|
||||||
|
val = v.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.push(val.clone());
|
||||||
|
}
|
||||||
|
table.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut table_escaped = Vec::new();
|
||||||
|
for line in table {
|
||||||
|
let mut row = Vec::new();
|
||||||
|
for mut item in line {
|
||||||
|
if item.contains(',') {
|
||||||
|
if item.contains('"') {
|
||||||
|
item = item.replace('"', "\"\"");
|
||||||
|
}
|
||||||
|
item = format!("\"{item}\"");
|
||||||
|
}
|
||||||
|
row.push(item);
|
||||||
|
}
|
||||||
|
table_escaped.push(row.join(","));
|
||||||
|
}
|
||||||
|
for row in table_escaped {
|
||||||
|
println!("{row}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutputFormat::Table => {
|
||||||
|
let mut builder = tabled::builder::Builder::default();
|
||||||
|
let mut wanted_cols = Vec::new();
|
||||||
|
let mut top_row_added = false;
|
||||||
|
for (host, out) in output {
|
||||||
|
for out in out {
|
||||||
|
let out: BTreeMap<_, _> = out.clone().into_iter().collect();
|
||||||
|
if !top_row_added {
|
||||||
|
let mut row = vec!["Name".to_string(), "IP".to_string()];
|
||||||
|
let mut data_cols = out.keys().cloned().collect::<Vec<String>>();
|
||||||
|
wanted_cols = data_cols.clone();
|
||||||
|
row.append(&mut data_cols);
|
||||||
|
|
||||||
|
builder.push_record(row);
|
||||||
|
top_row_added = true;
|
||||||
|
}
|
||||||
|
let mut row = vec![host.name.clone(), host.ip.clone().to_string()];
|
||||||
|
for wanted_col in &wanted_cols {
|
||||||
|
let mut val = String::new();
|
||||||
|
if let Some(v) = out.get(wanted_col) {
|
||||||
|
if let Some(v) = v {
|
||||||
|
val = v.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.push(val.clone());
|
||||||
|
}
|
||||||
|
builder.push_record(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let table = builder.build();
|
||||||
|
println!("{}", table);
|
||||||
|
}
|
||||||
|
OutputFormat::Json => {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
|
||||||
|
for (host, out) in output {
|
||||||
|
let val = json!({
|
||||||
|
"host": host.display_tabled(&cfg),
|
||||||
|
"response": out,
|
||||||
|
});
|
||||||
|
res.push(val);
|
||||||
|
}
|
||||||
|
let out_text = serde_json::to_string_pretty(&res)?;
|
||||||
|
println!("{out_text}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ 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, String>> {
|
) -> anyhow::Result<HashMap<ConfigHost, Vec<HashMap<String, Option<String>>>>> {
|
||||||
let mut res_map = HashMap::new();
|
let mut res_map = HashMap::new();
|
||||||
for host in hosts {
|
for host in hosts {
|
||||||
let res = Self::run_command_on_host(host, command).await?;
|
let res = Self::run_command_on_host(host, command).await?;
|
||||||
@@ -37,12 +37,15 @@ impl MtCommander {
|
|||||||
}
|
}
|
||||||
Ok(res_map)
|
Ok(res_map)
|
||||||
}
|
}
|
||||||
pub async fn run_command_on_host(host: &ConfigHost, command: &str) -> anyhow::Result<String> {
|
pub async fn run_command_on_host(
|
||||||
|
host: &ConfigHost,
|
||||||
|
command: &str,
|
||||||
|
) -> 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()))?;
|
||||||
mtc.run_command(host, &command).await
|
mtc.run_command(host, &command).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_command_on_hosts_and_parse_re(
|
/*pub async fn run_command_on_hosts_and_parse_re(
|
||||||
hosts: &[ConfigHost],
|
hosts: &[ConfigHost],
|
||||||
command: &str,
|
command: &str,
|
||||||
parse_re: &Regex,
|
parse_re: &Regex,
|
||||||
@@ -53,9 +56,9 @@ impl MtCommander {
|
|||||||
res_map.insert(host.clone(), res);
|
res_map.insert(host.clone(), res);
|
||||||
}
|
}
|
||||||
Ok(res_map)
|
Ok(res_map)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
pub async fn run_command_on_host_and_parse_re(
|
/*pub async fn run_command_on_host_and_parse_re(
|
||||||
host: &ConfigHost,
|
host: &ConfigHost,
|
||||||
command: &str,
|
command: &str,
|
||||||
parse_re: &Regex,
|
parse_re: &Regex,
|
||||||
@@ -67,7 +70,7 @@ impl MtCommander {
|
|||||||
.map(|s| s.as_str().to_string())
|
.map(|s| s.as_str().to_string())
|
||||||
.collect();
|
.collect();
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MtCommanderInternal {
|
impl MtCommanderInternal {
|
||||||
@@ -95,9 +98,13 @@ impl MtCommanderInternal {
|
|||||||
tokio::spawn(watcher());
|
tokio::spawn(watcher());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn run_command(&self, host: &ConfigHost, cmd: &str) -> anyhow::Result<String> {
|
async fn run_command(
|
||||||
|
&self,
|
||||||
|
host: &ConfigHost,
|
||||||
|
cmd: &str,
|
||||||
|
) -> anyhow::Result<Vec<HashMap<String, Option<String>>>> {
|
||||||
let dev = MikrotikDevice::connect(
|
let dev = MikrotikDevice::connect(
|
||||||
format!("{}", &host.ip),
|
format!("{}:{}", &host.ip, host.port(&self.cfg())),
|
||||||
&host.username(self.cfg()),
|
&host.username(self.cfg()),
|
||||||
Some(&host.password(self.cfg())),
|
Some(&host.password(self.cfg())),
|
||||||
)
|
)
|
||||||
@@ -107,14 +114,17 @@ impl MtCommanderInternal {
|
|||||||
.send_command(CommandBuilder::new().command(cmd).build())
|
.send_command(CommandBuilder::new().command(cmd).build())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut res = String::new();
|
let mut res = Vec::new();
|
||||||
|
|
||||||
while let Some(event) = rx.recv().await {
|
while let Some(event) = rx.recv().await {
|
||||||
match event {
|
match event {
|
||||||
Event::Reply { response, .. } => {
|
Event::Reply { response, .. } => {
|
||||||
log::debug!("({}) {:?}", &host.ip, response.attributes);
|
log::debug!("({}) {:?}", &host.ip, response.attributes);
|
||||||
|
res.push(response.attributes);
|
||||||
|
}
|
||||||
|
Event::Done { .. } => {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Event::Done { .. } => break,
|
|
||||||
Event::Trap { response, .. } => {
|
Event::Trap { response, .. } => {
|
||||||
log::error!("({}) {:?}", &host.ip, response.message);
|
log::error!("({}) {:?}", &host.ip, response.message);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user