use std::env;
use std::time::Duration;
use clientapi_pve::apis::configuration::Configuration;
use clientapi_pve::apis::nodes_api;
use clientapi_pve::websocket::{
AuthAttacher, ConsoleConnector, TerminalSession, TerminalTarget, WebSocketTransport, WsError,
WsStream,
};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let host = env::var("PVE_HOST").unwrap_or_else(|_| "https://localhost:8006".into());
let insecure_http = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
let mut cfg = Configuration::new();
cfg.base_path = format!("{}/api2/json", host);
cfg.bearer_access_token = env::var("PVE_TOKEN").ok();
cfg.client = insecure_http;
let resp = nodes_api::nodes_get_nodes(&cfg).await?;
let nodes = resp.data;
println!("Connected (insecure TLS): {} node(s)", nodes.len());
struct InsecureTransport;
#[async_trait::async_trait]
impl WebSocketTransport for InsecureTransport {
async fn open(
&self,
url: &str,
auth: &dyn AuthAttacher,
cfg: &Configuration,
) -> Result<WsStream, WsError> {
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
let mut req = url.into_client_request()?;
auth.apply(&mut req, cfg)?;
let connector = native_tls::TlsConnector::builder()
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.map_err(|e| WsError::BadBasePath(e.to_string()))?;
let tls = tokio_tungstenite::Connector::NativeTls(connector);
let (stream, _) = tokio_tungstenite::connect_async_tls_with_config(
req,
None,
false,
Some(tls),
)
.await?;
Ok(stream)
}
}
let connector = ConsoleConnector::new(
Box::new(DefaultAuthForExample),
std::sync::Arc::new(InsecureTransport),
);
let target = TerminalTarget::Qemu {
node: env::var("PVE_NODE").unwrap_or_else(|_| "pve1".into()),
vmid: env::var("PVE_VMID").unwrap_or_else(|_| "100".into()).parse()?,
};
let mut session: TerminalSession = connector.open_terminal(&cfg, target).await?;
session.send("uname -a\n").await?;
let _ = tokio::time::timeout(Duration::from_secs(3), async {
while let Ok(Some(msg)) = session.recv().await {
print!("{msg}");
}
})
.await;
session.close().await?;
Ok(())
}
struct DefaultAuthForExample;
impl AuthAttacher for DefaultAuthForExample {
fn apply(
&self,
req: &mut tokio_tungstenite::tungstenite::http::Request<()>,
cfg: &Configuration,
) -> Result<(), WsError> {
if let Some(token) = cfg.bearer_access_token.as_ref() {
let header =
tokio_tungstenite::tungstenite::http::HeaderValue::from_str(&format!("PVEAPIToken={}", token))?;
req.headers_mut().insert("authorization", header);
}
Ok(())
}
}