feat(reflectors): refactor reflector configuration
Build binaries / Build (arm-musleabi) (push) Successful in 1m46s
Build binaries / Build (aarch64-musl) (push) Successful in 1m46s
Build binaries / Build (arm-musleabihf) (push) Successful in 2m1s
Build binaries / Build (armv7-musleabi) (push) Successful in 2m1s
Build binaries / Build (armv7-musleabihf) (push) Successful in 1m53s
Build binaries / Build (i686-musl) (push) Successful in 1m53s
Build binaries / Build (mips-musl) (push) Successful in 2m4s
Build binaries / Build (mips64-muslabi64) (push) Successful in 2m1s
Build binaries / Build (mips64el-muslabi64) (push) Successful in 2m5s
Build binaries / Build (mipsel-musl) (push) Successful in 2m3s
Build binaries / Build (riscv64gc-musl) (push) Successful in 1m43s
Build binaries / Build (x86_64-musl) (push) Successful in 1m47s
Build binaries / Build (arm-musleabi) (push) Successful in 1m46s
Build binaries / Build (aarch64-musl) (push) Successful in 1m46s
Build binaries / Build (arm-musleabihf) (push) Successful in 2m1s
Build binaries / Build (armv7-musleabi) (push) Successful in 2m1s
Build binaries / Build (armv7-musleabihf) (push) Successful in 1m53s
Build binaries / Build (i686-musl) (push) Successful in 1m53s
Build binaries / Build (mips-musl) (push) Successful in 2m4s
Build binaries / Build (mips64-muslabi64) (push) Successful in 2m1s
Build binaries / Build (mips64el-muslabi64) (push) Successful in 2m5s
Build binaries / Build (mipsel-musl) (push) Successful in 2m3s
Build binaries / Build (riscv64gc-musl) (push) Successful in 1m43s
Build binaries / Build (x86_64-musl) (push) Successful in 1m47s
This commit is contained in:
4 files changed
+101
-79
No files matched your search
+1
-1
@@ -148,7 +148,7 @@ fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
|
|||||||
IpAddr::from_str("94.140.14.14")?,
|
IpAddr::from_str("94.140.14.14")?,
|
||||||
];
|
];
|
||||||
|
|
||||||
let reselection_enabled = configured_count > settings.advanced_settings.num_reflectors as usize;
|
let reselection_enabled = configured_count > settings.reflectors.num_reflectors as usize;
|
||||||
let pool = if reselection_enabled {
|
let pool = if reselection_enabled {
|
||||||
configured
|
configured
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -159,10 +159,8 @@ impl ReflectorSelector {
|
|||||||
|
|
||||||
// After 40 reselections, slow down to every 15 minutes
|
// After 40 reselections, slow down to every 15 minutes
|
||||||
if reselection_count > 40 {
|
if reselection_count > 40 {
|
||||||
selector_sleep_time = Duration::new(
|
selector_sleep_time =
|
||||||
self.settings.advanced_settings.peer_reselection_time * 60,
|
Duration::new(self.settings.reflectors.peer_reselection_time * 60, 0);
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut next_peers: Vec<IpAddr> = Vec::new();
|
let mut next_peers: Vec<IpAddr> = Vec::new();
|
||||||
@@ -222,7 +220,7 @@ impl ReflectorSelector {
|
|||||||
candidates.sort_by(|a, b| a.1.cmp(&b.1));
|
candidates.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
|
|
||||||
// Now we will just limit the candidates down to 2 * num_reflectors
|
// Now we will just limit the candidates down to 2 * num_reflectors
|
||||||
let mut num_reflectors = self.settings.advanced_settings.num_reflectors;
|
let mut num_reflectors = self.settings.reflectors.num_reflectors;
|
||||||
let candidate_pool_num = (2 * num_reflectors) as usize;
|
let candidate_pool_num = (2 * num_reflectors) as usize;
|
||||||
candidates.truncate(candidate_pool_num);
|
candidates.truncate(candidate_pool_num);
|
||||||
|
|
||||||
|
|||||||
+55
-55
@@ -78,28 +78,32 @@ where
|
|||||||
deserializer.deserialize_any(FlexiBoolVisitor)
|
deserializer.deserialize_any(FlexiBoolVisitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)]
|
||||||
pub enum MeasurementType {
|
#[serde(rename_all = "kebab-case")]
|
||||||
Icmp = 1,
|
pub enum ReflectorProtocol {
|
||||||
|
IcmpEcho = 1,
|
||||||
IcmpTimestamps,
|
IcmpTimestamps,
|
||||||
|
Irtt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for MeasurementType {
|
impl Display for ReflectorProtocol {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
MeasurementType::Icmp => write!(f, "icmp"),
|
ReflectorProtocol::IcmpEcho => write!(f, "icmp"),
|
||||||
MeasurementType::IcmpTimestamps => write!(f, "icmp-timestamps"),
|
ReflectorProtocol::IcmpTimestamps => write!(f, "icmp-timestamps"),
|
||||||
|
ReflectorProtocol::Irtt => write!(f, "irtt"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for MeasurementType {
|
impl FromStr for ReflectorProtocol {
|
||||||
type Err = ConfigError;
|
type Err = ConfigError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s.to_lowercase().as_str() {
|
match s.to_lowercase().as_str() {
|
||||||
"icmp" => Ok(MeasurementType::Icmp),
|
"icmp" => Ok(ReflectorProtocol::IcmpEcho),
|
||||||
"icmp-timestamps" | "icmp_timestamps" => Ok(MeasurementType::IcmpTimestamps),
|
"icmp-timestamps" | "icmp_timestamps" => Ok(ReflectorProtocol::IcmpTimestamps),
|
||||||
|
"irtt" => Ok(ReflectorProtocol::Irtt),
|
||||||
_ => Err(ConfigError::Message(format!(
|
_ => Err(ConfigError::Message(format!(
|
||||||
"invalid measurement type: {s}"
|
"invalid measurement type: {s}"
|
||||||
))),
|
))),
|
||||||
@@ -127,12 +131,13 @@ impl FromStr for ObservabilityProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize, UciSection)]
|
||||||
struct ReflectorRecord {
|
pub(crate) struct ReflectorDefinition {
|
||||||
reflector_ip: IpAddr,
|
pub(crate) host: String,
|
||||||
ip_version: u8,
|
pub(crate) country: Option<String>,
|
||||||
#[allow(dead_code)]
|
pub(crate) protocols: Vec<ReflectorProtocol>,
|
||||||
description: String,
|
pub(crate) irtt_port: Option<u16>,
|
||||||
|
pub(crate) irtt_hmac: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, UciConfig)]
|
#[derive(Clone, Debug, Deserialize, UciConfig)]
|
||||||
@@ -141,6 +146,10 @@ pub(crate) struct Settings {
|
|||||||
#[uci(section)]
|
#[uci(section)]
|
||||||
pub(crate) network: NetworkSettings,
|
pub(crate) network: NetworkSettings,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[uci(section)]
|
||||||
|
pub(crate) reflectors: ReflectorSettings,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[uci(section)]
|
#[uci(section)]
|
||||||
pub(crate) output: OutputSettings,
|
pub(crate) output: OutputSettings,
|
||||||
@@ -173,40 +182,8 @@ impl Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_reflectors(&self) -> anyhow::Result<Vec<IpAddr>> {
|
pub fn load_reflectors(&self) -> anyhow::Result<Vec<IpAddr>> {
|
||||||
let mut reader = csv::ReaderBuilder::new()
|
|
||||||
.has_headers(true)
|
|
||||||
.trim(csv::Trim::All)
|
|
||||||
.from_path(&self.advanced_settings.reflector_list_file)
|
|
||||||
.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to open reflector list: {}",
|
|
||||||
self.advanced_settings.reflector_list_file
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut reflectors = Vec::with_capacity(50);
|
let mut reflectors = Vec::with_capacity(50);
|
||||||
|
|
||||||
for record in reader.deserialize::<ReflectorRecord>() {
|
|
||||||
let record = record.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to parse reflector list: {}",
|
|
||||||
self.advanced_settings.reflector_list_file
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match (record.ip_version, record.reflector_ip) {
|
|
||||||
(4, IpAddr::V4(_)) => {}
|
|
||||||
(6, address @ IpAddr::V6(_)) => {
|
|
||||||
bail!("reflector {address} is IPv6; only IPv4 reflectors are supported");
|
|
||||||
}
|
|
||||||
(version, address) => {
|
|
||||||
bail!("reflector {address} has inconsistent IP version {version}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reflectors.push(record.reflector_ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(reflectors)
|
Ok(reflectors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,6 +217,35 @@ impl NetworkSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, UciSection)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) struct ReflectorSettings {
|
||||||
|
pub(crate) files: Vec<String>,
|
||||||
|
pub(crate) countries: Vec<String>,
|
||||||
|
|
||||||
|
#[uci(skip)]
|
||||||
|
pub(crate) entries: Vec<ReflectorDefinition>,
|
||||||
|
|
||||||
|
#[uci(skip)]
|
||||||
|
pub(crate) bootstrap: Vec<ReflectorDefinition>,
|
||||||
|
|
||||||
|
pub(crate) num_reflectors: u8,
|
||||||
|
pub(crate) peer_reselection_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ReflectorSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
files: vec!["/etc/sqm-autorate-rust/reflectors.csv".into()],
|
||||||
|
countries: Vec::new(),
|
||||||
|
entries: Vec::new(),
|
||||||
|
bootstrap: Vec::new(),
|
||||||
|
num_reflectors: 5,
|
||||||
|
peer_reselection_time: 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, UciSection)]
|
#[derive(Clone, Debug, Deserialize, UciSection)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub(crate) struct OutputSettings {
|
pub(crate) struct OutputSettings {
|
||||||
@@ -309,10 +315,7 @@ pub(crate) struct AdvancedSettings {
|
|||||||
pub(crate) high_load_level: f64,
|
pub(crate) high_load_level: f64,
|
||||||
pub(crate) min_change_interval: f64,
|
pub(crate) min_change_interval: f64,
|
||||||
#[serde(deserialize_with = "deserialize_from_str")]
|
#[serde(deserialize_with = "deserialize_from_str")]
|
||||||
pub(crate) measurement_type: MeasurementType,
|
pub(crate) measurement_type: ReflectorProtocol,
|
||||||
pub(crate) num_reflectors: u8,
|
|
||||||
pub(crate) peer_reselection_time: u64,
|
|
||||||
pub(crate) reflector_list_file: String,
|
|
||||||
pub(crate) speed_hist_size: u32,
|
pub(crate) speed_hist_size: u32,
|
||||||
pub(crate) tick_interval: f64,
|
pub(crate) tick_interval: f64,
|
||||||
pub(crate) upload_delay_ms: f64,
|
pub(crate) upload_delay_ms: f64,
|
||||||
@@ -326,10 +329,7 @@ impl Default for AdvancedSettings {
|
|||||||
download_delay_ms: 15.0,
|
download_delay_ms: 15.0,
|
||||||
high_load_level: 0.8,
|
high_load_level: 0.8,
|
||||||
min_change_interval: 0.5,
|
min_change_interval: 0.5,
|
||||||
measurement_type: MeasurementType::IcmpTimestamps,
|
measurement_type: ReflectorProtocol::IcmpTimestamps,
|
||||||
num_reflectors: 5,
|
|
||||||
peer_reselection_time: 15,
|
|
||||||
reflector_list_file: "/etc/sqm-autorate/reflectors-icmp.csv".to_owned(),
|
|
||||||
speed_hist_size: 100,
|
speed_hist_size: 100,
|
||||||
tick_interval: 0.5,
|
tick_interval: 0.5,
|
||||||
upload_delay_ms: 15.0,
|
upload_delay_ms: 15.0,
|
||||||
|
|||||||
+42
-18
@@ -3,6 +3,8 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use crate::settings::ReflectorSettings;
|
||||||
|
|
||||||
use super::{AdvancedSettings, NetworkSettings, ObservabilitySettings, OutputSettings, Settings};
|
use super::{AdvancedSettings, NetworkSettings, ObservabilitySettings, OutputSettings, Settings};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
@@ -88,6 +90,7 @@ impl Settings {
|
|||||||
let mut report = ValidationReport::default();
|
let mut report = ValidationReport::default();
|
||||||
|
|
||||||
self.network.validate(&mut report);
|
self.network.validate(&mut report);
|
||||||
|
self.reflectors.validate(&mut report);
|
||||||
self.output.validate(&mut report);
|
self.output.validate(&mut report);
|
||||||
self.observability.validate(&mut report);
|
self.observability.validate(&mut report);
|
||||||
self.advanced_settings.validate(&mut report);
|
self.advanced_settings.validate(&mut report);
|
||||||
@@ -192,6 +195,45 @@ impl NetworkSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReflectorSettings {
|
||||||
|
fn validate(&self, report: &mut ValidationReport) {
|
||||||
|
if self.num_reflectors == 0 {
|
||||||
|
report.error("reflectors.num_reflectors", "must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.peer_reselection_time == 0 {
|
||||||
|
report.error("reflectors.peer_reselection_time", "must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.files.is_empty() && self.entries.is_empty() {
|
||||||
|
report.error(
|
||||||
|
"reflectors",
|
||||||
|
"at least one reflector file or inline entry is required",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, file) in self.files.iter().enumerate() {
|
||||||
|
if file.trim().is_empty() {
|
||||||
|
report.error(
|
||||||
|
"reflectors.files",
|
||||||
|
format!("entry {index} must not be empty"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, country) in self.countries.iter().enumerate() {
|
||||||
|
if country.len() != 2 || !country.bytes().all(|c| c.is_ascii_alphabetic()) {
|
||||||
|
report.error(
|
||||||
|
"reflectors.countries",
|
||||||
|
format!(
|
||||||
|
"entry {index} ({country:?}) must be an ISO 3166-1 alpha-2 country code"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OutputSettings {
|
impl OutputSettings {
|
||||||
fn validate(&self, report: &mut ValidationReport) {
|
fn validate(&self, report: &mut ValidationReport) {
|
||||||
if self.suppress_statistics {
|
if self.suppress_statistics {
|
||||||
@@ -298,24 +340,6 @@ impl AdvancedSettings {
|
|||||||
self.tick_interval,
|
self.tick_interval,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.num_reflectors < 5 {
|
|
||||||
report.error(
|
|
||||||
"advanced_settings.num_reflectors",
|
|
||||||
format!("must be at least 5, got {}", self.num_reflectors,),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.peer_reselection_time == 0 {
|
|
||||||
report.error(
|
|
||||||
"advanced_settings.peer_reselection_time",
|
|
||||||
"must be greater than 0",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.reflector_list_file.trim().is_empty() {
|
|
||||||
report.error("advanced_settings.reflector_list_file", "must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.speed_hist_size == 0 {
|
if self.speed_hist_size == 0 {
|
||||||
report.error(
|
report.error(
|
||||||
"advanced_settings.speed_hist_size",
|
"advanced_settings.speed_hist_size",
|
||||||
|
|||||||
Reference in new issue
Block a user