diff --git a/virtweb_backend/src/actors/libvirt_actor.rs b/virtweb_backend/src/actors/libvirt_actor.rs index bf0b1e5..b9a5106 100644 --- a/virtweb_backend/src/actors/libvirt_actor.rs +++ b/virtweb_backend/src/actors/libvirt_actor.rs @@ -150,3 +150,78 @@ impl Handler for LibVirtActor { Ok(()) } } + +#[derive(Message)] +#[rtype(result = "anyhow::Result<()>")] +pub struct ShutdownDomainReq(pub DomainXMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result<()>; + + fn handle(&mut self, msg: ShutdownDomainReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Shutdown domain:\n{}", msg.0.as_string()); + let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + domain.shutdown()?; + Ok(()) + } +} + +#[derive(Message)] +#[rtype(result = "anyhow::Result<()>")] +pub struct KillDomainReq(pub DomainXMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result<()>; + + fn handle(&mut self, msg: KillDomainReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Kill domain:\n{}", msg.0.as_string()); + let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + domain.destroy()?; + Ok(()) + } +} + +#[derive(Message)] +#[rtype(result = "anyhow::Result<()>")] +pub struct ResetDomainReq(pub DomainXMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result<()>; + + fn handle(&mut self, msg: ResetDomainReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Reset domain:\n{}", msg.0.as_string()); + let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + domain.reset()?; + Ok(()) + } +} + +#[derive(Message)] +#[rtype(result = "anyhow::Result<()>")] +pub struct SuspendDomainReq(pub DomainXMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result<()>; + + fn handle(&mut self, msg: SuspendDomainReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Suspend domain:\n{}", msg.0.as_string()); + let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + domain.suspend()?; + Ok(()) + } +} + +#[derive(Message)] +#[rtype(result = "anyhow::Result<()>")] +pub struct ResumeDomainReq(pub DomainXMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result<()>; + + fn handle(&mut self, msg: ResumeDomainReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Resume domain:\n{}", msg.0.as_string()); + let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + domain.resume()?; + Ok(()) + } +} diff --git a/virtweb_backend/src/controllers/vm_controller.rs b/virtweb_backend/src/controllers/vm_controller.rs index e9c53df..8baf46d 100644 --- a/virtweb_backend/src/controllers/vm_controller.rs +++ b/virtweb_backend/src/controllers/vm_controller.rs @@ -75,3 +75,58 @@ pub async fn start(client: LibVirtReq, id: web::Path) -> HttpRe } }) } + +/// 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!") + } + }) +} diff --git a/virtweb_backend/src/libvirt_client.rs b/virtweb_backend/src/libvirt_client.rs index 62849cb..96b8d98 100644 --- a/virtweb_backend/src/libvirt_client.rs +++ b/virtweb_backend/src/libvirt_client.rs @@ -42,4 +42,29 @@ impl LibVirtClient { pub async fn start_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { self.0.send(libvirt_actor::StartDomainReq(id)).await? } + + /// Shutdown a domain + pub async fn shutdown_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { + self.0.send(libvirt_actor::ShutdownDomainReq(id)).await? + } + + /// Kill a domain + pub async fn kill_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { + self.0.send(libvirt_actor::KillDomainReq(id)).await? + } + + /// Reset a domain + pub async fn reset_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { + self.0.send(libvirt_actor::ResetDomainReq(id)).await? + } + + /// Suspend a domain + pub async fn suspend_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { + self.0.send(libvirt_actor::SuspendDomainReq(id)).await? + } + + /// Resume a domain + pub async fn resume_domain(&self, id: DomainXMLUuid) -> anyhow::Result<()> { + self.0.send(libvirt_actor::ResumeDomainReq(id)).await? + } } diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index 2e84fcd..5a63aa9 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -139,6 +139,17 @@ async fn main() -> std::io::Result<()> { .route("/api/vm/list", web::get().to(vm_controller::list_all)) .route("/api/vm/{uid}", web::get().to(vm_controller::get_single)) .route("/api/vm/{uid}/start", web::get().to(vm_controller::start)) + .route( + "/api/vm/{uid}/shutdown", + web::get().to(vm_controller::shutdown), + ) + .route("/api/vm/{uid}/kill", web::get().to(vm_controller::kill)) + .route("/api/vm/{uid}/reset", web::get().to(vm_controller::reset)) + .route( + "/api/vm/{uid}/suspend", + web::get().to(vm_controller::suspend), + ) + .route("/api/vm/{uid}/resume", web::get().to(vm_controller::resume)) }) .bind(&AppConfig::get().listen_address)? .run()