From 42758244a042f42d60e578296df1c97dd797bcbe Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 1 Oct 2019 14:07:15 +0200 Subject: [PATCH] vmm: Implement the /api/v1/vm.info endpoint This, for now, returns the VM config and its state. Signed-off-by: Samuel Ortiz --- vmm/src/api/http.rs | 3 ++- vmm/src/api/http_endpoint.rs | 31 +++++++++++++++++++++++++++++- vmm/src/api/mod.rs | 37 +++++++++++++++++++++++++++++++++++- vmm/src/lib.rs | 35 ++++++++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 5 deletions(-) diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index db919f543..1e99ed16d 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -5,7 +5,7 @@ extern crate threadpool; -use crate::api::http_endpoint::{VmActionHandler, VmCreate}; +use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo}; use crate::api::{ApiRequest, VmAction}; use crate::{Error, Result}; use micro_http::{HttpConnection, Request, Response, StatusCode, Version}; @@ -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.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 f26a044de..4e5de1007 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -5,7 +5,7 @@ use crate::api::http::EndpointHandler; use crate::api::VmConfig; -use crate::api::{vm_boot, vm_create, vm_reboot, vm_shutdown, ApiRequest, VmAction}; +use crate::api::{vm_boot, vm_create, vm_info, vm_reboot, vm_shutdown, ApiRequest, VmAction}; use crate::{Error, Result}; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -25,6 +25,9 @@ pub enum HttpError { /// Could not boot a VM VmBoot(Error), + /// Could not get the VM information + VmInfo(Error), + /// Could not shut a VM down VmShutdown(Error), @@ -124,3 +127,29 @@ impl EndpointHandler for VmActionHandler { } } } + +// /api/v1/vm.info handler +pub struct VmInfo {} + +impl EndpointHandler for VmInfo { + fn handle_request( + &self, + req: &Request, + api_notifier: EventFd, + api_sender: Sender, + ) -> Response { + match req.method() { + Method::Get => match vm_info(api_notifier, api_sender).map_err(HttpError::VmInfo) { + Ok(info) => { + let mut response = Response::new(Version::Http11, StatusCode::OK); + let info_serialized = serde_json::to_string(&info).unwrap(); + + response.set_body(Body::new(info_serialized)); + response + } + Err(e) => error_response(e, StatusCode::InternalServerError), + }, + _ => Response::new(Version::Http11, StatusCode::BadRequest), + } + } +} diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index a50abd3c7..3d9d1962d 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -37,7 +37,7 @@ pub mod http; pub mod http_endpoint; use crate::config::VmConfig; -use crate::vm::Error as VmError; +use crate::vm::{Error as VmError, VmState}; use crate::{Error, Result}; use std::sync::mpsc::{channel, Sender}; use std::sync::Arc; @@ -55,6 +55,9 @@ pub enum ApiError { /// The VM is already created. VmAlreadyCreated, + /// The VM info is not available. + VmInfo, + /// The VM config is missing. VmMissingConfig, @@ -71,9 +74,18 @@ pub enum ApiError { VmReboot, } +#[derive(Clone, Deserialize, Serialize)] +pub struct VmInfo { + pub config: Arc, + pub state: VmState, +} + pub enum ApiResponsePayload { /// No data is sent on the channel. Empty, + + /// Virtual machine information + VmInfo(VmInfo), } /// This is the response sent by the VMM API server through the mpsc channel. @@ -92,6 +104,9 @@ pub enum ApiRequest { /// VmBoot error back. VmBoot(Sender), + /// Request the VM information. + VmInfo(Sender), + /// Shut the previously booted virtual machine down. /// If the VM was not previously booted or created, the VMM API server /// will send a VmShutdown error back. @@ -188,3 +203,23 @@ pub fn vm_shutdown(api_evt: EventFd, api_sender: Sender) -> Result<( pub fn vm_reboot(api_evt: EventFd, api_sender: Sender) -> Result<()> { vm_action(api_evt, api_sender, VmAction::Reboot) } + +pub fn vm_info(api_evt: EventFd, api_sender: Sender) -> Result { + let (response_sender, response_receiver) = channel(); + + // Send the VM request. + api_sender + .send(ApiRequest::VmInfo(response_sender)) + .map_err(Error::ApiRequestSend)?; + api_evt.write(1).map_err(Error::EventFdWrite)?; + + let vm_info = response_receiver + .recv() + .map_err(Error::ApiResponseRecv)? + .map_err(|_| Error::ApiVmInfo)?; + + match vm_info { + ApiResponsePayload::VmInfo(info) => Ok(info), + _ => Err(Error::ApiVmInfo), + } +} diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 9535f61c8..1c4bea8f5 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -14,9 +14,9 @@ extern crate serde_derive; extern crate serde_json; extern crate vmm_sys_util; -use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload}; +use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo}; use crate::config::VmConfig; -use crate::vm::{Error as VmError, ExitBehaviour, Vm}; +use crate::vm::{Error as VmError, ExitBehaviour, Vm, VmState}; use libc::EFD_NONBLOCK; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; @@ -52,6 +52,9 @@ pub enum Error { /// Cannot boot a VM from the API ApiVmBoot(ApiError), + /// Cannot get the VM info + ApiVmInfo, + /// Cannot shut a VM down from the API ApiVmShutdown(ApiError), @@ -88,6 +91,9 @@ pub enum Error { /// Cannot boot a VM VmBoot(VmError), + /// Cannot fetch the VM information + VmInfo, + /// The Vm is not created VmNotCreated, @@ -289,6 +295,23 @@ impl Vmm { Ok(()) } + fn vm_info(&self) -> Result { + match &self.vm_config { + Some(config) => { + let state = match &self.vm { + Some(vm) => vm.get_state().unwrap(), + None => VmState::Created, + }; + + Ok(VmInfo { + config: Arc::clone(config), + state, + }) + } + None => Err(Error::VmNotCreated), + } + } + fn control_loop(&mut self, api_receiver: Arc>) -> Result { const EPOLL_EVENTS_LEN: usize = 100; @@ -429,6 +452,14 @@ impl Vmm { Err(_) => Err(ApiError::VmReboot), }; + sender.send(response).map_err(Error::ApiResponseSend)?; + } + ApiRequest::VmInfo(sender) => { + let response = match self.vm_info() { + Ok(info) => Ok(ApiResponsePayload::VmInfo(info)), + Err(_) => Err(ApiError::VmInfo), + }; + sender.send(response).map_err(Error::ApiResponseSend)?; } }