use crate::controllers::{HttpResult, LibVirtReq}; use crate::libvirt_lib_structures::{DomainState, DomainXMLUuid}; use crate::libvirt_rest_structures::VMInfo; use actix_web::{web, HttpResponse}; #[derive(serde::Serialize)] struct VMInfoAndState { #[serde(flatten)] info: VMInfo, state: DomainState, } #[derive(serde::Serialize)] struct VMUuid { uuid: DomainXMLUuid, } /// Create a new VM pub async fn create(client: LibVirtReq, req: web::Json) -> HttpResult { let domain = match req.0.to_domain() { Ok(d) => d, Err(e) => { log::error!("Failed to extract domain info! {e}"); return Ok(HttpResponse::BadRequest().body(e.to_string())); } }; let id = client.update_domain(domain).await?; Ok(HttpResponse::Ok().json(VMUuid { uuid: id })) } /// Get the list of domains pub async fn list_all(client: LibVirtReq) -> HttpResult { let list = client.get_full_list().await?; let mut out = Vec::with_capacity(list.len()); for entry in list { let info = VMInfo::from_domain(entry)?; out.push(VMInfoAndState { state: client .get_domain_state(info.uuid.expect("Domain without UUID !")) .await?, info, }) } Ok(HttpResponse::Ok().json(out)) } #[derive(serde::Deserialize)] pub struct SingleVMUUidReq { uid: DomainXMLUuid, } /// Get the information about a single VM pub async fn get_single(client: LibVirtReq, id: web::Path) -> HttpResult { let info = match client.get_single_domain(id.uid).await { Ok(i) => i, Err(e) => { log::error!("Failed to get domain info! {e}"); return Ok(HttpResponse::InternalServerError().json(e.to_string())); } }; let state = client.get_domain_state(id.uid).await?; Ok(HttpResponse::Ok().json(VMInfoAndState { info: VMInfo::from_domain(info)?, state, })) } /// Update a VM information pub async fn update( client: LibVirtReq, id: web::Path, req: web::Json, ) -> HttpResult { let mut domain = req.0.to_domain().map_err(|e| { log::error!("Failed to extract domain info! {e}"); HttpResponse::BadRequest().body(e.to_string()) })?; domain.uuid = Some(id.uid); client.update_domain(domain).await?; Ok(HttpResponse::Ok().finish()) } #[derive(serde::Deserialize)] pub struct DeleteVMQuery { keep_files: bool, } /// Delete a VM pub async fn delete( client: LibVirtReq, id: web::Path, req: web::Json, ) -> HttpResult { if let Err(e) = client.kill_domain(id.uid).await { log::info!("Failed to kill domain before deleting it: {e}"); } client .delete_domain(id.uid, req.keep_files) .await .map_err(|e| { log::error!("Failed to delete domain! {e}"); HttpResponse::InternalServerError().body(e.to_string()) })?; Ok(HttpResponse::Ok().finish()) } /// Start a VM pub async fn start(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.start_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain started"), Err(e) => { log::error!("Failed to start domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to start domain!") } }) } /// Shutdown a VM pub async fn shutdown(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.shutdown_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain shutdown"), Err(e) => { log::error!("Failed to shutdown domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to shutdown domain!") } }) } /// Kill a VM pub async fn kill(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.kill_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain killed"), Err(e) => { log::error!("Failed to kill domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to kill domain!") } }) } /// Reset a VM pub async fn reset(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.reset_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain reseted"), Err(e) => { log::error!("Failed to reset domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to reset domain!") } }) } /// Suspend a VM pub async fn suspend(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.suspend_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain suspended"), Err(e) => { log::error!("Failed to suspend domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to suspend domain!") } }) } /// Resume a VM pub async fn resume(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.resume_domain(id.uid).await { Ok(_) => HttpResponse::Ok().json("Domain resumed"), Err(e) => { log::error!("Failed to resume domain {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to resume domain!") } }) } #[derive(serde::Serialize)] struct DomainStateRes { state: DomainState, } /// Get the state of a VM pub async fn state(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.get_domain_state(id.uid).await { Ok(s) => HttpResponse::Ok().json(DomainStateRes { state: s }), Err(e) => { log::error!("Failed to get domain state {:?} ! {e}", id.uid); HttpResponse::InternalServerError().json("Failed to get domain state!") } }) } /// Take a screenshot of a VM pub async fn screenshot(client: LibVirtReq, id: web::Path) -> HttpResult { Ok(match client.screenshot_domain(id.uid).await { Ok(b) => HttpResponse::Ok().content_type("image/png").body(b), Err(e) => { log::error!( "Failed to take the screenshot of a domain {:?} ! {e}", id.uid ); HttpResponse::InternalServerError().json("Failed to take the screenshot of the domain!") } }) }