diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index 1e99ed16d..cc8dc220d 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -56,6 +56,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.create"), Box::new(VmCreate {})); r.routes.insert(endpoint!("/vm.boot"), Box::new(VmActionHandler::new(VmAction::Boot))); + r.routes.insert(endpoint!("/vm.delete"), Box::new(VmActionHandler::new(VmAction::Delete))); r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {})); r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown))); r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot))); diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index e5cef19d6..22b0dccfb 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -5,8 +5,8 @@ use crate::api::http::EndpointHandler; use crate::api::{ - vm_boot, vm_create, vm_info, vm_reboot, vm_shutdown, ApiError, ApiRequest, ApiResult, VmAction, - VmConfig, + vm_boot, vm_create, vm_delete, vm_info, vm_reboot, vm_shutdown, ApiError, ApiRequest, + ApiResult, VmAction, VmConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -97,6 +97,7 @@ impl VmActionHandler { pub fn new(action: VmAction) -> Self { let action_fn = Box::new(match action { VmAction::Boot => vm_boot, + VmAction::Delete => vm_delete, VmAction::Shutdown => vm_shutdown, VmAction::Reboot => vm_reboot, }); diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index e64feefc5..6f67a8794 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -67,6 +67,9 @@ pub enum ApiError { /// The VM could not be created. VmCreate(VmError), + /// The VM could not be deleted. + VmDelete(VmError), + /// The VM info is not available. VmInfo(VmError), @@ -117,6 +120,12 @@ pub enum ApiRequest { /// VmBoot error back. VmBoot(Sender), + /// Delete the previously created virtual machine. + /// If the VM was not previously created, the VMM API server will send a + /// VmDelete error back. + /// If the VM is booted, we shut it down first. + VmDelete(Sender), + /// Request the VM information. VmInfo(Sender), @@ -156,6 +165,9 @@ pub enum VmAction { /// Boot a VM Boot, + /// Delete a VM + Delete, + /// Shut a VM down Shutdown, @@ -168,6 +180,7 @@ fn vm_action(api_evt: EventFd, api_sender: Sender, action: VmAction) let request = match action { VmAction::Boot => ApiRequest::VmBoot(response_sender), + VmAction::Delete => ApiRequest::VmDelete(response_sender), VmAction::Shutdown => ApiRequest::VmShutdown(response_sender), VmAction::Reboot => ApiRequest::VmReboot(response_sender), }; @@ -176,19 +189,7 @@ fn vm_action(api_evt: EventFd, api_sender: Sender, action: VmAction) api_sender.send(request).map_err(ApiError::RequestSend)?; api_evt.write(1).map_err(ApiError::EventFdWrite)?; - match action { - VmAction::Boot => { - response_receiver.recv().map_err(ApiError::ResponseRecv)??; - } - - VmAction::Shutdown => { - response_receiver.recv().map_err(ApiError::ResponseRecv)??; - } - - VmAction::Reboot => { - response_receiver.recv().map_err(ApiError::ResponseRecv)??; - } - } + response_receiver.recv().map_err(ApiError::ResponseRecv)??; Ok(()) } @@ -197,6 +198,10 @@ pub fn vm_boot(api_evt: EventFd, api_sender: Sender) -> ApiResult<() vm_action(api_evt, api_sender, VmAction::Boot) } +pub fn vm_delete(api_evt: EventFd, api_sender: Sender) -> ApiResult<()> { + vm_action(api_evt, api_sender, VmAction::Delete) +} + pub fn vm_shutdown(api_evt: EventFd, api_sender: Sender) -> ApiResult<()> { vm_action(api_evt, api_sender, VmAction::Shutdown) } diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 2c49ca53f..370cf98fc 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -307,6 +307,22 @@ impl Vmm { } } + fn vm_delete(&mut self) -> result::Result<(), VmError> { + if self.vm_config.is_none() { + return Ok(()); + } + + self.vm_config = None; + + // First we shut the current VM down if necessary. + if let Some(ref mut vm) = self.vm { + vm.shutdown()?; + self.vm = None; + } + + Ok(()) + } + fn control_loop(&mut self, api_receiver: Arc>) -> Result { const EPOLL_EVENTS_LEN: usize = 100; @@ -377,6 +393,14 @@ impl Vmm { sender.send(response).map_err(Error::ApiResponseSend)?; } + ApiRequest::VmDelete(sender) => { + let response = match self.vm_delete() { + Ok(_) => Ok(ApiResponsePayload::Empty), + Err(e) => Err(ApiError::VmDelete(e)), + }; + + sender.send(response).map_err(Error::ApiResponseSend)?; + } ApiRequest::VmBoot(sender) => { // If we don't have a config, we can not boot a VM. if self.vm_config.is_none() {