diff --git a/docs/api.md b/docs/api.md index 77a1d9959..49e594940 100644 --- a/docs/api.md +++ b/docs/api.md @@ -96,6 +96,7 @@ Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` Add fs device to the VM | `/vm.add-fs` | `/schemas/FsConfig` | N/A | The VM is booted Add pmem device to the VM | `/vm.add-pmem` | `/schemas/PmemConfig` | N/A | The VM is booted Add network device to the VM | `/vm.add-net` | `/schemas/NetConfig` | N/A | The VM is booted +Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | N/A | The VM is booted ### REST API Examples diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index f361b0e67..baf54ad0b 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -4,8 +4,8 @@ // use crate::api::http_endpoint::{ - VmActionHandler, VmAddDevice, VmAddDisk, VmAddFs, VmAddNet, VmAddPmem, VmCreate, VmInfo, - VmRemoveDevice, VmResize, VmRestore, VmSnapshot, VmmPing, VmmShutdown, + VmActionHandler, VmAddDevice, VmAddDisk, VmAddFs, VmAddNet, VmAddPmem, VmAddVsock, VmCreate, + VmInfo, VmRemoveDevice, VmResize, VmRestore, VmSnapshot, VmmPing, VmmShutdown, }; use crate::api::{ApiRequest, VmAction}; use crate::seccomp_filters::{get_seccomp_filter, Thread}; @@ -73,6 +73,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.add-fs"), Box::new(VmAddFs {})); r.routes.insert(endpoint!("/vm.add-pmem"), Box::new(VmAddPmem {})); r.routes.insert(endpoint!("/vm.add-net"), Box::new(VmAddNet {})); + r.routes.insert(endpoint!("/vm.add-vsock"), Box::new(VmAddVsock {})); r }; diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index 60c0f80f0..9318befa4 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -5,11 +5,11 @@ use crate::api::http::EndpointHandler; use crate::api::{ - vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_boot, vm_create, vm_delete, - vm_info, vm_pause, vm_reboot, vm_remove_device, vm_resize, vm_restore, vm_resume, vm_shutdown, - vm_snapshot, vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult, DeviceConfig, DiskConfig, - FsConfig, NetConfig, PmemConfig, RestoreConfig, VmAction, VmConfig, VmRemoveDeviceData, - VmResizeData, VmSnapshotConfig, + vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_add_vsock, vm_boot, + vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_remove_device, vm_resize, vm_restore, + vm_resume, vm_shutdown, vm_snapshot, vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult, + DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmAction, VmConfig, + VmRemoveDeviceData, VmResizeData, VmSnapshotConfig, VsockConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -79,6 +79,9 @@ pub enum HttpError { /// Could not add a network device to a VM VmAddNet(ApiError), + + /// Could not add a vsock device to a VM + VmAddVsock(ApiError), } fn error_response(error: HttpError, status: StatusCode) -> Response { @@ -604,3 +607,42 @@ impl EndpointHandler for VmAddNet { } } } + +// /api/v1/vm.add-vsock handler +pub struct VmAddVsock {} + +impl EndpointHandler for VmAddVsock { + 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 VsockConfig + let vm_add_vsock_data: VsockConfig = + match serde_json::from_slice(body.raw()) + .map_err(HttpError::SerdeJsonDeserialize) + { + Ok(config) => config, + Err(e) => return error_response(e, StatusCode::BadRequest), + }; + + match vm_add_vsock(api_notifier, api_sender, Arc::new(vm_add_vsock_data)) + .map_err(HttpError::VmAddVsock) + { + 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 e179616b3..2001dd857 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -38,7 +38,7 @@ pub mod http; pub mod http_endpoint; use crate::config::{ - DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, + DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, VsockConfig, }; use crate::vm::{Error as VmError, VmState}; use std::io; @@ -132,6 +132,9 @@ pub enum ApiError { /// The network device could not be added to the VM. VmAddNet(VmError), + + /// The vsock device could not be added to the VM. + VmAddVsock(VmError), } pub type ApiResult = std::result::Result; @@ -244,6 +247,9 @@ pub enum ApiRequest { /// Add a network device to the VM. VmAddNet(Arc, Sender), + /// Add a vsock device to the VM. + VmAddVsock(Arc, Sender), + /// Take a VM snapshot VmSnapshot(Arc, Sender), @@ -545,3 +551,21 @@ pub fn vm_add_net( Ok(()) } + +pub fn vm_add_vsock( + api_evt: EventFd, + api_sender: Sender, + data: Arc, +) -> ApiResult<()> { + let (response_sender, response_receiver) = channel(); + + // Send the VM add-vsock request. + api_sender + .send(ApiRequest::VmAddVsock(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 ad3362a25..3c677d927 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -235,6 +235,23 @@ paths: 500: description: The new device could not be added to the VM instance. + /vm.add-vsock: + put: + summary: Add a new vsock device to the VM + requestBody: + description: The details of the new vsock device + content: + application/json: + schema: + $ref: '#/components/schemas/VsockConfig' + required: true + responses: + 204: + description: The new device was successfully added to the VM instance. + 500: + description: The new device could not be added to the VM instance. + + /vm.snapshot: put: summary: Returns a VM snapshot. diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 36b07d23c..3c3b4aa34 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -19,7 +19,7 @@ extern crate vmm_sys_util; use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo, VmmPingResponse}; use crate::config::{ - DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, + DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, VsockConfig, }; use crate::migration::{recv_vm_snapshot, vm_config_from_snapshot}; use crate::seccomp_filters::{get_seccomp_filter, Thread}; @@ -524,6 +524,19 @@ impl Vmm { } } + fn vm_add_vsock(&mut self, vsock_cfg: VsockConfig) -> result::Result<(), VmError> { + if let Some(ref mut vm) = self.vm { + if let Err(e) = vm.add_vsock(vsock_cfg) { + error!("Error when adding new vsock device to the VM: {:?}", e); + Err(e) + } else { + Ok(()) + } + } else { + Err(VmError::VmNotRunning) + } + } + fn control_loop(&mut self, api_receiver: Arc>) -> Result<()> { const EPOLL_EVENTS_LEN: usize = 100; @@ -737,6 +750,13 @@ impl Vmm { .map(|_| ApiResponsePayload::Empty); sender.send(response).map_err(Error::ApiResponseSend)?; } + ApiRequest::VmAddVsock(add_vsock_data, sender) => { + let response = self + .vm_add_vsock(add_vsock_data.as_ref().clone()) + .map_err(ApiError::VmAddVsock) + .map(|_| ApiResponsePayload::Empty); + sender.send(response).map_err(Error::ApiResponseSend)?; + } } } }