Refactor for additional socktop connector library
- socktop connector allows you to communicate with socktop agent directly from you code without needing to implement the agent API directly. - will also be used for non tui implementation of "socktop collector" in the future. - moved to rust 2024 to take advantage of some new features that helped with refactor. - fixed everything that exploded with update. - added rust docs for lib.
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
//! WebSocket connector for communicating with socktop agents.
|
||||
|
||||
use flate2::bufread::GzDecoder;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use prost::Message as _;
|
||||
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
||||
use rustls::{ClientConfig, RootCertStore};
|
||||
use rustls::{DigitallySignedStruct, SignatureScheme};
|
||||
use rustls_pemfile::Item;
|
||||
use std::io::Read;
|
||||
use std::{fs::File, io::BufReader, sync::Arc};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{
|
||||
Connector, MaybeTlsStream, WebSocketStream, connect_async, connect_async_tls_with_config,
|
||||
tungstenite::Message, tungstenite::client::IntoClientRequest,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::types::{AgentRequest, AgentResponse, DiskInfo, Metrics, ProcessInfo, ProcessesPayload};
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
fn ensure_crypto_provider() {
|
||||
use std::sync::Once;
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
});
|
||||
}
|
||||
|
||||
mod pb {
|
||||
// generated by build.rs
|
||||
include!(concat!(env!("OUT_DIR"), "/socktop.rs"));
|
||||
}
|
||||
|
||||
pub type WsStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||
|
||||
/// Configuration for connecting to a socktop agent
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectorConfig {
|
||||
pub url: String,
|
||||
pub tls_ca_path: Option<String>,
|
||||
pub verify_hostname: bool,
|
||||
}
|
||||
|
||||
impl ConnectorConfig {
|
||||
pub fn new(url: impl Into<String>) -> Self {
|
||||
Self {
|
||||
url: url.into(),
|
||||
tls_ca_path: None,
|
||||
verify_hostname: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_tls_ca(mut self, ca_path: impl Into<String>) -> Self {
|
||||
self.tls_ca_path = Some(ca_path.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_hostname_verification(mut self, verify: bool) -> Self {
|
||||
self.verify_hostname = verify;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A WebSocket connector for communicating with socktop agents
|
||||
pub struct SocktopConnector {
|
||||
config: ConnectorConfig,
|
||||
stream: Option<WsStream>,
|
||||
}
|
||||
|
||||
impl SocktopConnector {
|
||||
/// Create a new connector with the given configuration
|
||||
pub fn new(config: ConnectorConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
stream: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect to the agent
|
||||
pub async fn connect(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let stream = connect_to_agent(
|
||||
&self.config.url,
|
||||
self.config.tls_ca_path.as_deref(),
|
||||
self.config.verify_hostname,
|
||||
)
|
||||
.await?;
|
||||
self.stream = Some(stream);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a request to the agent and get the response
|
||||
pub async fn request(
|
||||
&mut self,
|
||||
request: AgentRequest,
|
||||
) -> Result<AgentResponse, Box<dyn std::error::Error>> {
|
||||
let stream = self.stream.as_mut().ok_or("Not connected")?;
|
||||
|
||||
match request {
|
||||
AgentRequest::Metrics => {
|
||||
let metrics = request_metrics(stream)
|
||||
.await
|
||||
.ok_or("Failed to get metrics")?;
|
||||
Ok(AgentResponse::Metrics(metrics))
|
||||
}
|
||||
AgentRequest::Disks => {
|
||||
let disks = request_disks(stream).await.ok_or("Failed to get disks")?;
|
||||
Ok(AgentResponse::Disks(disks))
|
||||
}
|
||||
AgentRequest::Processes => {
|
||||
let processes = request_processes(stream)
|
||||
.await
|
||||
.ok_or("Failed to get processes")?;
|
||||
Ok(AgentResponse::Processes(processes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the connector is connected
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.stream.is_some()
|
||||
}
|
||||
|
||||
/// Disconnect from the agent
|
||||
pub async fn disconnect(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mut stream) = self.stream.take() {
|
||||
let _ = stream.close(None).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to the agent and return the WS stream
|
||||
async fn connect_to_agent(
|
||||
url: &str,
|
||||
tls_ca: Option<&str>,
|
||||
verify_hostname: bool,
|
||||
) -> Result<WsStream, Box<dyn std::error::Error>> {
|
||||
#[cfg(feature = "tls")]
|
||||
ensure_crypto_provider();
|
||||
|
||||
let mut u = Url::parse(url)?;
|
||||
if let Some(ca_path) = tls_ca {
|
||||
if u.scheme() == "ws" {
|
||||
let _ = u.set_scheme("wss");
|
||||
}
|
||||
return connect_with_ca(u.as_str(), ca_path, verify_hostname).await;
|
||||
}
|
||||
// No TLS - hostname verification is not applicable
|
||||
let (ws, _) = connect_async(u.as_str()).await?;
|
||||
Ok(ws)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
async fn connect_with_ca(
|
||||
url: &str,
|
||||
ca_path: &str,
|
||||
verify_hostname: bool,
|
||||
) -> Result<WsStream, Box<dyn std::error::Error>> {
|
||||
// Initialize the crypto provider for rustls
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
|
||||
let mut root = RootCertStore::empty();
|
||||
let mut reader = BufReader::new(File::open(ca_path)?);
|
||||
let mut der_certs = Vec::new();
|
||||
while let Ok(Some(item)) = rustls_pemfile::read_one(&mut reader) {
|
||||
if let Item::X509Certificate(der) = item {
|
||||
der_certs.push(der);
|
||||
}
|
||||
}
|
||||
root.add_parsable_certificates(der_certs);
|
||||
|
||||
let mut cfg = ClientConfig::builder()
|
||||
.with_root_certificates(root)
|
||||
.with_no_client_auth();
|
||||
|
||||
let req = url.into_client_request()?;
|
||||
if !verify_hostname {
|
||||
#[derive(Debug)]
|
||||
struct NoVerify;
|
||||
impl ServerCertVerifier for NoVerify {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &CertificateDer<'_>,
|
||||
_intermediates: &[CertificateDer<'_>],
|
||||
_server_name: &ServerName,
|
||||
_ocsp_response: &[u8],
|
||||
_now: UnixTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
Ok(ServerCertVerified::assertion())
|
||||
}
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &CertificateDer<'_>,
|
||||
_dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &CertificateDer<'_>,
|
||||
_dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||||
vec![
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
SignatureScheme::ED25519,
|
||||
SignatureScheme::RSA_PSS_SHA256,
|
||||
]
|
||||
}
|
||||
}
|
||||
cfg.dangerous().set_certificate_verifier(Arc::new(NoVerify));
|
||||
eprintln!(
|
||||
"socktop_connector: hostname verification disabled (default). Set SOCKTOP_VERIFY_NAME=1 to enable strict SAN checking."
|
||||
);
|
||||
}
|
||||
let cfg = Arc::new(cfg);
|
||||
let (ws, _) =
|
||||
connect_async_tls_with_config(req, None, verify_hostname, Some(Connector::Rustls(cfg)))
|
||||
.await?;
|
||||
Ok(ws)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
async fn connect_with_ca(
|
||||
_url: &str,
|
||||
_ca_path: &str,
|
||||
) -> Result<WsStream, Box<dyn std::error::Error>> {
|
||||
Err("TLS support not compiled in".into())
|
||||
}
|
||||
|
||||
// Send a "get_metrics" request and await a single JSON reply
|
||||
async fn request_metrics(ws: &mut WsStream) -> Option<Metrics> {
|
||||
if ws.send(Message::Text("get_metrics".into())).await.is_err() {
|
||||
return None;
|
||||
}
|
||||
match ws.next().await {
|
||||
Some(Ok(Message::Binary(b))) => {
|
||||
gunzip_to_string(&b).and_then(|s| serde_json::from_str::<Metrics>(&s).ok())
|
||||
}
|
||||
Some(Ok(Message::Text(json))) => serde_json::from_str::<Metrics>(&json).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Send a "get_disks" request and await a JSON Vec<DiskInfo>
|
||||
async fn request_disks(ws: &mut WsStream) -> Option<Vec<DiskInfo>> {
|
||||
if ws.send(Message::Text("get_disks".into())).await.is_err() {
|
||||
return None;
|
||||
}
|
||||
match ws.next().await {
|
||||
Some(Ok(Message::Binary(b))) => {
|
||||
gunzip_to_string(&b).and_then(|s| serde_json::from_str::<Vec<DiskInfo>>(&s).ok())
|
||||
}
|
||||
Some(Ok(Message::Text(json))) => serde_json::from_str::<Vec<DiskInfo>>(&json).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Send a "get_processes" request and await a ProcessesPayload decoded from protobuf (binary, may be gzipped)
|
||||
async fn request_processes(ws: &mut WsStream) -> Option<ProcessesPayload> {
|
||||
if ws
|
||||
.send(Message::Text("get_processes".into()))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
match ws.next().await {
|
||||
Some(Ok(Message::Binary(b))) => {
|
||||
let gz = is_gzip(&b);
|
||||
let data = if gz { gunzip_to_vec(&b)? } else { b };
|
||||
match pb::Processes::decode(data.as_slice()) {
|
||||
Ok(pb) => {
|
||||
let rows: Vec<ProcessInfo> = pb
|
||||
.rows
|
||||
.into_iter()
|
||||
.map(|p: pb::Process| ProcessInfo {
|
||||
pid: p.pid,
|
||||
name: p.name,
|
||||
cpu_usage: p.cpu_usage,
|
||||
mem_bytes: p.mem_bytes,
|
||||
})
|
||||
.collect();
|
||||
Some(ProcessesPayload {
|
||||
process_count: pb.process_count as usize,
|
||||
top_processes: rows,
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
if std::env::var("SOCKTOP_DEBUG").ok().as_deref() == Some("1") {
|
||||
eprintln!("protobuf decode failed: {e}");
|
||||
}
|
||||
// Fallback: maybe it's JSON (bytes already decompressed if gz)
|
||||
match String::from_utf8(data) {
|
||||
Ok(s) => serde_json::from_str::<ProcessesPayload>(&s).ok(),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Ok(Message::Text(json))) => serde_json::from_str::<ProcessesPayload>(&json).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress a gzip-compressed binary frame into a String.
|
||||
fn gunzip_to_string(bytes: &[u8]) -> Option<String> {
|
||||
let mut dec = GzDecoder::new(bytes);
|
||||
let mut out = String::new();
|
||||
dec.read_to_string(&mut out).ok()?;
|
||||
Some(out)
|
||||
}
|
||||
|
||||
fn gunzip_to_vec(bytes: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut dec = GzDecoder::new(bytes);
|
||||
let mut out = Vec::new();
|
||||
dec.read_to_end(&mut out).ok()?;
|
||||
Some(out)
|
||||
}
|
||||
|
||||
fn is_gzip(bytes: &[u8]) -> bool {
|
||||
bytes.len() >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b
|
||||
}
|
||||
|
||||
/// Convenience function to create a connector and connect in one step.
|
||||
///
|
||||
/// This function is for non-TLS WebSocket connections (`ws://`). Since there's no
|
||||
/// certificate involved, hostname verification is not applicable.
|
||||
///
|
||||
/// For TLS connections with certificate pinning, use `connect_to_socktop_agent_with_tls()`.
|
||||
pub async fn connect_to_socktop_agent(
|
||||
url: impl Into<String>,
|
||||
) -> Result<SocktopConnector, Box<dyn std::error::Error>> {
|
||||
let config = ConnectorConfig::new(url);
|
||||
let mut connector = SocktopConnector::new(config);
|
||||
connector.connect().await?;
|
||||
Ok(connector)
|
||||
}
|
||||
|
||||
/// Convenience function to create a connector with TLS and connect in one step.
|
||||
///
|
||||
/// This function enables TLS with certificate pinning using the provided CA certificate.
|
||||
/// The `verify_hostname` parameter controls whether the server's hostname is verified
|
||||
/// against the certificate (recommended for production, can be disabled for testing).
|
||||
#[cfg(feature = "tls")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
|
||||
pub async fn connect_to_socktop_agent_with_tls(
|
||||
url: impl Into<String>,
|
||||
ca_path: impl Into<String>,
|
||||
verify_hostname: bool,
|
||||
) -> Result<SocktopConnector, Box<dyn std::error::Error>> {
|
||||
let config = ConnectorConfig::new(url)
|
||||
.with_tls_ca(ca_path)
|
||||
.with_hostname_verification(verify_hostname);
|
||||
let mut connector = SocktopConnector::new(config);
|
||||
connector.connect().await?;
|
||||
Ok(connector)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//! WebSocket connector library for socktop agents.
|
||||
//!
|
||||
//! This library provides a high-level interface for connecting to socktop agents
|
||||
//! over WebSocket connections with support for TLS and certificate pinning.
|
||||
//!
|
||||
//! # Quick Start
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use socktop_connector::{connect_to_socktop_agent, AgentRequest, AgentResponse};
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let mut connector = connect_to_socktop_agent("ws://localhost:3000/ws").await?;
|
||||
//!
|
||||
//! // Get comprehensive system metrics
|
||||
//! if let Ok(AgentResponse::Metrics(metrics)) = connector.request(AgentRequest::Metrics).await {
|
||||
//! println!("Hostname: {}", metrics.hostname);
|
||||
//! println!("CPU Usage: {:.1}%", metrics.cpu_total);
|
||||
//!
|
||||
//! // CPU temperature if available
|
||||
//! if let Some(temp) = metrics.cpu_temp_c {
|
||||
//! println!("CPU Temperature: {:.1}°C", temp);
|
||||
//! }
|
||||
//!
|
||||
//! // Memory usage
|
||||
//! println!("Memory: {:.1} GB / {:.1} GB",
|
||||
//! metrics.mem_used as f64 / 1_000_000_000.0,
|
||||
//! metrics.mem_total as f64 / 1_000_000_000.0);
|
||||
//!
|
||||
//! // Per-core CPU usage
|
||||
//! for (i, usage) in metrics.cpu_per_core.iter().enumerate() {
|
||||
//! println!("Core {}: {:.1}%", i, usage);
|
||||
//! }
|
||||
//!
|
||||
//! // GPU information
|
||||
//! if let Some(gpus) = &metrics.gpus {
|
||||
//! for gpu in gpus {
|
||||
//! if let Some(name) = &gpu.name {
|
||||
//! println!("GPU {}: {:.1}% usage", name, gpu.utilization.unwrap_or(0.0));
|
||||
//! if let Some(temp) = gpu.temp {
|
||||
//! println!(" Temperature: {:.1}°C", temp);
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Get process information
|
||||
//! if let Ok(AgentResponse::Processes(processes)) = connector.request(AgentRequest::Processes).await {
|
||||
//! println!("Running processes: {}", processes.process_count);
|
||||
//! for proc in &processes.top_processes {
|
||||
//! println!(" PID {}: {} ({:.1}% CPU, {:.1} MB RAM)",
|
||||
//! proc.pid, proc.name, proc.cpu_usage, proc.mem_bytes as f64 / 1_000_000.0);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Get disk information
|
||||
//! if let Ok(AgentResponse::Disks(disks)) = connector.request(AgentRequest::Disks).await {
|
||||
//! for disk in disks {
|
||||
//! let used_gb = (disk.total - disk.available) as f64 / 1_000_000_000.0;
|
||||
//! let total_gb = disk.total as f64 / 1_000_000_000.0;
|
||||
//! println!("Disk {}: {:.1} GB / {:.1} GB", disk.name, used_gb, total_gb);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # TLS Support
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use socktop_connector::connect_to_socktop_agent_with_tls;
|
||||
//!
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let connector = connect_to_socktop_agent_with_tls(
|
||||
//! "wss://secure-host:3000/ws",
|
||||
//! "/path/to/ca.pem",
|
||||
//! false // Enable hostname verification
|
||||
//! ).await?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub mod connector;
|
||||
pub mod types;
|
||||
|
||||
pub use connector::{
|
||||
ConnectorConfig, SocktopConnector, WsStream, connect_to_socktop_agent,
|
||||
connect_to_socktop_agent_with_tls,
|
||||
};
|
||||
pub use types::{
|
||||
AgentRequest, AgentResponse, DiskInfo, GpuInfo, Metrics, NetworkInfo, ProcessInfo,
|
||||
ProcessesPayload,
|
||||
};
|
||||
|
||||
/// Re-export commonly used error type
|
||||
pub use anyhow::Error;
|
||||
@@ -0,0 +1,105 @@
|
||||
//! Types that represent data from the socktop agent.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ProcessInfo {
|
||||
pub pid: u32,
|
||||
pub name: String,
|
||||
pub cpu_usage: f32,
|
||||
pub mem_bytes: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct DiskInfo {
|
||||
pub name: String,
|
||||
pub total: u64,
|
||||
pub available: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct NetworkInfo {
|
||||
pub name: String,
|
||||
pub received: u64,
|
||||
pub transmitted: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct GpuInfo {
|
||||
pub name: Option<String>,
|
||||
pub vendor: Option<String>,
|
||||
|
||||
// Accept both the new and legacy keys
|
||||
#[serde(
|
||||
default,
|
||||
alias = "utilization_gpu_pct",
|
||||
alias = "gpu_util_pct",
|
||||
alias = "gpu_utilization"
|
||||
)]
|
||||
pub utilization: Option<f32>,
|
||||
|
||||
#[serde(default, alias = "mem_used_bytes", alias = "vram_used_bytes")]
|
||||
pub mem_used: Option<u64>,
|
||||
|
||||
#[serde(default, alias = "mem_total_bytes", alias = "vram_total_bytes")]
|
||||
pub mem_total: Option<u64>,
|
||||
|
||||
#[serde(default, alias = "temp_c", alias = "temperature_c")]
|
||||
pub temp: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Metrics {
|
||||
pub cpu_total: f32,
|
||||
pub cpu_per_core: Vec<f32>,
|
||||
pub mem_total: u64,
|
||||
pub mem_used: u64,
|
||||
pub swap_total: u64,
|
||||
pub swap_used: u64,
|
||||
pub hostname: String,
|
||||
pub cpu_temp_c: Option<f32>,
|
||||
pub disks: Vec<DiskInfo>,
|
||||
pub networks: Vec<NetworkInfo>,
|
||||
pub top_processes: Vec<ProcessInfo>,
|
||||
pub gpus: Option<Vec<GpuInfo>>,
|
||||
// New: keep the last reported total process count
|
||||
#[serde(default)]
|
||||
pub process_count: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ProcessesPayload {
|
||||
pub process_count: usize,
|
||||
pub top_processes: Vec<ProcessInfo>,
|
||||
}
|
||||
|
||||
/// Request types that can be sent to the agent
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum AgentRequest {
|
||||
#[serde(rename = "metrics")]
|
||||
Metrics,
|
||||
#[serde(rename = "disks")]
|
||||
Disks,
|
||||
#[serde(rename = "processes")]
|
||||
Processes,
|
||||
}
|
||||
|
||||
impl AgentRequest {
|
||||
/// Convert to the legacy string format used by the agent
|
||||
pub fn to_legacy_string(&self) -> String {
|
||||
match self {
|
||||
AgentRequest::Metrics => "get_metrics".to_string(),
|
||||
AgentRequest::Disks => "get_disks".to_string(),
|
||||
AgentRequest::Processes => "get_processes".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Response types that can be received from the agent
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AgentResponse {
|
||||
Metrics(Metrics),
|
||||
Disks(Vec<DiskInfo>),
|
||||
Processes(ProcessesPayload),
|
||||
}
|
||||
Reference in New Issue
Block a user