Files
chimemon/src/lib.rs

315 lines
7.2 KiB
Rust

pub mod chrony;
pub mod chrony_refclock;
pub mod gpsd;
pub mod hwmon;
pub mod prs10;
pub mod uccm;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use figment::{
Figment,
providers::{Format, Serialized, Toml},
util::map,
value::Map,
};
use gethostname::gethostname;
use influxdb2::models::DataPoint;
use serde_derive::{Deserialize, Serialize};
use tokio::sync::broadcast::*;
use std::{fmt::Debug, net::SocketAddr, path::Path, sync::Arc};
#[derive(Serialize, Deserialize, Clone)]
pub struct InfluxConfig {
pub enabled: bool,
pub url: String,
pub org: String,
pub bucket: String,
pub token: String,
pub tags: Map<String, String>,
}
impl Default for InfluxConfig {
fn default() -> Self {
let host = gethostname().into_string().unwrap();
InfluxConfig {
enabled: false,
url: "http://localhost:8086".into(),
org: "default".into(),
bucket: "default".into(),
token: "".into(),
tags: map! { "host".into() => host },
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ChronyConfig {
pub enabled: bool,
pub timeout: u64,
pub tracking_interval: u64,
pub sources_interval: u64,
pub measurement_prefix: String,
pub tracking_measurement: String,
pub sources_measurement: String,
pub host: String,
}
impl Default for ChronyConfig {
fn default() -> Self {
ChronyConfig {
enabled: false,
timeout: 5,
tracking_interval: 60,
sources_interval: 300,
measurement_prefix: "chrony.".into(),
tracking_measurement: "tracking".into(),
sources_measurement: "sources".into(),
host: "127.0.0.1:323".into(),
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ChronySockConfig {
pub enabled: bool,
pub sock: String,
}
impl Default for ChronySockConfig {
fn default() -> Self {
ChronySockConfig {
enabled: false,
sock: "".into(),
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct HwmonSensorConfig {
pub name: String,
pub sensor: String,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct HwmonConfig {
pub enabled: bool,
pub interval: u64,
pub measurement: String,
pub sensors: Map<String, HwmonSensorConfig>,
}
impl Default for HwmonConfig {
fn default() -> Self {
HwmonConfig {
enabled: false,
interval: 60,
measurement: "hwmon".into(),
sensors: map! {},
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct GpsdConfig {
pub enabled: bool,
pub interval: u64,
pub host: String,
}
impl Default for GpsdConfig {
fn default() -> Self {
GpsdConfig {
enabled: false,
interval: 60,
host: "localhost:2947".into(),
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct Prs10Config {
pub enabled: bool,
pub port: String,
pub baud: u32,
pub timeout: std::time::Duration,
pub status_interval: std::time::Duration,
pub stats_interval: std::time::Duration,
}
impl Default for Prs10Config {
fn default() -> Self {
Prs10Config {
enabled: false,
port: "/dev/ttyS0".into(),
baud: 9600,
timeout: std::time::Duration::from_secs(1),
status_interval: std::time::Duration::from_secs(10),
stats_interval: std::time::Duration::from_secs(10),
}
}
}
#[derive(Clone, Debug)]
pub struct TimeReport {
pub system_time: DateTime<Utc>,
pub offset: chrono::Duration,
pub leaps: isize,
pub leap_flag: bool,
pub valid: bool,
}
#[derive(Clone, Debug)]
pub enum SourceStatus {
Healthy,
LossOfSignal(Option<String>),
LossOfSync(Option<String>),
Other(Option<String>),
Unknown,
}
#[derive(Clone, Debug)]
pub enum MetricValue {
Int(i64),
Float(f64),
Bool(bool),
}
#[derive(Clone, Debug)]
pub struct SourceMetric {
name: String,
value: MetricValue,
tags: Arc<Vec<(String, String)>>,
}
impl SourceMetric {
pub fn new_int(name: &str, value: i64, tags: Arc<Vec<(String, String)>>) -> Self {
Self {
name: name.to_owned(),
value: MetricValue::Int(value),
tags,
}
}
pub fn new_float(name: &str, value: f64, tags: Arc<Vec<(String, String)>>) -> Self {
Self {
name: name.to_owned(),
value: MetricValue::Float(value),
tags,
}
}
pub fn new_bool(name: &str, value: bool, tags: Arc<Vec<(String, String)>>) -> Self {
Self {
name: name.to_owned(),
value: MetricValue::Bool(value),
tags,
}
}
}
pub trait SourceReportDetails: Debug + Send + Sync {
fn to_metrics(&self) -> Vec<SourceMetric>;
fn is_healthy(&self) -> bool;
}
#[derive(Clone, Debug)]
pub struct SourceReport {
pub name: String,
pub status: SourceStatus,
pub details: Arc<dyn SourceReportDetails>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct UCCMConfig {
pub enabled: bool,
pub port: String,
pub baud: u32,
pub status_interval: std::time::Duration,
pub timeout: std::time::Duration,
pub measurement: String,
}
impl Default for UCCMConfig {
fn default() -> Self {
UCCMConfig {
enabled: false,
port: "/dev/ttyS0".into(),
baud: 57600,
status_interval: std::time::Duration::from_secs(10),
timeout: std::time::Duration::from_secs(1),
measurement: "uccm_gpsdo".into(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct SourcesConfig {
pub chrony: ChronyConfig,
pub hwmon: HwmonConfig,
pub uccm: UCCMConfig,
pub gpsd: GpsdConfig,
pub prs10: Prs10Config,
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct TargetsConfig {
pub chrony: ChronySockConfig,
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct Config {
pub influxdb: InfluxConfig,
pub sources: SourcesConfig,
pub targets: TargetsConfig,
}
pub fn load_config(filename: &Path) -> Figment {
Figment::from(Serialized::defaults(Config::default())).merge(Toml::file(filename))
}
#[derive(Debug, Clone)]
pub enum ChimemonMessage {
DataPoint(DataPoint),
DataPoints(Vec<DataPoint>),
TimeReport(TimeReport),
SourceReport(SourceReport),
}
impl From<DataPoint> for ChimemonMessage {
fn from(dp: DataPoint) -> Self {
ChimemonMessage::DataPoint(dp)
}
}
impl From<Vec<DataPoint>> for ChimemonMessage {
fn from(dps: Vec<DataPoint>) -> Self {
ChimemonMessage::DataPoints(dps)
}
}
impl From<TimeReport> for ChimemonMessage {
fn from(tr: TimeReport) -> Self {
ChimemonMessage::TimeReport(tr)
}
}
impl From<SourceReport> for ChimemonMessage {
fn from(sr: SourceReport) -> Self {
ChimemonMessage::SourceReport(sr)
}
}
pub type ChimemonSourceChannel = Sender<ChimemonMessage>;
pub type ChimemonTargetChannel = Receiver<ChimemonMessage>;
#[async_trait]
pub trait ChimemonSource {
async fn run(self, chan: ChimemonSourceChannel);
}
#[async_trait]
pub trait ChimemonTarget {
async fn run(self, chan: ChimemonTargetChannel);
}