diff --git a/src/main.rs b/src/main.rs index b50e65b74..7ff959d55 100755 --- a/src/main.rs +++ b/src/main.rs @@ -2722,6 +2722,9 @@ mod tests { thread::sleep(std::time::Duration::new(1, 0)); + // Verify API server is running + curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None); + // Create the VM first let cpu_count: u8 = 4; let http_body = guest.api_create_body(cpu_count); @@ -2778,6 +2781,9 @@ mod tests { thread::sleep(std::time::Duration::new(1, 0)); + // Verify API server is running + curl_command(&api_socket, "GET", "http://localhost/api/v1/vmm.ping", None); + // Create the VM first let cpu_count: u8 = 4; let http_body = guest.api_create_body(cpu_count); diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index 21fa29602..015c4e6f4 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmShutdown}; +use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown}; use crate::api::{ApiRequest, VmAction}; use crate::{Error, Result}; use micro_http::{HttpServer, MediaType, Request, Response, StatusCode, Version}; @@ -58,6 +58,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown))); r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot))); r.routes.insert(endpoint!("/vmm.shutdown"), Box::new(VmmShutdown {})); + r.routes.insert(endpoint!("/vmm.ping"), Box::new(VmmPing {})); r }; diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index d758c9171..eaa165add 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::{ - vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_resume, vm_shutdown, + vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_resume, vm_shutdown, vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult, VmAction, VmConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; @@ -46,6 +46,9 @@ pub enum HttpError { /// Could not shut the VMM down VmmShutdown(ApiError), + + /// Could not handle VMM ping + VmmPing(ApiError), } fn error_response(error: HttpError, status: StatusCode) -> Response { @@ -169,6 +172,32 @@ impl EndpointHandler for VmInfo { } } +// /api/v1/vmm.info handler +pub struct VmmPing {} + +impl EndpointHandler for VmmPing { + fn handle_request( + &self, + req: &Request, + api_notifier: EventFd, + api_sender: Sender, + ) -> Response { + match req.method() { + Method::Get => match vmm_ping(api_notifier, api_sender).map_err(HttpError::VmmPing) { + Ok(pong) => { + let mut response = Response::new(Version::Http11, StatusCode::OK); + let info_serialized = serde_json::to_string(&pong).unwrap(); + + response.set_body(Body::new(info_serialized)); + response + } + Err(e) => error_response(e, StatusCode::InternalServerError), + }, + _ => Response::new(Version::Http11, StatusCode::BadRequest), + } + } +} + // /api/v1/vmm.shutdown handler pub struct VmmShutdown {} diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 46769051f..fd6d8df63 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -105,12 +105,20 @@ pub struct VmInfo { pub state: VmState, } +#[derive(Clone, Deserialize, Serialize)] +pub struct VmmPingResponse { + pub version: String, +} + pub enum ApiResponsePayload { /// No data is sent on the channel. Empty, /// Virtual machine information VmInfo(VmInfo), + + /// Vmm ping response + VmmPing(VmmPingResponse), } /// This is the response sent by the VMM API server through the mpsc channel. @@ -138,6 +146,9 @@ pub enum ApiRequest { /// Request the VM information. VmInfo(Sender), + /// Request the VMM API server status + VmmPing(Sender), + /// Pause a VM. VmPause(Sender), @@ -263,6 +274,22 @@ pub fn vm_info(api_evt: EventFd, api_sender: Sender) -> ApiResult) -> ApiResult { + let (response_sender, response_receiver) = channel(); + + api_sender + .send(ApiRequest::VmmPing(response_sender)) + .map_err(ApiError::RequestSend)?; + api_evt.write(1).map_err(ApiError::EventFdWrite)?; + + let vmm_pong = response_receiver.recv().map_err(ApiError::ResponseRecv)??; + + match vmm_pong { + ApiResponsePayload::VmmPing(pong) => Ok(pong), + _ => Err(ApiError::ResponsePayloadType), + } +} + pub fn vmm_shutdown(api_evt: EventFd, api_sender: Sender) -> ApiResult<()> { let (response_sender, response_receiver) = channel(); diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index efbad4155..421aac2a2 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -12,16 +12,16 @@ servers: paths: - /vmm.info: + /vmm.ping: get: - summary: Returns general information about the cloud-hypervisor Virtual Machine Monitor (VMM). + summary: Ping the VMM to check for API server availability responses: 200: description: The VMM information content: application/json: schema: - $ref: '#/components/schemas/VmmInfo' + $ref: '#/components/schemas/VmmPingResponse' /vmm.shutdown: put: @@ -126,7 +126,7 @@ paths: components: schemas: - VmmInfo: + VmmPingResponse: required: - version type: object diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index db176f4f9..779a3f861 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -14,7 +14,7 @@ extern crate serde_derive; extern crate serde_json; extern crate vmm_sys_util; -use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo}; +use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo, VmmPingResponse}; use crate::config::VmConfig; use crate::vm::{Error as VmError, Vm, VmState}; use libc::EFD_NONBLOCK; @@ -302,6 +302,12 @@ impl Vmm { } } + fn vmm_ping(&self) -> result::Result { + Ok(VmmPingResponse { + version: env!("CARGO_PKG_VERSION").to_string(), + }) + } + fn vm_delete(&mut self) -> result::Result<(), VmError> { if self.vm_config.is_none() { return Ok(()); @@ -433,6 +439,11 @@ impl Vmm { sender.send(response).map_err(Error::ApiResponseSend)?; } + ApiRequest::VmmPing(sender) => { + let response = self.vmm_ping().map(ApiResponsePayload::VmmPing); + + sender.send(response).map_err(Error::ApiResponseSend)?; + } ApiRequest::VmPause(sender) => { let response = self .vm_pause()