From 86339b4cb4b69c861716747d637144cb9ff1ac3a Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 26 Nov 2019 16:46:10 +0000 Subject: [PATCH] vmm: Add HTTP API to resize the VM Currently only increasing the number of vCPUs is supported but in the future it will be extended. Signed-off-by: Rob Bradford --- vmm/src/api/http.rs | 5 ++- vmm/src/api/http_endpoint.rs | 43 +++++++++++++++++++++-- vmm/src/api/mod.rs | 29 +++++++++++++++ vmm/src/api/openapi/cloud-hypervisor.yaml | 20 +++++++++++ vmm/src/lib.rs | 15 ++++++++ vmm/src/vm.rs | 8 +++++ 6 files changed, 117 insertions(+), 3 deletions(-) diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index 015c4e6f4..37de6dcff 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -3,7 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 // -use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown}; +use crate::api::http_endpoint::{ + VmActionHandler, VmCreate, VmInfo, VmResize, VmmPing, VmmShutdown, +}; use crate::api::{ApiRequest, VmAction}; use crate::{Error, Result}; use micro_http::{HttpServer, MediaType, Request, Response, StatusCode, Version}; @@ -59,6 +61,7 @@ lazy_static! { 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.routes.insert(endpoint!("/vm.resize"), Box::new(VmResize {})); r }; diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index eaa165add..d0a0effbe 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_delete, vm_info, vm_pause, vm_reboot, vm_resume, vm_shutdown, vmm_ping, - vmm_shutdown, ApiError, ApiRequest, ApiResult, VmAction, VmConfig, + vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_resize, vm_resume, vm_shutdown, + vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult, VmAction, VmConfig, VmResizeData, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -219,3 +219,42 @@ impl EndpointHandler for VmmShutdown { } } } + +// /api/v1/vm.resize handler +pub struct VmResize {} + +impl EndpointHandler for VmResize { + fn handle_request( + &self, + req: &Request, + api_notifier: EventFd, + api_sender: Sender, + ) -> Response { + match req.method() { + Method::Put => { + match &req.body { + Some(body) => { + // Deserialize into a VmConfig + let vm_resize_data: VmResizeData = match serde_json::from_slice(body.raw()) + .map_err(HttpError::SerdeJsonDeserialize) + { + Ok(config) => config, + Err(e) => return error_response(e, StatusCode::BadRequest), + }; + + // Call vm_resize() + match vm_resize(api_notifier, api_sender, Arc::new(vm_resize_data)) + .map_err(HttpError::VmCreate) + { + Ok(_) => Response::new(Version::Http11, StatusCode::NoContent), + Err(e) => error_response(e, StatusCode::InternalServerError), + } + } + + None => Response::new(Version::Http11, StatusCode::BadRequest), + } + } + _ => Response::new(Version::Http11, StatusCode::BadRequest), + } + } +} diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index fd6d8df63..aa8f4c200 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -96,6 +96,9 @@ pub enum ApiError { /// The VMM could not shutdown. VmmShutdown(VmError), + + /// The VM could not be resized + VmResize(VmError), } pub type ApiResult = std::result::Result; @@ -110,6 +113,11 @@ pub struct VmmPingResponse { pub version: String, } +#[derive(Clone, Deserialize, Serialize)] +pub struct VmResizeData { + pub desired_vcpus: u8, +} + pub enum ApiResponsePayload { /// No data is sent on the channel. Empty, @@ -169,6 +177,9 @@ pub enum ApiRequest { /// This will shutdown and delete the current VM, if any, and then exit the /// VMM process. VmmShutdown(Sender), + + //// Resuze the VMM + VmResize(Arc, Sender), } pub fn vm_create( @@ -303,3 +314,21 @@ pub fn vmm_shutdown(api_evt: EventFd, api_sender: Sender) -> ApiResu Ok(()) } + +pub fn vm_resize( + api_evt: EventFd, + api_sender: Sender, + data: Arc, +) -> ApiResult<()> { + let (response_sender, response_receiver) = channel(); + + // Send the VM creation request. + api_sender + .send(ApiRequest::VmResize(data, response_sender)) + .map_err(ApiError::RequestSend)?; + api_evt.write(1).map_err(ApiError::EventFdWrite)?; + + response_receiver.recv().map_err(ApiError::ResponseRecv)??; + + Ok(()) +} diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index fed1433e5..2257cabd7 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -123,6 +123,16 @@ paths: 405: description: The VM instance could not reboot because it is not booted. + /vm.resize: + put: + summary: Resize the VM + requestBody: + description: The target size for the VM + content: + application/json: + schema: + $ref: '#/components/schemas/VmResize' + required: true components: schemas: @@ -411,3 +421,13 @@ components: iommu: type: boolean default: false + + VmResize: + required: + - desired_vcpus + type: object + properties: + cpu_count: + minimum: 1 + default: 1 + type: integer diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 7c015aa8d..08a08e7ab 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -328,6 +328,14 @@ impl Vmm { self.vm_delete() } + fn vm_resize(&mut self, desired_vcpus: u8) -> result::Result<(), VmError> { + if let Some(ref mut vm) = self.vm { + vm.resize(desired_vcpus) + } else { + Err(VmError::VmNotRunning) + } + } + fn control_loop(&mut self, api_receiver: Arc>) -> Result<()> { const EPOLL_EVENTS_LEN: usize = 100; @@ -473,6 +481,13 @@ impl Vmm { break 'outer; } + ApiRequest::VmResize(resize_data, sender) => { + let response = self + .vm_resize(resize_data.desired_vcpus) + .map_err(ApiError::VmResize) + .map(|_| ApiResponsePayload::Empty); + sender.send(response).map_err(Error::ApiResponseSend)?; + } } } } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 6ee88c11b..3bf2ba5c8 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -649,6 +649,14 @@ impl Vm { Ok(()) } + pub fn resize(&mut self, desired_vcpus: u8) -> Result<()> { + self.cpu_manager + .lock() + .unwrap() + .resize(desired_vcpus) + .map_err(Error::CpuManager) + } + fn os_signal_handler(signals: Signals, console_input_clone: Arc) { for signal in signals.forever() { if signal == SIGWINCH {