pfusch
This commit is contained in:
parent
54f109f48a
commit
8147aa8e88
5 changed files with 175 additions and 157 deletions
|
@ -8,10 +8,9 @@ edition = "2021"
|
|||
[dependencies]
|
||||
config = { version = "0.13.1", features = ["yaml"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
prometheus-client = "0.18.1"
|
||||
prometheus-client = "0.19.0"
|
||||
futures-util = "0.3.25"
|
||||
actix-web-httpauth = "0.8.0"
|
||||
actix-web = "4.2.1"
|
||||
tokio = "1.21.2"
|
||||
axum = "0.6.12"
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread","macros"] }
|
||||
rsvici = "0.1"
|
||||
anyhow = "1.0.70"
|
||||
|
|
134
src/main.rs
134
src/main.rs
|
@ -1,122 +1,104 @@
|
|||
#![allow(dead_code,non_camel_case_types)] // I don't want to be bothered for case stuff decided upon me by the VICI API
|
||||
#![allow(dead_code)]
|
||||
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, Responder, Result};
|
||||
use axum::{
|
||||
response::IntoResponse,
|
||||
http::{
|
||||
StatusCode,
|
||||
header::{self}
|
||||
},
|
||||
extract::State,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
|
||||
use prometheus_client::{
|
||||
registry::Registry,
|
||||
encoding::{text::encode},
|
||||
metrics::{
|
||||
family::Family,
|
||||
info::Info,
|
||||
counter::Counter,
|
||||
gauge::Gauge,
|
||||
MetricType::Unknown,
|
||||
}
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
sync::Mutex,
|
||||
net::IpAddr,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
|
||||
use futures_util::{
|
||||
stream::{TryStreamExt},
|
||||
pin_mut,
|
||||
// collections::HashMap,
|
||||
// error::Error,
|
||||
sync::Arc,
|
||||
net::{IpAddr,SocketAddr},
|
||||
// path::Path,
|
||||
};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use config::Config;
|
||||
|
||||
mod vici_structs;
|
||||
pub mod vici;
|
||||
pub mod metrics;
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Configuration {
|
||||
vici_socket: String,
|
||||
actix_bind_addr: IpAddr,
|
||||
actix_bind_port: u16,
|
||||
axtix_auth_token: Option<String>,
|
||||
axum_bind_addr: IpAddr,
|
||||
axum_bind_port: u16,
|
||||
}
|
||||
|
||||
pub async fn metrics_handler(state: web::Data<Mutex<AppState>>) -> Result<HttpResponse> {
|
||||
let state = state.lock().unwrap();
|
||||
let mut buf = Vec::new();
|
||||
encode(&mut buf, &state.registry)?;
|
||||
let body = std::str::from_utf8(buf.as_slice()).unwrap().to_string();
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/openmetrics-text; version=1.0.0; charset=utf-8")
|
||||
.body(body))
|
||||
pub async fn metrics_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
|
||||
let state: Arc<AppState> = state.clone();
|
||||
let mut buffer = String::new();
|
||||
prometheus_client::encoding::text::encode(&mut buffer, &state.registry).unwrap();
|
||||
(
|
||||
StatusCode::OK,
|
||||
[(header::CONTENT_TYPE, "application/openmetrics-text; version=1.0.0; charset=utf-8")],
|
||||
buffer,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
pub registry: Registry,
|
||||
pub vici: vici_structs::VICIState,
|
||||
pub vici: vici::VICIState,
|
||||
}
|
||||
|
||||
pub struct Metrics {
|
||||
sa_uptime: Family<vici_structs::SecurityAssociationLabels, Counter>,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
pub fn sa_uptime(&self, security_associations: vici_structs::SecurityAssociations) {
|
||||
for (sa_name, sa_values) in security_associations.into_iter() {
|
||||
self.sa_uptime.get_or_create(&vici_structs::SecurityAssociationLabels{uniqueid: sa_values.uniqueid}).inner(sa_values.established); // "Vertrau mir Bruder"
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
pub fn get_vici_state(client: rsvici::Client) -> Result<vici_structs::VICIState, actix_web::Error>{
|
||||
let version: vici_structs::Version = client.request("version", ()).await?;
|
||||
let statistics: vici_structs::Statistics = client.request("statistics", ()).await?;
|
||||
let connections = client.stream_request::<(), vici_structs::Connections>("list-connections", "list-conn", ());
|
||||
pin_mut!(connections);
|
||||
while let Some(conn) = connections.try_next().await? {
|
||||
println!("{:#?}", conn);
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let settings = Config::builder()
|
||||
.add_source(config::File::with_name("config"))
|
||||
.add_source(config::Environment::with_prefix("VICI_EXPORTER"))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut conf: Configuration = settings.try_deserialize().unwrap();
|
||||
let mut client = rsvici::unix::connect(conf.vici_socket).await?;
|
||||
let mut vici_state: vici_structs::VICIState;
|
||||
let mut vici_client = rsvici::unix::connect(conf.vici_socket).await?;
|
||||
let mut vici_state: vici::VICIState;
|
||||
|
||||
let metrics = web::Data::new(Metrics {
|
||||
let metrics = Arc::new(metrics::Metrics {
|
||||
sa_uptime: Family::default(),
|
||||
});
|
||||
|
||||
let mut state = AppState {
|
||||
registry: Registry::default(),
|
||||
vici: vici_structs::VICIState.update(client),
|
||||
};
|
||||
let mut initial_registery = Registry::default();
|
||||
|
||||
state.registry.register(
|
||||
initial_registery.register(
|
||||
"sa_uptime",
|
||||
"How Long a connection has been established",
|
||||
Box::new(metrics.sa_uptime.clone()),
|
||||
metrics.sa_uptime.clone(),
|
||||
);
|
||||
let state = web::Data::new(Mutex::new(state));
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
//.app_data(metrics.clone())
|
||||
.app_data(state.clone())
|
||||
.service(web::resource("/metrics").route(web::get().to(metrics_handler)))
|
||||
})
|
||||
.bind((conf.actix_bind_addr, conf.actix_bind_port))?
|
||||
.run()
|
||||
.await
|
||||
let mut state = Arc::new(
|
||||
AppState {
|
||||
registry: initial_registery,
|
||||
vici: vici::VICIState::update(&mut vici_client).await?,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
|
||||
let addr = SocketAddr::from((conf.axum_bind_addr,conf.axum_bind_port));
|
||||
let app = Router::new()
|
||||
.route("/metrics",get(metrics_handler))
|
||||
.with_state(state);
|
||||
|
||||
axum::Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
25
src/metrics.rs
Normal file
25
src/metrics.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use prometheus_client::{
|
||||
metrics::{
|
||||
family::Family,
|
||||
gauge::Gauge,
|
||||
}
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::vici;
|
||||
pub mod labels;
|
||||
|
||||
pub struct Metrics {
|
||||
pub sa_uptime: Family<labels::SecurityAssociationLabels, Gauge>,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
pub async fn sa_uptime(&self, security_associations: vici::SecurityAssociations) -> Result<()>{
|
||||
for named_sa in security_associations.into_iter() {
|
||||
let label_set = labels::SecurityAssociationLabels::set_from_sa(&mut named_sa).await?;
|
||||
let (_sa_name, sa_value) = named_sa;
|
||||
self.sa_uptime.get_or_create(&label_set).set(sa_value.established as i64);
|
||||
}
|
||||
Ok(());
|
||||
}
|
||||
}
|
65
src/metrics/labels.rs
Normal file
65
src/metrics/labels.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use serde::Deserialize;
|
||||
use prometheus_client::encoding::{EncodeLabelValue,EncodeLabelSet};
|
||||
|
||||
use crate::vici;
|
||||
use anyhow::Result;
|
||||
|
||||
|
||||
// I don't really wanna define *all* of this here, it's gonna get really tedious and uncomfortable to maintain.
|
||||
|
||||
/*
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct SecurityAssociationLabels {
|
||||
pub uniqueid: String,
|
||||
pub local_id: String,
|
||||
pub local_host: String,
|
||||
pub local_port: u16,
|
||||
pub remote_id: String,
|
||||
pub remote_host: String,
|
||||
pub remote_port: u16,
|
||||
}
|
||||
*/
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct SecurityAssociationInfo {
|
||||
pub uniqueid: String,
|
||||
pub version: u8,
|
||||
pub local_host: String,
|
||||
pub local_port: u16,
|
||||
pub local_id: String,
|
||||
pub remote_host: String,
|
||||
pub remote_port: u16,
|
||||
pub remote_id: String,
|
||||
pub if_id_in: String,
|
||||
pub if_id_out: String,
|
||||
pub encr_alg: String,
|
||||
pub encr_keysize: String,
|
||||
pub integ_alg: String,
|
||||
pub integ_keysize: String,
|
||||
pub prf_alg: String,
|
||||
pub dh_group: Option<String>,
|
||||
pub local_vips: Vec<String>,
|
||||
pub remote_vips: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, EncodeLabelSet)]
|
||||
pub struct SecurityAssociationLabels {
|
||||
pub name: String,
|
||||
pub uniqueid: String,
|
||||
pub ike_version: u8,
|
||||
pub local_id: String,
|
||||
pub remote_id: String,
|
||||
}
|
||||
|
||||
|
||||
impl SecurityAssociationLabels {
|
||||
pub async fn set_from_sa(sa: &mut vici::NamedSecurityAssociation) -> Result<SecurityAssociationLabels> {
|
||||
let (sa_name, sa_value) = sa;
|
||||
Ok(SecurityAssociationLabels {
|
||||
name: sa_name,
|
||||
uniqueid: sa_value.uniqueid,
|
||||
ike_version: sa_value.version,
|
||||
local_id: sa_value.local_id.unwrap(),
|
||||
remote_id: sa_value.remote_id.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
#![allow(dead_code,non_camel_case_types)] // I don't want to be bothered for case stuff decided upon me by the VICI API
|
||||
#![allow(dead_code)]
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use futures_util:: stream::StreamExt;
|
||||
|
||||
use prometheus_client::encoding::text::Encode;
|
||||
use futures_util::stream::StreamExt;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
|
@ -22,16 +20,16 @@ pub struct VICIState {
|
|||
}
|
||||
|
||||
impl VICIState {
|
||||
async fn update(client: &mut rsvici::Client) -> Result<VICIState> {
|
||||
pub async fn update(client: &mut rsvici::Client) -> Result<VICIState> {
|
||||
Ok(VICIState {
|
||||
version: client.request("version", ()).await?,
|
||||
statistics: client.request("statistics", ()).await?,
|
||||
policies: collected_stream::<NamedPolicy, Policies>(client, "list-policies", "list-policy").await,
|
||||
connections: client.stream_request::<(), Connections>("list-connections", "list-conn", ()).await?,
|
||||
security_associations: client.stream_request::<(), SecurityAssociations>("list-sas", "list-sa", ()).await?,
|
||||
certificates: client.stream_request::<(), Certificates>("list-certs", "list-cert", ()).await?,
|
||||
authorities: client.stream_request::<(), Authorities>("list-authoroties", "list-authoroty", ()).await?,
|
||||
pools: client.stream_request::<(), Pools>("list-pools", "list-pool", ()).await?,
|
||||
connections: collected_stream::<NamedConnection, Connections>(client, "list-connections", "list-conn").await,
|
||||
security_associations: collected_stream::<NamedSecurityAssociation, SecurityAssociations>(client, "list-sas", "list-sa").await,
|
||||
certificates: collected_stream::<NamedCertificate, Certificates>(client, "list-certs", "list-cert").await,
|
||||
authorities: collected_stream::<NamedAuthority, Authorities>(client, "list-authorities", "list-authority").await,
|
||||
pools: collected_stream::<NamedPool, Pools>(client, "list-pools", "list-pool").await,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +119,7 @@ pub struct Policy {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum PolicyMode {
|
||||
pub enum PolicyMode {
|
||||
tunnel,
|
||||
transport,
|
||||
pass,
|
||||
|
@ -130,6 +128,8 @@ enum PolicyMode {
|
|||
|
||||
pub type Connections = HashMap<String, Conn>;
|
||||
|
||||
pub type NamedConnection = (String, Conn);
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Conn {
|
||||
pub local_addrs: Vec<String>,
|
||||
|
@ -167,21 +167,22 @@ pub struct ConnChildSection {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
enum ChildSecurityAssociationMode {
|
||||
pub enum ChildSecurityAssociationMode {
|
||||
TUNNEL,
|
||||
TRANSPORT,
|
||||
BEET,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
enum ChildSecurityAssociationProtocol {
|
||||
pub enum ChildSecurityAssociationProtocol {
|
||||
AH,
|
||||
ESP,
|
||||
}
|
||||
|
||||
pub type SecurityAssociations = HashMap<String, SecurityAssociation>;
|
||||
|
||||
pub type NamedSecurityAssociation = (String, SecurityAssociation);
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct SecurityAssociation {
|
||||
pub uniqueid: String,
|
||||
pub version: u8,
|
||||
|
@ -221,7 +222,6 @@ pub struct SecurityAssociation {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct SecurityAssociationChild {
|
||||
pub name: String,
|
||||
pub uniqueid: String,
|
||||
|
@ -262,6 +262,8 @@ pub struct SecurityAssociationChild {
|
|||
|
||||
pub type Certificates = HashMap<String, Cert>;
|
||||
|
||||
pub type NamedCertificate = (String, Cert);
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Cert {
|
||||
pub r#type: CertType,
|
||||
|
@ -274,7 +276,7 @@ pub struct Cert {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum CertType {
|
||||
pub enum CertType {
|
||||
X509,
|
||||
X509_AC,
|
||||
X509_CRL,
|
||||
|
@ -283,7 +285,7 @@ enum CertType {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum X509CertFlag {
|
||||
pub enum X509CertFlag {
|
||||
NONE,
|
||||
CA,
|
||||
AA,
|
||||
|
@ -292,6 +294,8 @@ enum X509CertFlag {
|
|||
|
||||
pub type Authorities = HashMap<String, Authority>;
|
||||
|
||||
pub type NamedAuthority = (String, Authority);
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Authority {
|
||||
pub cacert: String,
|
||||
|
@ -302,6 +306,8 @@ pub struct Authority {
|
|||
|
||||
pub type Pools = HashMap<String, Pool>;
|
||||
|
||||
pub type NamedPool = (String, Pool);
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct Pool {
|
||||
pub name: String,
|
||||
|
@ -320,66 +326,7 @@ pub struct PoolLease {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum PoolLeaseStatus {
|
||||
pub enum PoolLeaseStatus {
|
||||
online,
|
||||
offline,
|
||||
}
|
||||
|
||||
// Structs for generating metrics, TODO
|
||||
|
||||
/*
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct SecurityAssociationLabels {
|
||||
pub uniqueid: String,
|
||||
pub local_id: String,
|
||||
pub local_host: String,
|
||||
pub local_port: u16,
|
||||
pub remote_id: String,
|
||||
pub remote_host: String,
|
||||
pub remote_port: u16,
|
||||
}
|
||||
*/
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct SecurityAssociationInfo {
|
||||
pub uniqueid: String,
|
||||
pub version: u8,
|
||||
pub local_host: String,
|
||||
pub local_port: u16,
|
||||
pub local_id: String,
|
||||
pub remote_host: String,
|
||||
pub remote_port: u16,
|
||||
pub remote_id: String,
|
||||
pub if_id_in: String,
|
||||
pub if_id_out: String,
|
||||
pub encr_alg: String,
|
||||
pub encr_keysize: String,
|
||||
pub integ_alg: String,
|
||||
pub integ_keysize: String,
|
||||
pub prf_alg: String,
|
||||
pub dh_group: Option<String>,
|
||||
pub local_vips: Vec<String>,
|
||||
pub remote_vips: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct SecurityAssociationChildInfo {
|
||||
pub name: String,
|
||||
pub uniqueid: String,
|
||||
pub reqid: String,
|
||||
pub mode: ChildSecurityAssociationMode,
|
||||
pub if_id_in: String,
|
||||
pub if_id_out: String,
|
||||
pub encr_alg: String,
|
||||
pub encr_keysize: String,
|
||||
pub integ_alg: String,
|
||||
pub integ_keysize: String,
|
||||
pub prf_alg: String,
|
||||
pub dh_group: Option<String>,
|
||||
pub local_ts: Vec<String>,
|
||||
pub remote_ts: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
|
||||
pub struct SecurityAssociationLabels {
|
||||
pub uniqueid: String,
|
||||
}
|
Loading…
Add table
Reference in a new issue