refactoring, log->tracing, gpsd source
This commit is contained in:
235
src/chrony.rs
235
src/chrony.rs
@@ -1,13 +1,17 @@
|
||||
use crate::{
|
||||
ChimemonSource, ChimemonSourceChannel, Config, SourceMetric, SourceReport, SourceReportDetails,
|
||||
SourceStatus,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use chimemon::{ChimemonSource, ChimemonSourceChannel, Config};
|
||||
use chrony_candm::reply::{self, ReplyBody, SourceMode};
|
||||
use chrony_candm::reply::{self, ReplyBody, SourceMode, SourceState};
|
||||
use chrony_candm::request::{self, RequestBody};
|
||||
use chrony_candm::{blocking_query, ClientOptions};
|
||||
use chrony_candm::{ClientOptions, blocking_query};
|
||||
use influxdb2::models::DataPoint;
|
||||
use log::{info, warn};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tokio::join;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub struct ChronyClient {
|
||||
pub server: SocketAddr,
|
||||
@@ -15,82 +19,136 @@ pub struct ChronyClient {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
fn datapoint_from_tracking(
|
||||
t: &reply::Tracking,
|
||||
config: &Config,
|
||||
) -> Result<DataPoint, Box<dyn std::error::Error>> {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
let measurement = config.sources.chrony.measurement_prefix.to_owned()
|
||||
+ &config.sources.chrony.tracking_measurement;
|
||||
let mut builder =
|
||||
DataPoint::builder(&measurement).timestamp(now.as_nanos().try_into().unwrap());
|
||||
for (key, value) in &config.influxdb.tags {
|
||||
builder = builder.tag(key, value);
|
||||
}
|
||||
|
||||
let point = builder
|
||||
.field("ref_id", t.ref_id as i64)
|
||||
.field("ref_ip_addr", t.ip_addr.to_string())
|
||||
.field("stratum", t.stratum as i64)
|
||||
.field("leap_status", t.leap_status as i64)
|
||||
.field("current_correction", f64::from(t.current_correction))
|
||||
.field("last_offset", f64::from(t.last_offset))
|
||||
.field("rms_offset", f64::from(t.rms_offset))
|
||||
.field("freq_ppm", f64::from(t.freq_ppm))
|
||||
.field("resid_freq_ppm", f64::from(t.resid_freq_ppm))
|
||||
.field("skew_ppm", f64::from(t.skew_ppm))
|
||||
.field("root_delay", f64::from(t.root_delay))
|
||||
.field("root_dispersion", f64::from(t.root_dispersion))
|
||||
.field("last_update_interval", f64::from(t.last_update_interval))
|
||||
.build()?;
|
||||
|
||||
Ok(point)
|
||||
#[derive(Debug)]
|
||||
pub struct ChronyTrackingReport {
|
||||
tags: Arc<Vec<(String, String)>>,
|
||||
pub ref_id: i64,
|
||||
pub ref_ip_addr: String,
|
||||
pub stratum: i64,
|
||||
pub leap_status: i64,
|
||||
pub current_correction: f64,
|
||||
pub last_offset: f64,
|
||||
pub rms_offset: f64,
|
||||
pub freq_ppm: f64,
|
||||
pub resid_freq_ppm: f64,
|
||||
pub skew_ppm: f64,
|
||||
pub root_delay: f64,
|
||||
pub root_dispersion: f64,
|
||||
pub last_update_interval: f64,
|
||||
}
|
||||
|
||||
pub fn datapoint_from_sourcedata(
|
||||
d: &reply::SourceData,
|
||||
config: &Config,
|
||||
) -> Result<DataPoint, Box<dyn std::error::Error>> {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
let measurement = config.sources.chrony.measurement_prefix.to_owned()
|
||||
+ &config.sources.chrony.sources_measurement;
|
||||
let mut builder =
|
||||
DataPoint::builder(&measurement).timestamp(now.as_nanos().try_into().unwrap());
|
||||
for (key, value) in &config.influxdb.tags {
|
||||
builder = builder.tag(key, value)
|
||||
impl SourceReportDetails for ChronyTrackingReport {
|
||||
fn is_healthy(&self) -> bool {
|
||||
true
|
||||
}
|
||||
builder = builder
|
||||
.tag("ref_id", d.ip_addr.to_string())
|
||||
.tag(
|
||||
"mode",
|
||||
match d.mode {
|
||||
SourceMode::Client => String::from("server"),
|
||||
SourceMode::Peer => String::from("peer"),
|
||||
SourceMode::Ref => String::from("refclock"),
|
||||
},
|
||||
)
|
||||
.tag(
|
||||
"state",
|
||||
match d.state {
|
||||
reply::SourceState::Selected => String::from("best"),
|
||||
reply::SourceState::NonSelectable => String::from("unusable"),
|
||||
reply::SourceState::Falseticker => String::from("falseticker"),
|
||||
reply::SourceState::Jittery => String::from("jittery"),
|
||||
reply::SourceState::Unselected => String::from("combined"),
|
||||
reply::SourceState::Selectable => String::from("unused"),
|
||||
},
|
||||
)
|
||||
.field("poll", d.poll as i64)
|
||||
.field("stratum", d.stratum as i64)
|
||||
.field("flags", d.flags.bits() as i64)
|
||||
.field("reachability", d.reachability.count_ones() as i64)
|
||||
.field("since_sample", d.since_sample as i64)
|
||||
.field("orig_latest_meas", f64::from(d.orig_latest_meas))
|
||||
.field("latest_meas", f64::from(d.latest_meas))
|
||||
.field("latest_meas_err", f64::from(d.latest_meas_err));
|
||||
let point = builder.build()?;
|
||||
fn to_metrics(&self) -> Vec<SourceMetric> {
|
||||
let tags = &self.tags;
|
||||
vec![
|
||||
SourceMetric::new_int("ref_id", self.ref_id, tags.clone()),
|
||||
SourceMetric::new_int("stratum", self.stratum, tags.clone()),
|
||||
SourceMetric::new_int("leap_status", self.leap_status, tags.clone()),
|
||||
SourceMetric::new_float("current_correction", self.current_correction, tags.clone()),
|
||||
SourceMetric::new_float("last_offset", self.last_offset, tags.clone()),
|
||||
SourceMetric::new_float("rms_offset", self.rms_offset, tags.clone()),
|
||||
SourceMetric::new_float("freq_ppm", self.freq_ppm, tags.clone()),
|
||||
SourceMetric::new_float("resid_freq_ppm", self.resid_freq_ppm, tags.clone()),
|
||||
SourceMetric::new_float("skew_ppm", self.skew_ppm, tags.clone()),
|
||||
SourceMetric::new_float("root_delay", self.root_delay, tags.clone()),
|
||||
SourceMetric::new_float("root_dispersion", self.root_dispersion, tags.clone()),
|
||||
SourceMetric::new_float(
|
||||
"last_update_interval",
|
||||
self.last_update_interval,
|
||||
tags.clone(),
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Ok(point)
|
||||
#[derive(Debug)]
|
||||
pub struct ChronySourcesReport {
|
||||
pub sources: Vec<reply::SourceData>,
|
||||
}
|
||||
|
||||
impl SourceReportDetails for ChronySourcesReport {
|
||||
fn is_healthy(&self) -> bool {
|
||||
//TODO: think about whether there is an idea of unhealthy sources
|
||||
true
|
||||
}
|
||||
fn to_metrics(&self) -> Vec<SourceMetric> {
|
||||
let mut metrics = Vec::with_capacity(8 * self.sources.len());
|
||||
|
||||
for source in &self.sources {
|
||||
let tags = Arc::new(vec![
|
||||
("ref_id".to_owned(), source.ip_addr.to_string()),
|
||||
(
|
||||
"mode".to_owned(),
|
||||
match source.mode {
|
||||
SourceMode::Client => String::from("server"),
|
||||
SourceMode::Peer => String::from("peer"),
|
||||
SourceMode::Ref => String::from("refclock"),
|
||||
},
|
||||
),
|
||||
(
|
||||
"state".to_owned(),
|
||||
match source.state {
|
||||
reply::SourceState::Selected => String::from("best"),
|
||||
reply::SourceState::NonSelectable => String::from("unusable"),
|
||||
reply::SourceState::Falseticker => String::from("falseticker"),
|
||||
reply::SourceState::Jittery => String::from("jittery"),
|
||||
reply::SourceState::Unselected => String::from("combined"),
|
||||
reply::SourceState::Selectable => String::from("unused"),
|
||||
},
|
||||
),
|
||||
]);
|
||||
metrics.extend([
|
||||
SourceMetric::new_int("poll", source.poll as i64, tags.clone()),
|
||||
SourceMetric::new_int("stratum", source.stratum as i64, tags.clone()),
|
||||
SourceMetric::new_int("flags", source.flags.bits() as i64, tags.clone()),
|
||||
SourceMetric::new_int(
|
||||
"reachability",
|
||||
source.reachability.count_ones() as i64,
|
||||
tags.clone(),
|
||||
),
|
||||
SourceMetric::new_int("since_sample", source.since_sample as i64, tags.clone()),
|
||||
SourceMetric::new_float(
|
||||
"orig_latest_meas",
|
||||
source.orig_latest_meas.into(),
|
||||
tags.clone(),
|
||||
),
|
||||
SourceMetric::new_float("latest_meas", source.latest_meas.into(), tags.clone()),
|
||||
SourceMetric::new_float(
|
||||
"latest_meas_err",
|
||||
source.latest_meas_err.into(),
|
||||
tags.clone(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
metrics
|
||||
}
|
||||
}
|
||||
|
||||
fn report_from_tracking(
|
||||
t: &reply::Tracking,
|
||||
config: &Config,
|
||||
) -> Result<ChronyTrackingReport, Box<dyn std::error::Error>> {
|
||||
let report = ChronyTrackingReport {
|
||||
tags: Arc::new(vec![]), //TODO: allow configuring tags in the source
|
||||
ref_id: t.ref_id as i64,
|
||||
ref_ip_addr: t.ip_addr.to_string(),
|
||||
stratum: t.stratum as i64,
|
||||
leap_status: t.leap_status as i64,
|
||||
current_correction: t.current_correction.into(),
|
||||
last_offset: t.last_offset.into(),
|
||||
rms_offset: t.rms_offset.into(),
|
||||
freq_ppm: t.freq_ppm.into(),
|
||||
resid_freq_ppm: t.resid_freq_ppm.into(),
|
||||
skew_ppm: t.skew_ppm.into(),
|
||||
root_delay: t.root_delay.into(),
|
||||
root_dispersion: t.root_dispersion.into(),
|
||||
last_update_interval: t.last_update_interval.into(),
|
||||
};
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
impl ChronyClient {
|
||||
@@ -206,13 +264,16 @@ impl ChronyClient {
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tracking = self.get_tracking().await?;
|
||||
|
||||
let tracking_data = datapoint_from_tracking(&tracking, &self.config)?;
|
||||
let tracking_data = report_from_tracking(&tracking, &self.config)?;
|
||||
let report = SourceReport {
|
||||
name: "chrony-tracking".to_owned(),
|
||||
status: SourceStatus::Unknown,
|
||||
details: Arc::new(tracking_data),
|
||||
};
|
||||
|
||||
info!("Sending tracking data");
|
||||
|
||||
chan.send(tracking_data.into())
|
||||
.expect("Unable to send tracking data to targets");
|
||||
|
||||
chan.send(report.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -221,14 +282,14 @@ impl ChronyClient {
|
||||
chan: &ChimemonSourceChannel,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sources = self.get_sources().await?;
|
||||
let mut dps = Vec::with_capacity(sources.len());
|
||||
for ds in sources {
|
||||
let source_data = datapoint_from_sourcedata(&ds, &self.config)?;
|
||||
dps.push(source_data);
|
||||
}
|
||||
let details = ChronySourcesReport { sources };
|
||||
let report = SourceReport {
|
||||
name: "chrony-sources".to_owned(),
|
||||
status: SourceStatus::Unknown,
|
||||
details: Arc::new(details),
|
||||
};
|
||||
info!("Sending source data");
|
||||
chan.send(dps.into())
|
||||
.expect("Unable to send source data to targets");
|
||||
chan.send(report.into())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user