burp: Add client/backup info retrieval methods
The `get_clients`, `get_client`, `get_backup`, `get_backup_log`, and `get_backup_stats` methods fetch information from the BURP server using its JSON stats interface. These will be used to populate the metrics we want to export to Prometheus.master
parent
cfdb33d4a3
commit
96849e6367
|
@ -28,6 +28,8 @@ name = "burp_exporter"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"openssl",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tracing",
|
||||
|
@ -101,6 +103,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -297,6 +305,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.19"
|
||||
|
@ -330,6 +344,37 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
|
|
|
@ -7,9 +7,14 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
openssl = "^0.10.38"
|
||||
serde_json = "1.0.78"
|
||||
tokio-native-tls = "^0.3.0"
|
||||
tracing = "^0.1.30"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "^1.0.136"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "^1.16.1"
|
||||
features = ["io-util", "macros", "net", "rt", "signal"]
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::io::prelude::*;
|
|||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
use serde_json;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_native_tls::native_tls::TlsConnector as NativeTlsConnector;
|
||||
|
@ -14,6 +15,7 @@ use tokio_native_tls::TlsStream;
|
|||
use tracing::{debug, info, trace};
|
||||
|
||||
use super::error::Error;
|
||||
use super::model::{BackupInfo, BackupStats, ClientInfo, ClientList};
|
||||
|
||||
/// The BURP client version emulated by this implementation
|
||||
const CLIENT_VERSION: &str = "2.1.32";
|
||||
|
@ -298,4 +300,148 @@ impl Client {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve information about all known clients
|
||||
///
|
||||
/// This method returns a list of clients known to the BURP server.
|
||||
/// Usually, a client can only retrieve information about itself;
|
||||
/// additional configuration is required in order for a client to
|
||||
/// view other clients' information. Specifically, the client must
|
||||
/// be named as a `restore_client` in the BURP server configuration
|
||||
/// AND it must have *at least* `client_can_list` set to `1` for at
|
||||
/// least one other client (or globally).
|
||||
pub async fn get_clients(&mut self) -> Result<ClientList, Error> {
|
||||
let res = self.command('c', "c:").await?;
|
||||
Ok(serde_json::from_str(&res)?)
|
||||
}
|
||||
|
||||
/// Retrieve information about a specific client
|
||||
///
|
||||
/// This method returns information about a specific client, if that
|
||||
/// client is known to the BURP server. The same caveat applies
|
||||
/// about client permissions as for [`get_clients`].
|
||||
pub async fn get_client<S: AsRef<str>>(
|
||||
&mut self,
|
||||
name: S,
|
||||
) -> Result<ClientInfo, Error> {
|
||||
let res = self.command('c', &format!("c:{}", name.as_ref())).await?;
|
||||
let mut clientlist: ClientList = serde_json::from_str(&res)?;
|
||||
match clientlist.clients.pop() {
|
||||
Some(c) => Ok(c),
|
||||
None => Err(Error::Protocol(
|
||||
"Invalid response from server: no client listed".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve information about a client backup
|
||||
///
|
||||
/// This method returns details about a specific backup for a
|
||||
/// particular client.
|
||||
///
|
||||
/// * `name`: The client name
|
||||
/// * `number`: The backup number
|
||||
pub async fn get_backup<S: AsRef<str>>(
|
||||
&mut self,
|
||||
name: S,
|
||||
number: u64,
|
||||
) -> Result<BackupInfo, Error> {
|
||||
let res = self
|
||||
.command('c', &format!("c:{}:b:{}", name.as_ref(), number))
|
||||
.await?;
|
||||
let mut clientlist: ClientList = serde_json::from_str(&res)?;
|
||||
let mut client = match clientlist.clients.pop() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return Err(Error::Protocol(
|
||||
"Invalid response from server: no client listed".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
match client.backups.pop() {
|
||||
Some(b) => Ok(b),
|
||||
None => Err(Error::Protocol(
|
||||
"Invalid response from server: no backup listed".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the log for a client backup
|
||||
///
|
||||
/// This method returns the log for a specific backup for a
|
||||
/// particular client, as a string.
|
||||
///
|
||||
/// * `name`: The client name
|
||||
/// * `number`: The backup number
|
||||
pub async fn get_backup_log<S: AsRef<str>>(
|
||||
&mut self,
|
||||
name: S,
|
||||
number: u64,
|
||||
) -> Result<Option<Vec<String>>, Error> {
|
||||
let res = self
|
||||
.command(
|
||||
'c',
|
||||
&format!("c:{}:b:{}:l:backup", name.as_ref(), number),
|
||||
)
|
||||
.await?;
|
||||
let mut clientlist: ClientList = serde_json::from_str(&res)?;
|
||||
let mut client = match clientlist.clients.pop() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return Err(Error::Protocol(
|
||||
"Invalid response from server: no client listed".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let backup = match client.backups.pop() {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
return Err(Error::Protocol(
|
||||
"Invalid response from server: no backup listed".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(backup.logs.backup)
|
||||
}
|
||||
|
||||
/// Retrieve the statistics for a client backup
|
||||
///
|
||||
/// This method returns the statistics of a specific backup for a
|
||||
/// particular client.
|
||||
///
|
||||
/// * `name`: The client name
|
||||
/// * `number`: The backup number
|
||||
pub async fn get_backup_stats<S: AsRef<str>>(
|
||||
&mut self,
|
||||
name: S,
|
||||
number: u64,
|
||||
) -> Result<Option<BackupStats>, Error> {
|
||||
let res = self
|
||||
.command(
|
||||
'c',
|
||||
&format!("c:{}:b:{}:l:backup_stats", name.as_ref(), number),
|
||||
)
|
||||
.await?;
|
||||
let mut clientlist: ClientList = serde_json::from_str(&res)?;
|
||||
let mut client = match clientlist.clients.pop() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return Err(Error::Protocol(
|
||||
"Invalid response from server: no client listed".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let backup = match client.backups.pop() {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
return Err(Error::Protocol(
|
||||
"Invalid response from server: no backup listed".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
match backup.logs.backup_stats {
|
||||
Some(lines) => Ok(Some(serde_json::from_str(&lines.join("\n"))?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Client error handling
|
||||
use std::io;
|
||||
|
||||
use serde_json;
|
||||
use tokio_native_tls::native_tls;
|
||||
|
||||
/// BURP client errors
|
||||
|
@ -32,3 +34,9 @@ impl From<native_tls::Error> for Error {
|
|||
Self::Tls(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(error: serde_json::Error) -> Self {
|
||||
Self::Protocol(error.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
#![warn(missing_docs)]
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod model;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Logs {
|
||||
pub list: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub backup: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub backup_stats: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct BackupInfo {
|
||||
pub number: u64,
|
||||
pub timestamp: u64,
|
||||
pub flags: Vec<String>,
|
||||
pub logs: Logs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ClientInfo {
|
||||
pub name: String,
|
||||
pub run_status: String,
|
||||
pub protocol: u64,
|
||||
pub backups: Vec<BackupInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ClientList {
|
||||
pub clients: Vec<ClientInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Counter {
|
||||
pub name: String,
|
||||
pub r#type: String,
|
||||
pub count: u64,
|
||||
#[serde(default)]
|
||||
pub changed: u64,
|
||||
#[serde(default)]
|
||||
pub same: u64,
|
||||
#[serde(default)]
|
||||
pub deleted: u64,
|
||||
#[serde(default)]
|
||||
pub scanned: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct BackupStats {
|
||||
pub counters: Vec<Counter>,
|
||||
}
|
Loading…
Reference in New Issue