use std::fmt;
use reqwest::header::COOKIE;
use super::{
ClientBuilder, Core, IPMI_PATH, LOGIN_PATH, MetricKind, Metrics, PMBUS_QUERY, SENSOR_QUERY,
configure_tls, cookie_header, wanted,
};
use crate::error::{Error, Result};
use crate::models::{PowerSupply, Sensor};
use crate::{parse_power_supplies, parse_sensors};
pub struct Client {
core: Core,
http: reqwest::Client,
}
impl ClientBuilder {
pub fn build(self) -> Result<Client> {
let http = reqwest::Client::builder().timeout(self.request_timeout);
let http = configure_tls!(http, self.accept_invalid_certs);
let http = http.build()?;
Ok(Client {
core: self.into_core()?,
http,
})
}
}
impl Client {
pub fn new(server: &str, username: &str, password: &str) -> Result<Self> {
Self::builder(server, username, password).build()
}
pub fn builder(server: &str, username: &str, password: &str) -> ClientBuilder {
ClientBuilder::new(server, username, password)
}
pub async fn login(&self) -> Result<()> {
self.new_session().await.map(drop)
}
pub async fn pmbus_metrics(&self) -> Result<Vec<PowerSupply>> {
self.fetch(PMBUS_QUERY, parse_power_supplies).await
}
pub async fn sensor_metrics(&self) -> Result<Vec<Sensor>> {
self.fetch(SENSOR_QUERY, parse_sensors).await
}
pub async fn metrics(&self, kinds: &[MetricKind]) -> Result<Metrics> {
let mut metrics = Metrics::default();
for kind in wanted(kinds) {
match kind {
MetricKind::Pmbus => metrics.pmbus = Some(self.pmbus_metrics().await?),
MetricKind::Sensor => metrics.sensor = Some(self.sensor_metrics().await?),
}
}
Ok(metrics)
}
async fn new_session(&self) -> Result<String> {
let response = self
.http
.post(self.core.url(LOGIN_PATH))
.form(&self.core.login_form())
.send()
.await?
.error_for_status()?;
self.core.store_session(response.headers())
}
async fn fetch<T>(&self, field: (&str, &str), parse: fn(&str) -> Result<T>) -> Result<T> {
let (sid, fresh) = match self.core.valid_sid() {
Some(sid) => (sid, false),
None => (self.new_session().await?, true),
};
match parse(&self.post_query(&sid, field).await?) {
Err(Error::Xml(_) | Error::UnexpectedResponse(_)) if !fresh => {
let sid = self.new_session().await?;
parse(&self.post_query(&sid, field).await?)
}
result => result,
}
}
async fn post_query(&self, sid: &str, field: (&str, &str)) -> Result<String> {
Ok(self
.http
.post(self.core.url(IPMI_PATH))
.header(COOKIE, cookie_header(sid))
.form(&[field])
.send()
.await?
.error_for_status()?
.text()
.await?)
}
}
impl fmt::Debug for Client {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.core.fmt_debug("Client", f)
}
}