mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Add a new id option to the VFIO hotplug command so that it matches the VFIO coldplug semantic. This is done by refactoring the existing code for VFIO hotplug, where VmAddDeviceData structure is replaced by DeviceConfig. This structure is the one used whenever a VFIO device is coldplugged, which is why it makes sense to reuse it for the hotplug codepath. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
355 lines
12 KiB
Rust
355 lines
12 KiB
Rust
// Copyright © 2019 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
use crate::api::http::EndpointHandler;
|
|
use crate::api::{
|
|
vm_add_device, vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_remove_device,
|
|
vm_resize, vm_resume, vm_shutdown, vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult,
|
|
DeviceConfig, VmAction, VmConfig, VmRemoveDeviceData, VmResizeData,
|
|
};
|
|
use micro_http::{Body, Method, Request, Response, StatusCode, Version};
|
|
use serde_json::Error as SerdeError;
|
|
use std::sync::mpsc::Sender;
|
|
use std::sync::{Arc, Mutex};
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
/// Errors associated with VMM management
|
|
#[derive(Debug)]
|
|
pub enum HttpError {
|
|
/// API request receive error
|
|
SerdeJsonDeserialize(SerdeError),
|
|
|
|
/// Could not create a VM
|
|
VmCreate(ApiError),
|
|
|
|
/// Could not boot a VM
|
|
VmBoot(ApiError),
|
|
|
|
/// Could not get the VM information
|
|
VmInfo(ApiError),
|
|
|
|
/// Could not pause the VM
|
|
VmPause(ApiError),
|
|
|
|
/// Could not pause the VM
|
|
VmResume(ApiError),
|
|
|
|
/// Could not shut a VM down
|
|
VmShutdown(ApiError),
|
|
|
|
/// Could not reboot a VM
|
|
VmReboot(ApiError),
|
|
|
|
/// Could not act on a VM
|
|
VmAction(ApiError),
|
|
|
|
/// Could not resize a VM
|
|
VmResize(ApiError),
|
|
|
|
/// Could not add a device to a VM
|
|
VmAddDevice(ApiError),
|
|
|
|
/// Could not remove a device from a VM
|
|
VmRemoveDevice(ApiError),
|
|
|
|
/// Could not shut the VMM down
|
|
VmmShutdown(ApiError),
|
|
|
|
/// Could not handle VMM ping
|
|
VmmPing(ApiError),
|
|
}
|
|
|
|
fn error_response(error: HttpError, status: StatusCode) -> Response {
|
|
let mut response = Response::new(Version::Http11, status);
|
|
response.set_body(Body::new(format!("{:?}", error)));
|
|
|
|
response
|
|
}
|
|
|
|
// /api/v1/vm.create handler
|
|
pub struct VmCreate {}
|
|
|
|
impl EndpointHandler for VmCreate {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> Response {
|
|
match req.method() {
|
|
Method::Put => {
|
|
match &req.body {
|
|
Some(body) => {
|
|
// Deserialize into a VmConfig
|
|
let vm_config: VmConfig = match serde_json::from_slice(body.raw())
|
|
.map_err(HttpError::SerdeJsonDeserialize)
|
|
{
|
|
Ok(config) => config,
|
|
Err(e) => return error_response(e, StatusCode::BadRequest),
|
|
};
|
|
|
|
// Call vm_create()
|
|
match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
|
|
.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),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Common handler for boot, shutdown and reboot
|
|
pub struct VmActionHandler {
|
|
action_fn: VmActionFn,
|
|
}
|
|
|
|
type VmActionFn = Box<dyn Fn(EventFd, Sender<ApiRequest>) -> ApiResult<()> + Send + Sync>;
|
|
|
|
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,
|
|
VmAction::Pause => vm_pause,
|
|
VmAction::Resume => vm_resume,
|
|
});
|
|
|
|
VmActionHandler { action_fn }
|
|
}
|
|
}
|
|
|
|
impl EndpointHandler for VmActionHandler {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> Response {
|
|
match req.method() {
|
|
Method::Put => {
|
|
match (self.action_fn)(api_notifier, api_sender).map_err(|e| match e {
|
|
ApiError::VmBoot(_) => HttpError::VmBoot(e),
|
|
ApiError::VmShutdown(_) => HttpError::VmShutdown(e),
|
|
ApiError::VmReboot(_) => HttpError::VmReboot(e),
|
|
ApiError::VmPause(_) => HttpError::VmPause(e),
|
|
ApiError::VmResume(_) => HttpError::VmResume(e),
|
|
_ => HttpError::VmAction(e),
|
|
}) {
|
|
Ok(_) => Response::new(Version::Http11, StatusCode::NoContent),
|
|
Err(e) => error_response(e, StatusCode::InternalServerError),
|
|
}
|
|
}
|
|
_ => Response::new(Version::Http11, StatusCode::BadRequest),
|
|
}
|
|
}
|
|
}
|
|
|
|
// /api/v1/vm.info handler
|
|
pub struct VmInfo {}
|
|
|
|
impl EndpointHandler for VmInfo {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> 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),
|
|
}
|
|
}
|
|
}
|
|
|
|
// /api/v1/vmm.info handler
|
|
pub struct VmmPing {}
|
|
|
|
impl EndpointHandler for VmmPing {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> 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 {}
|
|
|
|
impl EndpointHandler for VmmShutdown {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> Response {
|
|
match req.method() {
|
|
Method::Put => {
|
|
match vmm_shutdown(api_notifier, api_sender).map_err(HttpError::VmmShutdown) {
|
|
Ok(_) => Response::new(Version::Http11, StatusCode::OK),
|
|
Err(e) => error_response(e, StatusCode::InternalServerError),
|
|
}
|
|
}
|
|
_ => Response::new(Version::Http11, StatusCode::BadRequest),
|
|
}
|
|
}
|
|
}
|
|
|
|
// /api/v1/vm.resize handler
|
|
pub struct VmResize {}
|
|
|
|
impl EndpointHandler for VmResize {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> 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::VmResize)
|
|
{
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
// /api/v1/vm.add-device handler
|
|
pub struct VmAddDevice {}
|
|
|
|
impl EndpointHandler for VmAddDevice {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> Response {
|
|
match req.method() {
|
|
Method::Put => {
|
|
match &req.body {
|
|
Some(body) => {
|
|
// Deserialize into a DeviceConfig
|
|
let vm_add_device_data: DeviceConfig =
|
|
match serde_json::from_slice(body.raw())
|
|
.map_err(HttpError::SerdeJsonDeserialize)
|
|
{
|
|
Ok(config) => config,
|
|
Err(e) => return error_response(e, StatusCode::BadRequest),
|
|
};
|
|
|
|
// Call vm_add_device()
|
|
match vm_add_device(api_notifier, api_sender, Arc::new(vm_add_device_data))
|
|
.map_err(HttpError::VmAddDevice)
|
|
{
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
// /api/v1/vm.remove-device handler
|
|
pub struct VmRemoveDevice {}
|
|
|
|
impl EndpointHandler for VmRemoveDevice {
|
|
fn handle_request(
|
|
&self,
|
|
req: &Request,
|
|
api_notifier: EventFd,
|
|
api_sender: Sender<ApiRequest>,
|
|
) -> Response {
|
|
match req.method() {
|
|
Method::Put => {
|
|
match &req.body {
|
|
Some(body) => {
|
|
// Deserialize into a VmRemoveDeviceData
|
|
let vm_remove_device_data: VmRemoveDeviceData =
|
|
match serde_json::from_slice(body.raw())
|
|
.map_err(HttpError::SerdeJsonDeserialize)
|
|
{
|
|
Ok(config) => config,
|
|
Err(e) => return error_response(e, StatusCode::BadRequest),
|
|
};
|
|
|
|
// Call vm_remove_device()
|
|
match vm_remove_device(
|
|
api_notifier,
|
|
api_sender,
|
|
Arc::new(vm_remove_device_data),
|
|
)
|
|
.map_err(HttpError::VmRemoveDevice)
|
|
{
|
|
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),
|
|
}
|
|
}
|
|
}
|