mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
commit
bcbda6a262
1
src/VERSION_STRING
Normal file
1
src/VERSION_STRING
Normal file
@ -0,0 +1 @@
|
||||
1.4-rc10-devel
|
354
src/rust/Cargo.lock
generated
354
src/rust/Cargo.lock
generated
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.20.0"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c"
|
||||
checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -49,24 +49,23 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
|
||||
checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@ -88,9 +87,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
@ -98,9 +97,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.72"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
@ -135,18 +134,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.72"
|
||||
version = "0.1.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
|
||||
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -212,9 +211,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.68"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
@ -227,9 +226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.2"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
|
||||
|
||||
[[package]]
|
||||
name = "binascii"
|
||||
@ -265,7 +264,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
"which",
|
||||
]
|
||||
|
||||
@ -346,9 +345,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -430,9 +429,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.21"
|
||||
version = "4.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
|
||||
checksum = "7c8d502cbaec4595d2e7d5f61e318f05417bd2b66fdc3809498f0d3fdf0bea27"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -441,9 +440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.21"
|
||||
version = "4.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
|
||||
checksum = "5891c7bc0edb3e1c2204fc5e94009affabeb1821c9e5fdc3959536c5c0bb984d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -453,21 +452,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.12"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
|
||||
checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@ -709,14 +708,14 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.0"
|
||||
version = "5.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d"
|
||||
checksum = "9b101bb8960ab42ada6ae98eb82afcea4452294294c45b681295af26610d6d28"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.0",
|
||||
@ -744,9 +743,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
|
||||
|
||||
[[package]]
|
||||
name = "devise"
|
||||
@ -778,7 +777,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -843,9 +842,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.32"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@ -871,9 +870,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
||||
checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
@ -930,9 +929,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
|
||||
checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
@ -1024,7 +1023,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1093,9 +1092,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.27.3"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@ -1105,9 +1104,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.20"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
|
||||
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -1182,9 +1181,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
@ -1423,7 +1422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75adb4021282a72ca63ebbc0e4247750ad74ede68ff062d247691072d709ad8b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"num_cpus",
|
||||
"pkg-config",
|
||||
]
|
||||
@ -1462,9 +1461,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "log-once"
|
||||
@ -1515,7 +1514,7 @@ dependencies = [
|
||||
"lqos_config",
|
||||
"lqos_utils",
|
||||
"lts_client",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"thiserror",
|
||||
@ -1573,8 +1572,9 @@ dependencies = [
|
||||
"lqos_bus",
|
||||
"lqos_config",
|
||||
"lqos_utils",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"once_cell",
|
||||
"reqwest",
|
||||
"rocket",
|
||||
"rocket_async_compression",
|
||||
"sysinfo",
|
||||
@ -1587,7 +1587,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"lqos_bus",
|
||||
"lqos_utils",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"pyo3",
|
||||
"sysinfo",
|
||||
"tokio",
|
||||
@ -1638,7 +1638,7 @@ dependencies = [
|
||||
"lqos_bus",
|
||||
"lqos_config",
|
||||
"lqos_utils",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"zerocopy",
|
||||
@ -1650,7 +1650,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"log",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"notify",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@ -1673,7 +1673,7 @@ dependencies = [
|
||||
"lqos_sys",
|
||||
"lqos_utils",
|
||||
"lts_client",
|
||||
"nix",
|
||||
"nix 0.27.1",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"serde",
|
||||
@ -1729,6 +1729,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_json",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -1758,9 +1759,9 @@ checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@ -1901,16 +1902,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.2"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.7.1",
|
||||
"pin-utils",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1979,9 +1990,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.31.1"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
|
||||
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2000,11 +2011,11 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.56"
|
||||
version = "0.10.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e"
|
||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@ -2021,7 +2032,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2032,9 +2043,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.91"
|
||||
version = "0.9.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac"
|
||||
checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@ -2068,7 +2079,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.1",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2097,7 +2108,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2129,14 +2140,14 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -2152,9 +2163,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "platforms"
|
||||
version = "3.0.2"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
|
||||
checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
@ -2197,7 +2208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2217,7 +2228,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
"version_check",
|
||||
"yansi 1.0.0-rc.1",
|
||||
]
|
||||
@ -2284,9 +2295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.32"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -2354,34 +2365,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1"
|
||||
checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa"
|
||||
checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.3"
|
||||
version = "1.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
|
||||
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.3.6",
|
||||
"regex-syntax 0.7.4",
|
||||
"regex-automata 0.3.7",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2395,13 +2406,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
|
||||
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.4",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2412,15 +2423,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.18"
|
||||
version = "0.11.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
|
||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@ -2541,7 +2552,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rocket_http",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@ -2596,9 +2607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.8"
|
||||
version = "0.38.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
@ -2689,9 +2700,9 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.183"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -2708,20 +2719,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.183"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.104"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -2817,9 +2828,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@ -2904,12 +2915,6 @@ dependencies = [
|
||||
"loom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@ -2935,9 +2940,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.28"
|
||||
version = "2.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2952,9 +2957,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.29.7"
|
||||
version = "0.29.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "165d6d8539689e3d3bc8b98ac59541e1f21c7de7c85d60dc80e43ae0ed2113db"
|
||||
checksum = "a8d0e9cc2273cc8d31377bdd638d72e3ac3e5607b18621062b169d02787f1bab"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@ -2994,9 +2999,9 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.7.1"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
@ -3016,22 +3021,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.44"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.44"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3046,9 +3051,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.25"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||
checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@ -3065,9 +3070,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.11"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
|
||||
checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
@ -3099,9 +3104,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.30.0"
|
||||
version = "1.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd"
|
||||
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@ -3124,7 +3129,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3245,7 +3250,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3388,9 +3393,9 @@ checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@ -3478,7 +3483,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -3512,7 +3517,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -3581,7 +3586,7 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.1",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3599,7 +3604,7 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.1",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3619,17 +3624,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.1"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3640,9 +3645,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@ -3652,9 +3657,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@ -3664,9 +3669,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@ -3676,9 +3681,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@ -3688,9 +3693,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@ -3700,9 +3705,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@ -3712,26 +3717,27 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.9"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4344c9f03e6918ce61d94ea6b0500964bb42ee9ca9b2c9c8931990e20b481144"
|
||||
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3784,7 +3790,7 @@ checksum = "8f7f3a471f98d0a61c34322fbbfd10c384b07687f680d4119813713f72308d91"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3804,5 +3810,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
@ -15,7 +15,7 @@ thiserror = "1"
|
||||
lqos_config = { path = "../lqos_config" }
|
||||
lqos_utils = { path = "../lqos_utils" }
|
||||
lts_client = { path = "../lts_client" }
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
log = "0"
|
||||
nix = "0"
|
||||
serde_cbor = "0" # For RFC8949/7409 format C binary objects
|
||||
|
@ -141,7 +141,7 @@ impl WebUsers {
|
||||
role: UserRole,
|
||||
) -> Result<String, AuthenticationError> {
|
||||
let token; // Assigned in a branch
|
||||
if let Some(mut user) =
|
||||
if let Some(user) =
|
||||
self.users.iter_mut().find(|u| u.username == username)
|
||||
{
|
||||
user.password_hash = Self::hash_password(password);
|
||||
|
@ -211,6 +211,55 @@ impl EtcLqos {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run this if you've received the OK from the licensing server, and been
|
||||
/// sent a license key. This appends a [long_term_stats] section to your
|
||||
/// config file - ONLY if one doesn't already exist.
|
||||
pub fn enable_long_term_stats(license_key: String) {
|
||||
if let Ok(raw) = std::fs::read_to_string("/etc/lqos.conf") {
|
||||
let document = raw.parse::<Document>();
|
||||
match document {
|
||||
Err(e) => {
|
||||
error!("Unable to parse TOML from /etc/lqos.conf");
|
||||
error!("Full error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
Ok(mut config_doc) => {
|
||||
let cfg = toml_edit::de::from_document::<EtcLqos>(config_doc.clone());
|
||||
match cfg {
|
||||
Ok(cfg) => {
|
||||
// Now we enable LTS if its not present
|
||||
if let Ok(isp_config) = crate::LibreQoSConfig::load() {
|
||||
if cfg.long_term_stats.is_none() {
|
||||
|
||||
let mut new_section = toml_edit::table();
|
||||
new_section["gather_stats"] = value(true);
|
||||
new_section["collation_period_seconds"] = value(60);
|
||||
new_section["license_key"] = value(license_key);
|
||||
if isp_config.automatic_import_uisp {
|
||||
new_section["uisp_reporting_interval_seconds"] = value(300);
|
||||
}
|
||||
config_doc["long_term_stats"] = new_section;
|
||||
|
||||
let new_cfg = config_doc.to_string();
|
||||
if let Err(e) = fs::write(Path::new("/etc/lqos.conf"), new_cfg) {
|
||||
log::error!("Unable to write to /etc/lqos.conf");
|
||||
log::error!("{e:?}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Unable to parse TOML from /etc/lqos.conf");
|
||||
error!("Full error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_config(cfg_doc: &mut Document, cfg: &mut EtcLqos) {
|
||||
use sha2::digest::Update;
|
||||
use sha2::Digest;
|
||||
|
@ -14,7 +14,7 @@ mod program_control;
|
||||
mod shaped_devices;
|
||||
|
||||
pub use authentication::{UserRole, WebUsers};
|
||||
pub use etc::{BridgeConfig, BridgeInterface, BridgeVlan, EtcLqos, Tunables};
|
||||
pub use etc::{BridgeConfig, BridgeInterface, BridgeVlan, EtcLqos, Tunables, enable_long_term_stats};
|
||||
pub use libre_qos_config::LibreQoSConfig;
|
||||
pub use network_json::{NetworkJson, NetworkJsonNode, NetworkJsonTransport};
|
||||
pub use program_control::load_libreqos;
|
||||
|
@ -21,6 +21,7 @@ nix = "0"
|
||||
once_cell = "1"
|
||||
dns-lookup = "1"
|
||||
dashmap = "5"
|
||||
reqwest = { version = "0.11.20", features = ["json"] }
|
||||
|
||||
# Support JemAlloc on supported platforms
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
|
7
src/rust/lqos_node_manager/build.rs
Normal file
7
src/rust/lqos_node_manager/build.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use std::process::Command;
|
||||
fn main() {
|
||||
// Adds a git commit hash to the program
|
||||
let output = Command::new("git").args(["rev-parse", "HEAD"]).output().unwrap();
|
||||
let git_hash = String::from_utf8(output.stdout).unwrap();
|
||||
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
|
||||
}
|
@ -11,6 +11,7 @@ mod auth_guard;
|
||||
mod config_control;
|
||||
mod network_tree;
|
||||
mod queue_info;
|
||||
mod toasts;
|
||||
|
||||
// Use JemAllocator only on supported platforms
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
@ -105,6 +106,9 @@ fn rocket() -> _ {
|
||||
static_pages::fontawesome_solid,
|
||||
static_pages::fontawesome_webfont,
|
||||
static_pages::fontawesome_woff,
|
||||
// Front page toast checks
|
||||
toasts::version_check,
|
||||
toasts::stats_check,
|
||||
],
|
||||
);
|
||||
|
||||
|
113
src/rust/lqos_node_manager/src/toasts.rs
Normal file
113
src/rust/lqos_node_manager/src/toasts.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use lqos_config::EtcLqos;
|
||||
use lqos_utils::unix_time::unix_now;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
|
||||
static LAST_VERSION_CHECK: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||
const ONE_HOUR_SECONDS: u64 = 60 * 60;
|
||||
const VERSION_STRING: &str = include_str!("../../../VERSION_STRING");
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct VersionCheckRequest {
|
||||
current_git_hash: String,
|
||||
version_string: String,
|
||||
node_id: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct VersionCheckResponse {
|
||||
update_available: bool,
|
||||
}
|
||||
|
||||
async fn send_version_check() -> anyhow::Result<VersionCheckResponse> {
|
||||
if let Ok(cfg) = EtcLqos::load() {
|
||||
let current_hash = env!("GIT_HASH");
|
||||
let request = VersionCheckRequest {
|
||||
current_git_hash: current_hash.to_string(),
|
||||
version_string: VERSION_STRING.to_string(),
|
||||
node_id: cfg.node_id.unwrap_or("(not configured)".to_string()),
|
||||
};
|
||||
let response = reqwest::Client::new()
|
||||
.post("https://stats.libreqos.io/api/version_check")
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
} else {
|
||||
anyhow::bail!("No config");
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/version_check")]
|
||||
pub async fn version_check() -> Json<String> {
|
||||
let last_check = LAST_VERSION_CHECK.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if let Ok(now) = unix_now() {
|
||||
if now > last_check + ONE_HOUR_SECONDS {
|
||||
let res = send_version_check().await;
|
||||
if let Ok(response) = send_version_check().await {
|
||||
LAST_VERSION_CHECK.store(now, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
if response.update_available {
|
||||
return Json(String::from("Update available"));
|
||||
}
|
||||
} else {
|
||||
error!("Unable to send version check");
|
||||
error!("{res:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Json(String::from("All Good"))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub enum StatsCheckResponse {
|
||||
DoNothing,
|
||||
NotSetup,
|
||||
Disabled,
|
||||
GoodToGo,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct StatsCheckAction {
|
||||
action: StatsCheckResponse,
|
||||
node_id: String,
|
||||
}
|
||||
|
||||
#[get("/api/stats_check")]
|
||||
pub async fn stats_check() -> Json<StatsCheckAction> {
|
||||
let mut response = StatsCheckAction {
|
||||
action: StatsCheckResponse::DoNothing,
|
||||
node_id: String::new(),
|
||||
};
|
||||
|
||||
if let Ok(cfg) = EtcLqos::load() {
|
||||
if let Some(lts) = &cfg.long_term_stats {
|
||||
if !lts.gather_stats {
|
||||
response = StatsCheckAction {
|
||||
action: StatsCheckResponse::Disabled,
|
||||
node_id: cfg.node_id.unwrap_or("(not configured)".to_string()),
|
||||
};
|
||||
} else {
|
||||
// Stats are enabled
|
||||
response = StatsCheckAction {
|
||||
action: StatsCheckResponse::GoodToGo,
|
||||
node_id: cfg.node_id.unwrap_or("(not configured)".to_string()),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
response = StatsCheckAction {
|
||||
action: StatsCheckResponse::NotSetup,
|
||||
node_id: cfg.node_id.unwrap_or("(not configured)".to_string()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Json(response)
|
||||
}
|
@ -46,10 +46,7 @@
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth
|
||||
Test</a>
|
||||
</li>
|
||||
<li class="nav-item" id="statsLink"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -37,9 +37,7 @@
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth Test</a>
|
||||
</li>
|
||||
<li class="nav-item" id="statsLink"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -160,8 +160,28 @@ function updateHostCounts() {
|
||||
}
|
||||
$("#currentLogin").html(html);
|
||||
});
|
||||
$("#startTest").on('click', () => {
|
||||
/*$("#startTest").on('click', () => {
|
||||
$.get("/api/run_btest", () => { });
|
||||
});*/
|
||||
// LTS Check
|
||||
$.get("/api/stats_check", (data) => {
|
||||
console.log(data);
|
||||
let template = "<a class='nav-link' href='$URL$'><i class='fa fa-dashboard'></i> $TEXT$</a>";
|
||||
switch (data.action) {
|
||||
case "Disabled": {
|
||||
template = template.replace("$URL$", "#")
|
||||
.replace("$TEXT$", "<span style='color: red'>Stats Disabled</span>");
|
||||
}
|
||||
case "NotSetup": {
|
||||
template = template.replace("$URL$", "https://stats.libreqos.io/trial1/" + encodeURI(data.node_id))
|
||||
.replace("$TEXT$", "<span class='badge badge-pill badge-success green-badge'>Statistics Free Trial</span>");
|
||||
} break;
|
||||
default: {
|
||||
template = template.replace("$URL$", "https://stats.libreqos.io/")
|
||||
.replace("$TEXT$", "Statistics");
|
||||
}
|
||||
}
|
||||
$("#statsLink").html(template);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,7 @@
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth
|
||||
Test</a>
|
||||
</li>
|
||||
<li class="nav-item" id="statsLink"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
@ -61,6 +58,8 @@
|
||||
|
||||
<div id="container" class="pad4">
|
||||
|
||||
<div id="toasts"></div>
|
||||
|
||||
<!-- Dashboard Row 1 -->
|
||||
<div class="row mbot8">
|
||||
<!-- THROUGHPUT -->
|
||||
@ -351,6 +350,17 @@
|
||||
updateHostCounts();
|
||||
updateSiteFunnel();
|
||||
OneSecondCadence();
|
||||
|
||||
// Version Check
|
||||
$.get("/api/version_check", (data) => {
|
||||
if (data != "All Good") {
|
||||
let html = "<div class='alert alert-info alert-dismissible fade show' role='alert'>";
|
||||
html += "<strong>LibreQoS Update Available!</strong>";
|
||||
html += "<button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button>";
|
||||
html += "</div>";
|
||||
$("#toasts").append(html);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(start);
|
||||
|
@ -37,9 +37,7 @@
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth Test</a>
|
||||
</li>
|
||||
<li class="nav-item" id="statsLink"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -44,10 +44,7 @@
|
||||
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item" id="currentLogin"></li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="startTest"><i class="fa fa-flag-checkered"></i> Run Bandwidth
|
||||
Test</a>
|
||||
</li>
|
||||
<li class="nav-item" id="statsLink"></li>
|
||||
<li class="nav-item ms-auto">
|
||||
<a class="nav-link" href="/config"><i class="fa fa-gear"></i> Configuration</a>
|
||||
</li>
|
||||
|
@ -12,7 +12,7 @@ crate-type = ["cdylib"]
|
||||
pyo3 = "0"
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
lqos_utils = { path = "../lqos_utils" }
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
anyhow = "1"
|
||||
sysinfo = "0"
|
||||
nix = "0"
|
||||
|
@ -102,7 +102,8 @@ pub(crate) fn xps_setup_default_disable(interface: &str) -> Result<()> {
|
||||
fn sorted_txq_xps_cpus(interface: &str) -> Result<Vec<String>> {
|
||||
let mut result = Vec::new();
|
||||
let paths =
|
||||
std::fs::read_dir(&format!("/sys/class/net/{interface}/queues/"))?;
|
||||
std::fs::read_dir(&format!("/sys/class/net/{interface}/queues/"))
|
||||
.map_err(|_| anyhow::anyhow!("/sys/class/net/{interface}/queues/ does not exist. Does this card only support one queue (not supported)?"))?;
|
||||
for path in paths {
|
||||
if let Ok(path) = &path {
|
||||
if path.path().is_dir() {
|
||||
|
@ -6,7 +6,7 @@ license = "GPL-2.0-only"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
nix = "0"
|
||||
nix = { version = "0", features = ["time"] }
|
||||
log = "0"
|
||||
notify = { version = "5.0.0", default-features = false } # Not using crossbeam because of Tokio
|
||||
thiserror = "1"
|
||||
|
@ -44,7 +44,7 @@ pub(crate) fn get_nic_info() -> anyhow::Result<Vec<Nic>> {
|
||||
current_nic = Some(Nic::default());
|
||||
}
|
||||
|
||||
if let Some(mut nic) = current_nic.as_mut() {
|
||||
if let Some(nic) = current_nic.as_mut() {
|
||||
if let Some(d) = trimmed.strip_prefix("description: ") {
|
||||
nic.description = d.to_string();
|
||||
}
|
||||
|
@ -5,6 +5,6 @@ edition = "2021"
|
||||
license = "GPL-2.0-only"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
anyhow = "1"
|
||||
lqos_bus = { path = "../lqos_bus" }
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "GPL-2.0-only"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
lqos_utils = { path = "../lqos_utils" }
|
||||
anyhow = "1"
|
||||
|
@ -12,7 +12,7 @@ uisp = { path = "../uisp" }
|
||||
dryoc = { version = "0.5", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
serde_cbor = "0" # For RFC8949/7409 format C binary objects
|
||||
log = "0"
|
||||
bincode = "1"
|
||||
@ -21,3 +21,4 @@ sysinfo = "0"
|
||||
num-traits = "0.2"
|
||||
miniz_oxide = "0.7.1"
|
||||
dashmap = "5.4"
|
||||
serde_json = "1"
|
@ -112,8 +112,14 @@ pub(crate) async fn collate_stats(comm_tx: Sender<SenderChannelMessage>) {
|
||||
tree_entries.push(n);
|
||||
}
|
||||
|
||||
// Add to the submissions queue
|
||||
// Obtain the CPU/RAM utilization
|
||||
let (cpu, ram) = system_stats::get_cpu_ram().await;
|
||||
|
||||
// Obtain queue stats
|
||||
let cake_stats = super::update_cake_stats().await;
|
||||
|
||||
|
||||
// Add to the submissions queue
|
||||
new_submission(StatsSubmission {
|
||||
timestamp,
|
||||
totals: Some(StatsTotals {
|
||||
@ -138,6 +144,7 @@ pub(crate) async fn collate_stats(comm_tx: Sender<SenderChannelMessage>) {
|
||||
hosts: Some(stats_hosts),
|
||||
tree: Some(tree_entries),
|
||||
uisp_devices: None,
|
||||
cake_stats,
|
||||
}, comm_tx).await;
|
||||
|
||||
// Clear the collection buffer
|
||||
|
@ -35,10 +35,16 @@ pub async fn start_long_term_stats() -> Sender<StatsUpdateMessage> {
|
||||
}
|
||||
|
||||
async fn collation_scheduler(tx: Sender<StatsUpdateMessage>) {
|
||||
log::info!("Starting collation scheduler");
|
||||
loop {
|
||||
let collation_period = get_collation_period();
|
||||
tx.send(StatsUpdateMessage::CollationTime).await.unwrap();
|
||||
log::info!("Collation period: {}s", collation_period.as_secs());
|
||||
if tx.send(StatsUpdateMessage::CollationTime).await.is_err() {
|
||||
log::warn!("Unable to send collation time message");
|
||||
}
|
||||
log::info!("Sent collation time message. Sleeping.");
|
||||
tokio::time::sleep(collation_period).await;
|
||||
log::info!("Collation scheduler woke up.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,11 +113,20 @@ fn get_uisp_collation_period() -> Option<Duration> {
|
||||
}
|
||||
|
||||
async fn uisp_collection_manager(control_tx: Sender<StatsUpdateMessage>) {
|
||||
if let Some(period) = get_uisp_collation_period() {
|
||||
log::info!("Starting UISP poller with period {:?}", period);
|
||||
loop {
|
||||
control_tx.send(StatsUpdateMessage::UispCollationTime).await.unwrap();
|
||||
tokio::time::sleep(period).await;
|
||||
// Outer loop: If UISP is disabled, check hourly to see if it
|
||||
// was enabled. If it is enabled, start the inner loop.
|
||||
loop {
|
||||
// Inner loop - if there's a collation period set for UISP,
|
||||
// poll it.
|
||||
if let Some(period) = get_uisp_collation_period() {
|
||||
log::info!("Starting UISP poller with period {:?}", period);
|
||||
loop {
|
||||
control_tx.send(StatsUpdateMessage::UispCollationTime).await.unwrap();
|
||||
tokio::time::sleep(period).await;
|
||||
}
|
||||
} else {
|
||||
// Sleep for one hour - then we'll check again
|
||||
tokio::time::sleep(Duration::from_secs(3600)).await;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,11 @@ mod throughput_summary;
|
||||
mod collation;
|
||||
mod network_tree;
|
||||
mod uisp_ext;
|
||||
mod quick_drops;
|
||||
pub use stats_availability::StatsUpdateMessage;
|
||||
pub use collection_manager::start_long_term_stats;
|
||||
pub use throughput_summary::{ThroughputSummary, HostSummary};
|
||||
pub(crate) use collation::SESSION_BUFFER;
|
||||
pub use network_tree::NetworkTreeEntry;
|
||||
pub(crate) use quick_drops::*;
|
||||
pub use quick_drops::CakeStats;
|
@ -17,16 +17,20 @@ impl From<&NetworkJsonNode> for NetworkTreeEntry {
|
||||
let mut min = if value.rtts.is_empty() {
|
||||
0
|
||||
} else {
|
||||
u16::MAX
|
||||
u64::MAX
|
||||
};
|
||||
let mut sum = 0;
|
||||
let mut sum: u64 = 0;
|
||||
let mut count = 0;
|
||||
for n in value.rtts.iter() {
|
||||
let n = *n;
|
||||
sum += n;
|
||||
if n < min { min = n; }
|
||||
if n > max { max = n; }
|
||||
let n = *n as u64;
|
||||
if n > 0 {
|
||||
sum += n;
|
||||
if n < min { min = n; }
|
||||
if n > max { max = n; }
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
let avg = sum.checked_div(value.rtts.len() as u16).unwrap_or(0);
|
||||
let avg = sum.checked_div(count).unwrap_or(0);
|
||||
|
||||
Self {
|
||||
name: value.name.clone(),
|
||||
@ -38,7 +42,7 @@ impl From<&NetworkJsonNode> for NetworkTreeEntry {
|
||||
value.current_throughput.1.load(std::sync::atomic::Ordering::Relaxed) as u32,
|
||||
),
|
||||
node_type: value.node_type.clone(),
|
||||
rtts: (min, max, avg),
|
||||
rtts: (min as u16, max as u16, avg as u16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/rust/lts_client/src/collector/quick_drops/mod.rs
Normal file
17
src/rust/lts_client/src/collector/quick_drops/mod.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! Provides a quick'n'dirty 10 second snapshot of the TC queue
|
||||
//! status. This is used by the LTS system to provide a quick'n'dirty
|
||||
//! summary of drops and marks for the last 10 seconds.
|
||||
|
||||
mod queue_structure;
|
||||
mod retriever;
|
||||
mod stats_diff;
|
||||
pub(crate) use retriever::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
pub(crate) use stats_diff::*;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct CakeStats {
|
||||
pub circuit_id: String,
|
||||
pub drops: u64,
|
||||
pub marks: u64,
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
mod tc_handle;
|
||||
mod queue_network;
|
||||
mod queue_node;
|
||||
use log::error;
|
||||
use queue_network::QueueNetwork;
|
||||
use thiserror::Error;
|
||||
pub(crate) use queue_node::QueueNode;
|
||||
|
||||
pub(crate) fn read_queueing_structure(
|
||||
) -> Result<Vec<QueueNode>, QueueStructureError> {
|
||||
// Note: the ? is allowed because the sub-types return a QueueStructureError and handle logging.
|
||||
let network = QueueNetwork::from_json()?;
|
||||
let flattened = network.to_flat();
|
||||
Ok(flattened)
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum QueueStructureError {
|
||||
#[error("unable to parse u64")]
|
||||
U64Parse(String),
|
||||
#[error("Unable to retrieve string from JSON")]
|
||||
StringParse(String),
|
||||
#[error("Unable to convert string to TC Handle")]
|
||||
TcHandle(String),
|
||||
#[error("Unable to convert string to u32 via hex")]
|
||||
HexParse(String),
|
||||
#[error("Error reading child circuit")]
|
||||
Circuit,
|
||||
#[error("Error reading child device")]
|
||||
Device,
|
||||
#[error("Error reading child's children")]
|
||||
Children,
|
||||
#[error("Unable to read configuration from /etc/lqos.conf")]
|
||||
LqosConf,
|
||||
#[error("Unable to access queueingStructure.json")]
|
||||
FileNotFound,
|
||||
#[error("Unable to read JSON")]
|
||||
JsonError,
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
use super::{queue_node::QueueNode, QueueStructureError};
|
||||
use log::error;
|
||||
use lqos_config::EtcLqos;
|
||||
use serde_json::Value;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct QueueNetwork {
|
||||
pub(crate) cpu_node: Vec<QueueNode>,
|
||||
}
|
||||
|
||||
impl QueueNetwork {
|
||||
pub fn path() -> Result<PathBuf, QueueStructureError> {
|
||||
let cfg = EtcLqos::load();
|
||||
if cfg.is_err() {
|
||||
error!("unable to read /etc/lqos.conf");
|
||||
return Err(QueueStructureError::LqosConf);
|
||||
}
|
||||
let cfg = cfg.unwrap();
|
||||
let base_path = Path::new(&cfg.lqos_directory);
|
||||
Ok(base_path.join("queuingStructure.json"))
|
||||
}
|
||||
|
||||
fn exists() -> bool {
|
||||
if let Ok(path) = QueueNetwork::path() {
|
||||
path.exists()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_json() -> Result<Self, QueueStructureError> {
|
||||
let path = QueueNetwork::path()?;
|
||||
if !QueueNetwork::exists() {
|
||||
error!("queueStructure.json does not exist yet. Try running LibreQoS?");
|
||||
return Err(QueueStructureError::FileNotFound);
|
||||
}
|
||||
let raw_string = std::fs::read_to_string(path)
|
||||
.map_err(|_| QueueStructureError::FileNotFound)?;
|
||||
let mut result = Self { cpu_node: Vec::new() };
|
||||
let json: Value = serde_json::from_str(&raw_string)
|
||||
.map_err(|_| QueueStructureError::FileNotFound)?;
|
||||
if let Value::Object(map) = &json {
|
||||
if let Some(network) = map.get("Network") {
|
||||
if let Value::Object(map) = network {
|
||||
for (key, value) in map.iter() {
|
||||
result.cpu_node.push(QueueNode::from_json(key, value)?);
|
||||
}
|
||||
} else {
|
||||
error!("Unable to parse JSON for queueStructure");
|
||||
return Err(QueueStructureError::JsonError);
|
||||
}
|
||||
} else {
|
||||
error!("Unable to parse JSON for queueStructure");
|
||||
return Err(QueueStructureError::JsonError);
|
||||
}
|
||||
} else {
|
||||
error!("Unable to parse JSON for queueStructure");
|
||||
return Err(QueueStructureError::JsonError);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn to_flat(&self) -> Vec<QueueNode> {
|
||||
let mut result = Vec::new();
|
||||
for cpu in self.cpu_node.iter() {
|
||||
result.push(cpu.clone());
|
||||
let children = cpu.to_flat();
|
||||
result.extend_from_slice(&children);
|
||||
}
|
||||
for c in result.iter_mut() {
|
||||
c.circuits.clear();
|
||||
c.devices.clear();
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
use super::{QueueStructureError, tc_handle::TcHandle};
|
||||
use log::error;
|
||||
use lqos_utils::hex_string::read_hex_string;
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct QueueNode {
|
||||
pub download_bandwidth_mbps: u64,
|
||||
pub upload_bandwidth_mbps: u64,
|
||||
pub download_bandwidth_mbps_min: u64,
|
||||
pub upload_bandwidth_mbps_min: u64,
|
||||
pub class_id: TcHandle,
|
||||
pub up_class_id: TcHandle,
|
||||
pub parent_class_id: TcHandle,
|
||||
pub up_parent_class_id: TcHandle,
|
||||
pub class_major: u32,
|
||||
pub up_class_major: u32,
|
||||
pub class_minor: u32,
|
||||
pub cpu_num: u32,
|
||||
pub up_cpu_num: u32,
|
||||
pub circuits: Vec<QueueNode>,
|
||||
pub circuit_id: Option<String>,
|
||||
pub circuit_name: Option<String>,
|
||||
pub parent_node: Option<String>,
|
||||
pub devices: Vec<QueueNode>,
|
||||
pub comment: String,
|
||||
pub device_id: Option<String>,
|
||||
pub device_name: Option<String>,
|
||||
pub mac: Option<String>,
|
||||
pub children: Vec<QueueNode>,
|
||||
}
|
||||
|
||||
/// Provides a convenient wrapper that attempts to decode a u64 from a JSON
|
||||
/// value, and returns an error if decoding fails.
|
||||
macro_rules! grab_u64 {
|
||||
($target: expr, $key: expr, $value: expr) => {
|
||||
let tmp = $value.as_u64().ok_or(QueueStructureError::U64Parse(format!("{} => {:?}", $key, $value)));
|
||||
match tmp {
|
||||
Err(e) => {
|
||||
error!("Error decoding JSON. Key: {}, Value: {:?} is not readily convertible to a u64.", $key, $value);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(data) => $target = data,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Provides a macro to safely unwrap TC Handles and issue an error if they didn't parse
|
||||
/// correctly.
|
||||
macro_rules! grab_tc_handle {
|
||||
($target: expr, $key: expr, $value: expr) => {
|
||||
let s = $value.as_str();
|
||||
if s.is_none() {
|
||||
error!("Unable to parse {:?} as a string from JSON", s);
|
||||
return Err(QueueStructureError::StringParse(format!("{:?}", $value)));
|
||||
}
|
||||
let s = s.unwrap();
|
||||
let tmp = TcHandle::from_string(s);
|
||||
if tmp.is_err() {
|
||||
error!("Unable to parse {:?} as a TC Handle", s);
|
||||
return Err(QueueStructureError::TcHandle(format!("{:?}", tmp)));
|
||||
}
|
||||
$target = tmp.unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to convert hex strings (e.g. 0xff) to a u32
|
||||
macro_rules! grab_hex {
|
||||
($target: expr, $key: expr, $value: expr) => {
|
||||
let s = $value.as_str();
|
||||
if s.is_none() {
|
||||
error!("Unable to parse {:?} as a string from JSON", $value);
|
||||
return Err(QueueStructureError::StringParse(format!("{:?}", s)));
|
||||
}
|
||||
let s = s.unwrap();
|
||||
let tmp = read_hex_string(s);
|
||||
if tmp.is_err() {
|
||||
error!("Unable to parse {:?} as a hex string", $value);
|
||||
return Err(QueueStructureError::HexParse(format!("{:?}", tmp)));
|
||||
}
|
||||
$target = tmp.unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to extract an option<string>
|
||||
macro_rules! grab_string_option {
|
||||
($target: expr, $key: expr, $value: expr) => {
|
||||
let s = $value.as_str();
|
||||
if s.is_none() {
|
||||
error!("Unable to parse {:?} as a string from JSON", $value);
|
||||
return Err(QueueStructureError::StringParse(format!("{:?}", s)));
|
||||
}
|
||||
$target = Some(s.unwrap().to_string());
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to extract a string
|
||||
macro_rules! grab_string {
|
||||
($target: expr, $key: expr, $value: expr) => {
|
||||
let s = $value.as_str();
|
||||
if s.is_none() {
|
||||
error!("Unable to parse {:?} as a string from JSON", $value);
|
||||
return Err(QueueStructureError::StringParse(format!("{:?}", s)));
|
||||
}
|
||||
$target = s.unwrap().to_string();
|
||||
};
|
||||
}
|
||||
|
||||
impl QueueNode {
|
||||
pub(crate) fn from_json(
|
||||
key: &str,
|
||||
value: &Value,
|
||||
) -> Result<Self, QueueStructureError> {
|
||||
let mut result = Self::default();
|
||||
if let Value::Object(map) = value {
|
||||
for (key, value) in map.iter() {
|
||||
match key.as_str() {
|
||||
"downloadBandwidthMbps" | "maxDownload" => {
|
||||
grab_u64!(result.download_bandwidth_mbps, key.as_str(), value);
|
||||
}
|
||||
"uploadBandwidthMbps" | "maxUpload" => {
|
||||
grab_u64!(result.upload_bandwidth_mbps, key.as_str(), value);
|
||||
}
|
||||
"downloadBandwidthMbpsMin" | "minDownload" => {
|
||||
grab_u64!(result.download_bandwidth_mbps_min, key.as_str(), value);
|
||||
}
|
||||
"uploadBandwidthMbpsMin" | "minUpload" => {
|
||||
grab_u64!(result.upload_bandwidth_mbps_min, key.as_str(), value);
|
||||
}
|
||||
"classid" => {
|
||||
grab_tc_handle!(result.class_id, key.as_str(), value);
|
||||
}
|
||||
"up_classid" => {
|
||||
grab_tc_handle!(result.up_class_id, key.as_str(), value);
|
||||
}
|
||||
"classMajor" => {
|
||||
grab_hex!(result.class_major, key.as_str(), value);
|
||||
}
|
||||
"up_classMajor" => {
|
||||
grab_hex!(result.up_class_major, key.as_str(), value);
|
||||
}
|
||||
"classMinor" => {
|
||||
grab_hex!(result.class_minor, key.as_str(), value);
|
||||
}
|
||||
"cpuNum" => {
|
||||
grab_hex!(result.cpu_num, key.as_str(), value);
|
||||
}
|
||||
"up_cpuNum" => {
|
||||
grab_hex!(result.up_cpu_num, key.as_str(), value);
|
||||
}
|
||||
"parentClassID" => {
|
||||
grab_tc_handle!(result.parent_class_id, key.as_str(), value);
|
||||
}
|
||||
"up_parentClassID" => {
|
||||
grab_tc_handle!(result.up_parent_class_id, key.as_str(), value);
|
||||
}
|
||||
"circuitId" | "circuitID" => {
|
||||
grab_string_option!(result.circuit_id, key.as_str(), value);
|
||||
}
|
||||
"circuitName" => {
|
||||
grab_string_option!(result.circuit_name, key.as_str(), value);
|
||||
}
|
||||
"parentNode" | "ParentNode" => {
|
||||
grab_string_option!(result.parent_node, key.as_str(), value);
|
||||
}
|
||||
"comment" => {
|
||||
grab_string!(result.comment, key.as_str(), value);
|
||||
}
|
||||
"deviceId" | "deviceID" => {
|
||||
grab_string_option!(result.device_id, key.as_str(), value);
|
||||
}
|
||||
"deviceName" => {
|
||||
grab_string_option!(result.device_name, key.as_str(), value);
|
||||
}
|
||||
"mac" => {
|
||||
grab_string_option!(result.mac, key.as_str(), value);
|
||||
}
|
||||
"ipv4s" => {} // Ignore
|
||||
"ipv6s" => {}
|
||||
"circuits" => {
|
||||
if let Value::Array(array) = value {
|
||||
for c in array.iter() {
|
||||
let n = QueueNode::from_json(key, c);
|
||||
if n.is_err() {
|
||||
error!("Unable to read circuit children");
|
||||
error!("{:?}", n);
|
||||
return Err(QueueStructureError::Circuit);
|
||||
}
|
||||
result.circuits.push(n.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
"devices" => {
|
||||
if let Value::Array(array) = value {
|
||||
for c in array.iter() {
|
||||
let n = QueueNode::from_json(key, c);
|
||||
if n.is_err() {
|
||||
error!("Unable to read device children");
|
||||
error!("{:?}", n);
|
||||
return Err(QueueStructureError::Device);
|
||||
}
|
||||
result.devices.push(n.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
"children" => {
|
||||
if let Value::Object(map) = value {
|
||||
for (key, c) in map.iter() {
|
||||
let n = QueueNode::from_json(key, c);
|
||||
if n.is_err() {
|
||||
error!("Unable to read children. Don't worry, we all feel that way sometimes.");
|
||||
error!("{:?}", n);
|
||||
return Err(QueueStructureError::Children);
|
||||
}
|
||||
result.circuits.push(n.unwrap());
|
||||
}
|
||||
} else {
|
||||
log::warn!("Children was not an object");
|
||||
log::warn!("{:?}", value);
|
||||
}
|
||||
}
|
||||
"idForCircuitsWithoutParentNodes" | "type" => {
|
||||
// Ignore
|
||||
}
|
||||
_ => log::error!("I don't know how to parse key: [{key}]"),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("Unable to parse node structure for [{key}]");
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn to_flat(&self) -> Vec<QueueNode> {
|
||||
let mut result = Vec::new();
|
||||
for c in self.circuits.iter() {
|
||||
result.push(c.clone());
|
||||
let children = c.to_flat();
|
||||
result.extend_from_slice(&children);
|
||||
}
|
||||
for c in self.devices.iter() {
|
||||
result.push(c.clone());
|
||||
let children = c.to_flat();
|
||||
result.extend_from_slice(&children);
|
||||
}
|
||||
for c in self.children.iter() {
|
||||
result.push(c.clone());
|
||||
let children = c.to_flat();
|
||||
result.extend_from_slice(&children);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
use log::error;
|
||||
use lqos_utils::hex_string::read_hex_string;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Provides consistent handling of TC handle types.
|
||||
#[derive(
|
||||
Copy, Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq, Hash
|
||||
)]
|
||||
pub struct TcHandle(u32);
|
||||
|
||||
const TC_H_ROOT: u32 = 4294967295;
|
||||
const TC_H_UNSPEC: u32 = 0;
|
||||
|
||||
impl TcHandle {
|
||||
/// Returns the TC handle as two values, indicating major and minor
|
||||
/// TC handle values.
|
||||
#[inline(always)]
|
||||
pub fn get_major_minor(&self) -> (u16, u16) {
|
||||
// According to xdp_pping.c handles are minor:major u16s inside
|
||||
// a u32.
|
||||
((self.0 >> 16) as u16, (self.0 & 0xFFFF) as u16)
|
||||
}
|
||||
|
||||
/// Build a TC handle from a string. This is actually a complicated
|
||||
/// operation, since it has to handle "root" and other strings as well
|
||||
/// as simple "1:2" mappings. Calls a C function to handle this gracefully.
|
||||
pub fn from_string(
|
||||
handle: &str,
|
||||
) -> Result<Self, TcHandleParseError> {
|
||||
let handle = handle.trim();
|
||||
match handle {
|
||||
"root" => Ok(Self(TC_H_ROOT)),
|
||||
"none" => Ok(Self(TC_H_UNSPEC)),
|
||||
_ => {
|
||||
if !handle.contains(':') {
|
||||
if let Ok(major) = read_hex_string(handle) {
|
||||
let minor = 0;
|
||||
return Ok(Self((major << 16) | minor));
|
||||
} else {
|
||||
error!("Unable to parse TC handle {handle}. Must contain a colon.");
|
||||
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||
}
|
||||
}
|
||||
let parts: Vec<&str> = handle.split(':').collect();
|
||||
let major = read_hex_string(parts[0]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||
let minor = read_hex_string(parts[1]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||
if major >= (1<<16) || minor >= (1<<16) {
|
||||
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||
}
|
||||
Ok(Self((major << 16) | minor))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TcHandle {
|
||||
fn to_string(&self) -> String {
|
||||
let (major, minor) = self.get_major_minor();
|
||||
format!("{major:x}:{minor:x}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TcHandleParseError {
|
||||
#[error("Invalid input")]
|
||||
InvalidInput(String),
|
||||
}
|
||||
|
171
src/rust/lts_client/src/collector/quick_drops/retriever.rs
Normal file
171
src/rust/lts_client/src/collector/quick_drops/retriever.rs
Normal file
@ -0,0 +1,171 @@
|
||||
//! Async reader/parser for tc -s -j qdisc show dev (whatever)
|
||||
use thiserror::Error;
|
||||
use tokio::process::Command;
|
||||
pub use crate::collector::CakeStats;
|
||||
use super::queue_structure::{read_queueing_structure, QueueNode};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum AsyncQueueReaderMessage {
|
||||
#[error("Unable to figure out the current queue structure")]
|
||||
QueueStructure,
|
||||
#[error("Unable to query the interface with tc")]
|
||||
FetchRawFail,
|
||||
#[error("Unable to fetch stdout")]
|
||||
FetchStdout,
|
||||
#[error("JSON decode error")]
|
||||
JsonDecode,
|
||||
}
|
||||
|
||||
pub(crate) struct AsyncQueueReader {
|
||||
pub(crate) interface: String,
|
||||
}
|
||||
|
||||
impl AsyncQueueReader {
|
||||
pub(crate) fn new<S: ToString>(interface: S) -> Self {
|
||||
Self {
|
||||
interface: interface.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&self) -> Result<Option<Vec<CakeStats>>, AsyncQueueReaderMessage> {
|
||||
let mut result = None;
|
||||
if let Ok(queue_map) =
|
||||
read_queueing_structure().map_err(|_| AsyncQueueReaderMessage::QueueStructure)
|
||||
{
|
||||
if let Ok(raw) = self.fetch_raw().await {
|
||||
let stats = self.quick_parse(&raw, &queue_map).await?;
|
||||
result = Some(stats);
|
||||
} else {
|
||||
log::error!("Unable to fetch raw tc output");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) async fn run_on_a_stick(&self) -> Result<(Option<Vec<CakeStats>>, Option<Vec<CakeStats>>), AsyncQueueReaderMessage> {
|
||||
let mut result = (None, None);
|
||||
if let Ok(queue_map) =
|
||||
read_queueing_structure().map_err(|_| AsyncQueueReaderMessage::QueueStructure)
|
||||
{
|
||||
if let Ok(raw) = self.fetch_raw().await {
|
||||
let stats = self.quick_parse_stick(&raw, &queue_map).await?;
|
||||
result = (Some(stats.0), Some(stats.1));
|
||||
} else {
|
||||
log::error!("Unable to fetch raw tc output");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn fetch_raw(&self) -> Result<String, AsyncQueueReaderMessage> {
|
||||
let command_output = Command::new("/sbin/tc")
|
||||
.args(["-s", "-j", "qdisc", "show", "dev", &self.interface])
|
||||
.output()
|
||||
.await
|
||||
.map_err(|_| AsyncQueueReaderMessage::FetchRawFail)?;
|
||||
let json = String::from_utf8(command_output.stdout)
|
||||
.map_err(|_| AsyncQueueReaderMessage::FetchStdout)?;
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
async fn quick_parse(&self, raw: &str, structure: &[QueueNode]) -> Result<Vec<CakeStats>, AsyncQueueReaderMessage> {
|
||||
let mut result = Vec::with_capacity(structure.len());
|
||||
|
||||
let json = serde_json::from_str::<serde_json::Value>(raw)
|
||||
.map_err(|_| AsyncQueueReaderMessage::JsonDecode)?;
|
||||
|
||||
if let Some(array) = json.as_array() {
|
||||
for entry in array.iter() {
|
||||
if let Some(map) = entry.as_object() {
|
||||
if let (Some(kind), Some(handle)) =
|
||||
(map.get_key_value("kind"), map.get_key_value("parent"))
|
||||
{
|
||||
if let (Some("cake"), Some(handle)) = (kind.1.as_str(), handle.1.as_str()) {
|
||||
structure.iter().for_each(|node| {
|
||||
if node.class_id.to_string() == handle.to_string() {
|
||||
if let Some(circuit_id) = &node.circuit_id {
|
||||
let mut stats = CakeStats {
|
||||
circuit_id: circuit_id.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(serde_json::Value::Number(drops)) = map.get("drops") {
|
||||
stats.drops = drops.as_u64().unwrap_or(0);
|
||||
}
|
||||
if let Some(serde_json::Value::Number(marks)) = map.get("ecn_mark") {
|
||||
stats.marks = marks.as_u64().unwrap_or(0);
|
||||
}
|
||||
result.push(stats);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Be good async citizens and don't eat the CPU
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn quick_parse_stick(&self, raw: &str, structure: &[QueueNode]) -> Result<(Vec<CakeStats>, Vec<CakeStats>), AsyncQueueReaderMessage> {
|
||||
let mut down = Vec::with_capacity(structure.len());
|
||||
let mut up = Vec::with_capacity(structure.len());
|
||||
|
||||
let json = serde_json::from_str::<serde_json::Value>(raw)
|
||||
.map_err(|_| AsyncQueueReaderMessage::JsonDecode)?;
|
||||
|
||||
if let Some(array) = json.as_array() {
|
||||
for entry in array.iter() {
|
||||
if let Some(map) = entry.as_object() {
|
||||
if let (Some(kind), Some(handle)) =
|
||||
(map.get_key_value("kind"), map.get_key_value("parent"))
|
||||
{
|
||||
if let (Some("cake"), Some(handle)) = (kind.1.as_str(), handle.1.as_str()) {
|
||||
structure.iter().for_each(|node| {
|
||||
if node.class_id.to_string() == handle.to_string() {
|
||||
if let Some(circuit_id) = &node.circuit_id {
|
||||
let mut stats = CakeStats {
|
||||
circuit_id: circuit_id.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(serde_json::Value::Number(drops)) = map.get("drops") {
|
||||
stats.drops = drops.as_u64().unwrap_or(0);
|
||||
}
|
||||
if let Some(serde_json::Value::Number(marks)) = map.get("ecn_mark") {
|
||||
stats.marks = marks.as_u64().unwrap_or(0);
|
||||
}
|
||||
down.push(stats);
|
||||
}
|
||||
} else if node.up_class_id.to_string() == handle.to_string() {
|
||||
if let Some(circuit_id) = &node.circuit_id {
|
||||
let mut stats = CakeStats {
|
||||
circuit_id: circuit_id.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
if let Some(serde_json::Value::Number(drops)) = map.get("drops") {
|
||||
stats.drops = drops.as_u64().unwrap_or(0);
|
||||
}
|
||||
if let Some(serde_json::Value::Number(marks)) = map.get("ecn_mark") {
|
||||
stats.marks = marks.as_u64().unwrap_or(0);
|
||||
}
|
||||
up.push(stats);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Be good async citizens and don't eat the CPU
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((down, up))
|
||||
}
|
||||
}
|
76
src/rust/lts_client/src/collector/quick_drops/stats_diff.rs
Normal file
76
src/rust/lts_client/src/collector/quick_drops/stats_diff.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use tokio::sync::Mutex;
|
||||
use once_cell::sync::Lazy;
|
||||
use super::CakeStats;
|
||||
|
||||
static CAKE_TRACKER: Lazy<Mutex<CakeTracker>> = Lazy::new(|| Mutex::new(CakeTracker::new()));
|
||||
|
||||
pub(crate) async fn update_cake_stats() -> Option<(Vec<CakeStats>, Vec<CakeStats>)> {
|
||||
let mut tracker = CAKE_TRACKER.lock().await;
|
||||
tracker.update().await
|
||||
}
|
||||
|
||||
pub(crate) struct CakeTracker {
|
||||
prev: Option<(Vec<CakeStats>, Vec<CakeStats>)>,
|
||||
current: Option<(Vec<CakeStats>, Vec<CakeStats>)>,
|
||||
}
|
||||
|
||||
impl CakeTracker {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
prev: None,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn update(&mut self) -> Option<(Vec<CakeStats>, Vec<CakeStats>)> {
|
||||
if let Ok(cfg) = lqos_config::LibreQoSConfig::load() {
|
||||
let outbound = &cfg.internet_interface;
|
||||
let inbound = &cfg.isp_interface;
|
||||
if cfg.on_a_stick_mode {
|
||||
let reader = super::AsyncQueueReader::new(outbound);
|
||||
if let Ok((Some(up), Some(down))) = reader.run_on_a_stick().await {
|
||||
return self.read_up_down(up, down);
|
||||
}
|
||||
} else {
|
||||
let out_reader = super::AsyncQueueReader::new(outbound);
|
||||
let in_reader = super::AsyncQueueReader::new(inbound);
|
||||
let (up, down) = tokio::join!(
|
||||
out_reader.run(),
|
||||
in_reader.run(),
|
||||
);
|
||||
if let (Ok(Some(up)), Ok(Some(down))) = (up, down) {
|
||||
return self.read_up_down(up, down);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn read_up_down(&mut self, up: Vec<CakeStats>, down: Vec<CakeStats>) -> Option<(Vec<CakeStats>, Vec<CakeStats>)> {
|
||||
if self.prev.is_none() {
|
||||
self.prev = Some((up, down));
|
||||
None
|
||||
} else {
|
||||
// Delta time
|
||||
if let Some((down, up)) = &mut self.current {
|
||||
down.iter_mut().for_each(|d| {
|
||||
if let Some(prev) = self.prev.as_ref().unwrap().0.iter().find(|p| p.circuit_id == d.circuit_id) {
|
||||
d.drops = d.drops.saturating_sub(prev.drops);
|
||||
d.marks = d.marks.saturating_sub(prev.marks);
|
||||
}
|
||||
});
|
||||
up.iter_mut().for_each(|d| {
|
||||
if let Some(prev) = self.prev.as_ref().unwrap().1.iter().find(|p| p.circuit_id == d.circuit_id) {
|
||||
d.drops = d.drops.saturating_sub(prev.drops);
|
||||
d.marks = d.marks.saturating_sub(prev.marks);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Advance the previous
|
||||
self.prev = self.current.take();
|
||||
|
||||
Some((up, down))
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ pub(crate) async fn gather_uisp_data(comm_tx: Sender<SenderChannelMessage>) {
|
||||
cpu_usage: None,
|
||||
ram_percent: None,
|
||||
uisp_devices: Some(uisp_devices),
|
||||
cake_stats: None,
|
||||
};
|
||||
new_submission(submission, comm_tx).await;
|
||||
} else {
|
||||
|
@ -11,6 +11,7 @@ pub mod collector;
|
||||
|
||||
/// Submissions system for `lqosd`
|
||||
pub mod submission_queue;
|
||||
pub use collector::CakeStats;
|
||||
|
||||
/// Re-export bincode
|
||||
pub mod bincode {
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::path::Path;
|
||||
use dryoc::dryocbox::*;
|
||||
|
||||
/// Genereate a new keypair and store it in a file. If the file exists,
|
||||
@ -11,20 +10,9 @@ use dryoc::dryocbox::*;
|
||||
/// # Returns
|
||||
///
|
||||
/// The generated or loaded keypair
|
||||
pub fn generate_new_keypair(key_path: &str) -> KeyPair {
|
||||
let path = Path::new(key_path);
|
||||
if path.exists() {
|
||||
if let Ok(bytes) = std::fs::read(path) {
|
||||
if let Ok(keypair) = bincode::deserialize(&bytes) {
|
||||
log::info!("Loaded keypair from {}", path.display());
|
||||
return keypair;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn generate_new_keypair() -> KeyPair {
|
||||
let keypair = KeyPair::gen();
|
||||
let bytes = bincode::serialize(&keypair).unwrap();
|
||||
std::fs::write(path, bytes).unwrap();
|
||||
log::info!("Generated new keypair and stored it at {}", path.display());
|
||||
log::info!("Generated new keypair");
|
||||
keypair
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,68 @@
|
||||
use dryoc::{dryocbox::{Nonce, DryocBox}, types::{NewByteArray, ByteArray}};
|
||||
use lqos_config::EtcLqos;
|
||||
use crate::{transport_data::{LtsCommand, NodeIdAndLicense}, submission_queue::queue::QueueError};
|
||||
use thiserror::Error;
|
||||
use crate::{transport_data::{LtsCommand, NodeIdAndLicense, HelloVersion2}, submission_queue::queue::QueueError};
|
||||
use super::keys::{SERVER_PUBLIC_KEY, KEYPAIR};
|
||||
|
||||
pub(crate) async fn encode_submission_hello(license_key: &str, node_id: &str, node_name: &str) -> Result<Vec<u8>, QueueError> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
// Build the body
|
||||
let hello_message = HelloVersion2 {
|
||||
license_key: license_key.to_string(),
|
||||
node_id: node_id.to_string(),
|
||||
node_name: node_name.to_string(),
|
||||
client_public_key: KEYPAIR.read().await.public_key.clone().to_vec(),
|
||||
};
|
||||
|
||||
// Add the version
|
||||
result.extend(2u16.to_be_bytes());
|
||||
|
||||
// Pad to 32-bit boundary
|
||||
result.extend(3u16.to_be_bytes());
|
||||
|
||||
// Serialize the body
|
||||
let hello_bytes = serde_cbor::to_vec(&hello_message).map_err(|_| QueueError::SendFail)?;
|
||||
|
||||
// Add the length
|
||||
result.extend((hello_bytes.len() as u64).to_be_bytes());
|
||||
|
||||
// Add the body
|
||||
result.extend(hello_bytes);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SubmissionDecodeError {
|
||||
#[error("Invalid version")]
|
||||
InvalidVersion,
|
||||
#[error("Invalid padding")]
|
||||
InvalidPadding,
|
||||
#[error("Failed to deserialize")]
|
||||
Deserialize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn decode_submission_hello(bytes: &[u8]) -> Result<HelloVersion2, SubmissionDecodeError> {
|
||||
let version = u16::from_be_bytes([bytes[0], bytes[1]]);
|
||||
if version != 2 {
|
||||
log::error!("Received an invalid version from the server: {}", version);
|
||||
return Err(SubmissionDecodeError::InvalidVersion);
|
||||
}
|
||||
let padding = u16::from_be_bytes([bytes[2], bytes[3]]);
|
||||
if padding != 3 {
|
||||
log::error!("Received an invalid padding from the server: {}", padding);
|
||||
return Err(SubmissionDecodeError::InvalidPadding);
|
||||
}
|
||||
let size = u64::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11]]);
|
||||
let hello_bytes = &bytes[12..12 + size as usize];
|
||||
let hello: HelloVersion2 = serde_cbor::from_slice(hello_bytes).map_err(|_| SubmissionDecodeError::Deserialize)?;
|
||||
|
||||
Ok(hello)
|
||||
}
|
||||
|
||||
pub(crate) async fn encode_submission(submission: &LtsCommand) -> Result<Vec<u8>, QueueError> {
|
||||
let nonce = Nonce::gen();
|
||||
let mut result = Vec::new();
|
||||
@ -58,3 +118,18 @@ fn get_license_key_and_node_id(nonce: &Nonce) -> Result<NodeIdAndLicense, QueueE
|
||||
}
|
||||
Err(QueueError::SendFail)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[tokio::test]
|
||||
async fn hello_submission_roundtrip() {
|
||||
let license_key = "1234567890";
|
||||
let node_id = "node_id";
|
||||
let node_name = "node_name";
|
||||
let hello = super::encode_submission_hello(license_key, node_id, node_name).await.unwrap();
|
||||
let hello = super::decode_submission_hello(&hello).unwrap();
|
||||
assert_eq!(hello.license_key, license_key);
|
||||
assert_eq!(hello.node_id, node_id);
|
||||
assert_eq!(hello.node_name, node_name);
|
||||
}
|
||||
}
|
@ -3,10 +3,10 @@ use lqos_config::EtcLqos;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub(crate) static KEYPAIR: Lazy<RwLock<KeyPair>> = Lazy::new(|| RwLock::new(generate_new_keypair("lts_keys.bin")));
|
||||
pub(crate) static KEYPAIR: Lazy<RwLock<KeyPair>> = Lazy::new(|| RwLock::new(generate_new_keypair()));
|
||||
pub(crate) static SERVER_PUBLIC_KEY: Lazy<RwLock<Option<PublicKey>>> = Lazy::new(|| RwLock::new(None));
|
||||
|
||||
async fn store_server_public_key(key: &PublicKey) {
|
||||
pub(crate) async fn store_server_public_key(key: &PublicKey) {
|
||||
*SERVER_PUBLIC_KEY.write().await = Some(key.clone());
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
use std::time::Duration;
|
||||
use tokio::{sync::mpsc::Receiver, time::sleep, net::TcpStream};
|
||||
use self::keys::key_exchange;
|
||||
use super::{licensing::{get_license_status, LicenseState}, queue::send_queue};
|
||||
use lqos_config::EtcLqos;
|
||||
use tokio::{sync::mpsc::Receiver, time::sleep, net::TcpStream, io::{AsyncWriteExt, AsyncReadExt}};
|
||||
use crate::submission_queue::comm_channel::keys::store_server_public_key;
|
||||
use self::encode::encode_submission_hello;
|
||||
use super::queue::{send_queue, QueueError};
|
||||
mod keys;
|
||||
pub(crate) use keys::key_exchange;
|
||||
mod encode;
|
||||
pub(crate) use encode::encode_submission;
|
||||
|
||||
@ -12,38 +15,25 @@ pub(crate) enum SenderChannelMessage {
|
||||
}
|
||||
|
||||
pub(crate) async fn start_communication_channel(mut rx: Receiver<SenderChannelMessage>) {
|
||||
let mut connected = false;
|
||||
let mut stream: Option<TcpStream> = None;
|
||||
// let mut connected = false;
|
||||
// let mut stream: Option<TcpStream> = None;
|
||||
loop {
|
||||
match rx.try_recv() {
|
||||
Ok(SenderChannelMessage::QueueReady) => {
|
||||
// If not connected, see if we are allowed to connect and get a target
|
||||
if !connected || stream.is_none() {
|
||||
log::info!("Establishing LTS TCP channel.");
|
||||
stream = connect_if_permitted().await;
|
||||
if stream.is_some() {
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
log::info!("Trying to connect to stats.libreqos.io");
|
||||
let mut stream = connect_if_permitted().await;
|
||||
log::info!("Connection to stats.libreqos.io established");
|
||||
|
||||
// If we're still not connected, skip - otherwise, send the
|
||||
// queued data
|
||||
if let Some(tcpstream) = &mut stream {
|
||||
if connected && tcpstream.writable().await.is_ok() {
|
||||
// Send the data
|
||||
let all_good = send_queue(tcpstream).await;
|
||||
if all_good.is_err() {
|
||||
log::error!("Stream fail during send. Will re-send");
|
||||
connected = false;
|
||||
stream = None;
|
||||
}
|
||||
} else {
|
||||
stream = None;
|
||||
connected = false;
|
||||
if let Ok(tcpstream) = &mut stream {
|
||||
// Send the data
|
||||
let all_good = send_queue(tcpstream).await;
|
||||
if all_good.is_err() {
|
||||
log::error!("Stream fail during send. Will re-send");
|
||||
}
|
||||
} else {
|
||||
connected = false;
|
||||
stream = None;
|
||||
log::error!("Unable to submit data to stats.libreqos.io: {stream:?}");
|
||||
}
|
||||
}
|
||||
Ok(SenderChannelMessage::Quit) => {
|
||||
@ -56,28 +46,85 @@ pub(crate) async fn start_communication_channel(mut rx: Receiver<SenderChannelMe
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect_if_permitted() -> Option<TcpStream> {
|
||||
let license = get_license_status().await;
|
||||
if let LicenseState::Valid { stats_host, .. } = license {
|
||||
if !key_exchange().await {
|
||||
return None;
|
||||
}
|
||||
async fn connect_if_permitted() -> Result<TcpStream, QueueError> {
|
||||
log::info!("Connecting to stats.libreqos.io");
|
||||
// Check that we have a local license key and are enabled
|
||||
let cfg = EtcLqos::load().map_err(|_| {
|
||||
log::error!("Unable to load config file.");
|
||||
QueueError::NoLocalLicenseKey
|
||||
})?;
|
||||
let node_id = cfg.node_id.ok_or_else(|| {
|
||||
log::warn!("No node ID configured.");
|
||||
QueueError::NoLocalLicenseKey
|
||||
})?;
|
||||
let node_name = cfg.node_name.unwrap_or(node_id.clone());
|
||||
let usage_cfg = cfg.long_term_stats.ok_or_else(|| {
|
||||
log::warn!("Long-term stats are not configured.");
|
||||
QueueError::NoLocalLicenseKey
|
||||
})?;
|
||||
if !usage_cfg.gather_stats {
|
||||
log::warn!("Gathering long-term stats is disabled.");
|
||||
return Err(QueueError::StatsDisabled);
|
||||
}
|
||||
let license_key = usage_cfg.license_key.ok_or_else(|| {
|
||||
log::warn!("No license key configured.");
|
||||
QueueError::NoLocalLicenseKey
|
||||
})?;
|
||||
|
||||
let host = format!("{stats_host}:9128");
|
||||
let stream = TcpStream::connect(&host).await;
|
||||
match stream {
|
||||
Err(e) => {
|
||||
log::error!("Unable to connect to {host}: {e}");
|
||||
return None;
|
||||
}
|
||||
Ok(stream) => {
|
||||
if stream.writable().await.is_err() {
|
||||
log::error!("Unable to write to {host}");
|
||||
return None;
|
||||
}
|
||||
return Some(stream);
|
||||
}
|
||||
// Connect
|
||||
let host = "stats.libreqos.io:9128";
|
||||
let mut stream = TcpStream::connect(&host).await
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to connect to {host}: {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
|
||||
// Send Hello
|
||||
let bytes = encode_submission_hello(&license_key, &node_id, &node_name).await?;
|
||||
stream.write_all(&bytes).await
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to write to {host}: {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
|
||||
// Receive Server Public Key or Denied
|
||||
let result = stream.read_u16().await
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to read reply from {host}, {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
match result {
|
||||
0 => {
|
||||
log::error!("License validation failure.");
|
||||
return Err(QueueError::SendFail);
|
||||
}
|
||||
1 => {
|
||||
// We received validation. Now to decode the public key.
|
||||
let key_size = stream.read_u64().await
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to read reply from {host}, {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
let mut key_buffer = vec![0u8; key_size as usize];
|
||||
stream.read_exact(&mut key_buffer).await
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to read reply from {host}, {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
let server_public_key = serde_cbor::from_slice(&key_buffer)
|
||||
.map_err(|e| {
|
||||
log::error!("Unable to decode key from {host}, {e:?}");
|
||||
QueueError::SendFail
|
||||
})?;
|
||||
store_server_public_key(&server_public_key).await;
|
||||
log::info!("Received server public key.");
|
||||
}
|
||||
_ => {
|
||||
log::error!("Unexpected reply from server.");
|
||||
return Err(QueueError::SendFail);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
// Proceed
|
||||
Ok(stream)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::transport_data::{ask_license_server, LicenseReply};
|
||||
use crate::transport_data::{ask_license_server, LicenseReply, ask_license_server_for_new_account};
|
||||
use lqos_config::EtcLqos;
|
||||
use lqos_utils::unix_time::unix_now;
|
||||
use once_cell::sync::Lazy;
|
||||
@ -28,6 +28,7 @@ static LICENSE_STATUS: Lazy<RwLock<LicenseStatus>> =
|
||||
Lazy::new(|| RwLock::new(LicenseStatus::default()));
|
||||
|
||||
pub(crate) async fn get_license_status() -> LicenseState {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
if let Ok(unix_time) = unix_now() {
|
||||
let license_status = {
|
||||
LICENSE_STATUS.read().await.clone()
|
||||
@ -43,7 +44,11 @@ pub(crate) async fn get_license_status() -> LicenseState {
|
||||
const MISERLY_NO_KEY: &str = "IDontSupportDevelopersAndShouldFeelBad";
|
||||
|
||||
async fn check_license(unix_time: u64) -> LicenseState {
|
||||
log::info!("Checking LTS stats license");
|
||||
if let Ok(cfg) = EtcLqos::load() {
|
||||
// The config file is good. Is LTS enabled?
|
||||
// If it isn't, we need to try very gently to see if a pending
|
||||
// request has been submitted.
|
||||
if let Some(cfg) = cfg.long_term_stats {
|
||||
if let Some(key) = cfg.license_key {
|
||||
if key == MISERLY_NO_KEY {
|
||||
@ -80,7 +85,29 @@ async fn check_license(unix_time: u64) -> LicenseState {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// LTS is unconfigured - but not explicitly disabled.
|
||||
// So we need to check if we have a pending request.
|
||||
// If a license key has been assigned, then we'll setup
|
||||
// LTS. If it hasn't, we'll just return Unknown.
|
||||
if let Some(node_id) = &cfg.node_id {
|
||||
if let Ok(result) = ask_license_server_for_new_account(node_id.to_string()).await {
|
||||
if let LicenseReply::NewActivation { license_key } = result {
|
||||
// We have a new license!
|
||||
let _ = lqos_config::enable_long_term_stats(license_key);
|
||||
// Note that we're not doing anything beyond this - the next cycle
|
||||
// will pick up on there actually being a license
|
||||
} else {
|
||||
log::info!("No pending LTS license found");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// There's no node ID either - we can't talk to this
|
||||
log::warn!("No NodeID is configured. No online services are possible.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error!("Unable to load lqosd configuration. Not going to try.");
|
||||
}
|
||||
LicenseState::Unknown
|
||||
}
|
||||
|
@ -15,8 +15,11 @@ pub(crate) async fn enqueue_if_allowed(data: StatsSubmission, comm_tx: Sender<Se
|
||||
log::error!("Your license is invalid. Please contact support.");
|
||||
}
|
||||
LicenseState::Valid{ .. } => {
|
||||
log::info!("Sending data to the queue.");
|
||||
QUEUE.push(LtsCommand::Submit(Box::new(data))).await;
|
||||
let _ = comm_tx.send(SenderChannelMessage::QueueReady).await;
|
||||
if let Err(e) = comm_tx.send(SenderChannelMessage::QueueReady).await {
|
||||
log::error!("Unable to send queue ready message: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,12 +75,20 @@ pub(crate) async fn send_queue(stream: &mut TcpStream) -> Result<(), QueueError>
|
||||
let mut lock = QUEUE.queue.lock().await;
|
||||
for message in lock.iter_mut() {
|
||||
let submission_buffer = encode_submission(&message.body).await?;
|
||||
let ret = stream.write(&submission_buffer).await;
|
||||
let ret = stream.write_all(&submission_buffer).await;
|
||||
log::info!("Sent submission: {} bytes.", submission_buffer.len());
|
||||
if ret.is_err() {
|
||||
log::error!("Unable to write to TCP stream.");
|
||||
log::error!("{:?}", ret);
|
||||
message.sent = false;
|
||||
match crate::submission_queue::comm_channel::key_exchange().await {
|
||||
true => {
|
||||
log::info!("Successfully exchanged license keys.");
|
||||
}
|
||||
false => {
|
||||
log::error!("Unable to talk to the licensing system to fix keys.");
|
||||
}
|
||||
}
|
||||
return Err(QueueError::SendFail);
|
||||
} else {
|
||||
message.sent = true;
|
||||
@ -91,6 +102,10 @@ pub(crate) async fn send_queue(stream: &mut TcpStream) -> Result<(), QueueError>
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum QueueError {
|
||||
#[error("No local license key")]
|
||||
NoLocalLicenseKey,
|
||||
#[error("Stats are disabled")]
|
||||
StatsDisabled,
|
||||
#[error("Unable to send")]
|
||||
SendFail,
|
||||
}
|
@ -24,6 +24,11 @@ pub enum LicenseRequest {
|
||||
/// The sodium-style public key of the requesting shaper node
|
||||
public_key: PublicKey,
|
||||
},
|
||||
/// Check to see if this node has been newly approved
|
||||
PendingLicenseRequest {
|
||||
/// The local node id
|
||||
node_id: String,
|
||||
}
|
||||
}
|
||||
|
||||
/// License server responses for a key
|
||||
@ -43,6 +48,11 @@ pub enum LicenseReply {
|
||||
/// The server's public key
|
||||
public_key: PublicKey,
|
||||
},
|
||||
/// New Activation
|
||||
NewActivation {
|
||||
/// The license key to apply
|
||||
license_key: String,
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when checking licenses
|
||||
@ -72,3 +82,16 @@ pub struct NodeIdAndLicense {
|
||||
/// The Sodium Nonce
|
||||
pub nonce: [u8; 24],
|
||||
}
|
||||
|
||||
/// For the new V2 hello license system, encodes a greeting
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct HelloVersion2 {
|
||||
/// The node id
|
||||
pub node_id: String,
|
||||
/// The license key
|
||||
pub license_key: String,
|
||||
// The name of the node requesting service
|
||||
pub node_name: String,
|
||||
/// The Sodium Public Key
|
||||
pub client_public_key: Vec<u8>,
|
||||
}
|
@ -17,7 +17,27 @@ fn build_license_request(key: String) -> Result<Vec<u8>, LicenseCheckError> {
|
||||
let mut result = Vec::new();
|
||||
let payload = serde_cbor::to_vec(&LicenseRequest::LicenseCheck { key });
|
||||
if let Err(e) = payload {
|
||||
log::warn!("Unable to serialize statistics. Not sending them.");
|
||||
log::warn!("Unable to serialize license request. Not sending them.");
|
||||
log::warn!("{e:?}");
|
||||
return Err(LicenseCheckError::SerializeFail);
|
||||
}
|
||||
let payload = payload.unwrap();
|
||||
|
||||
// Store the version as network order
|
||||
result.extend(1u16.to_be_bytes());
|
||||
// Store the payload size as network order
|
||||
result.extend((payload.len() as u64).to_be_bytes());
|
||||
// Store the payload itself
|
||||
result.extend(payload);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn build_activation_query(node_id: String) -> Result<Vec<u8>, LicenseCheckError> {
|
||||
let mut result = Vec::new();
|
||||
let payload = serde_cbor::to_vec(&LicenseRequest::PendingLicenseRequest { node_id } );
|
||||
if let Err(e) = payload {
|
||||
log::warn!("Unable to serialize license request. Not sending them.");
|
||||
log::warn!("{e:?}");
|
||||
return Err(LicenseCheckError::SerializeFail);
|
||||
}
|
||||
@ -107,6 +127,47 @@ pub async fn ask_license_server(key: String) -> Result<LicenseReply, LicenseChec
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ask_license_server_for_new_account(
|
||||
node_id: String,
|
||||
) -> Result<LicenseReply, LicenseCheckError>
|
||||
{
|
||||
if let Ok(buffer) = build_activation_query(node_id) {
|
||||
let stream = TcpStream::connect(LICENSE_SERVER).await;
|
||||
if let Err(e) = &stream {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
log::error!("Unable to access {LICENSE_SERVER}. Check that lqosd is running and you have appropriate permissions.");
|
||||
return Err(LicenseCheckError::SendFail);
|
||||
}
|
||||
}
|
||||
let stream = stream;
|
||||
match stream {
|
||||
Ok(mut stream) => {
|
||||
let ret = stream.write(&buffer).await;
|
||||
if ret.is_err() {
|
||||
log::error!("Unable to write to {LICENSE_SERVER} stream.");
|
||||
log::error!("{:?}", ret);
|
||||
return Err(LicenseCheckError::SendFail);
|
||||
}
|
||||
let mut buf = Vec::with_capacity(10240);
|
||||
let ret = stream.read_to_end(&mut buf).await;
|
||||
if ret.is_err() {
|
||||
log::error!("Unable to read from {LICENSE_SERVER} stream.");
|
||||
log::error!("{:?}", ret);
|
||||
return Err(LicenseCheckError::SendFail);
|
||||
}
|
||||
|
||||
decode_response(&buf)
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("TCP stream failed to connect: {:?}", e);
|
||||
Err(LicenseCheckError::ReceiveFail)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(LicenseCheckError::SerializeFail)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the license server for the public key
|
||||
pub async fn exchange_keys_with_license_server(
|
||||
node_id: String,
|
||||
|
@ -5,6 +5,8 @@ use lqos_config::ShapedDevice;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uisp::Device;
|
||||
|
||||
use crate::collector::CakeStats;
|
||||
|
||||
/// Type that provides a minimum, maximum and average value
|
||||
/// for a given statistic within the associated time period.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
@ -92,6 +94,9 @@ pub struct StatsSubmission {
|
||||
pub ram_percent: Option<u32>,
|
||||
/// UISP Device Information
|
||||
pub uisp_devices: Option<Vec<UispExtDevice>>,
|
||||
/// Queue Stats
|
||||
pub cake_stats: Option<(Vec<CakeStats>, Vec<CakeStats>)>,
|
||||
|
||||
}
|
||||
|
||||
/// Submission to the `lts_node` process
|
||||
|
@ -6,7 +6,7 @@ license = "GPL-2.0-only"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
anyhow = "1"
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
lqos_utils = { path = "../lqos_utils" }
|
||||
|
@ -5,6 +5,6 @@ edition = "2021"
|
||||
license = "GPL-2.0-only"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
anyhow = "1"
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
|
Loading…
Reference in New Issue
Block a user