From 39d4f817f07120c43bac2a8b0d774acd2f11a207 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 24 Nov 2019 19:37:08 +0100 Subject: [PATCH] vmm: http: Add a /api/v1/vm.snapshot endpoint Signed-off-by: Samuel Ortiz --- vmm/src/api/http.rs | 3 +- vmm/src/api/http_endpoint.rs | 48 +++++++++++++++++++++-- vmm/src/api/openapi/cloud-hypervisor.yaml | 24 ++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index 9b3ccd462..d15cffe29 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -5,7 +5,7 @@ use crate::api::http_endpoint::{ VmActionHandler, VmAddDevice, VmAddDisk, VmAddNet, VmAddPmem, VmCreate, VmInfo, VmRemoveDevice, - VmResize, VmmPing, VmmShutdown, + VmResize, VmSnapshot, VmmPing, VmmShutdown, }; use crate::api::{ApiRequest, VmAction}; use crate::seccomp_filters::{get_seccomp_filter, Thread}; @@ -62,6 +62,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume))); 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!("/vm.snapshot"), Box::new(VmSnapshot {})); 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 {})); diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index 5738fbcd1..1a21b09ef 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -6,9 +6,9 @@ use crate::api::http::EndpointHandler; use crate::api::{ vm_add_device, vm_add_disk, vm_add_net, vm_add_pmem, 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, DiskConfig, NetConfig, PmemConfig, - VmAction, VmConfig, VmRemoveDeviceData, VmResizeData, + vm_pause, vm_reboot, vm_remove_device, vm_resize, vm_resume, vm_shutdown, vm_snapshot, + vmm_ping, vmm_shutdown, ApiError, ApiRequest, ApiResult, DeviceConfig, DiskConfig, NetConfig, + PmemConfig, VmAction, VmConfig, VmRemoveDeviceData, VmResizeData, VmSnapshotConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -43,6 +43,9 @@ pub enum HttpError { /// Could not reboot a VM VmReboot(ApiError), + /// Could not snapshot a VM + VmSnapshot(ApiError), + /// Could not act on a VM VmAction(ApiError), @@ -192,6 +195,45 @@ impl EndpointHandler for VmInfo { } } +// /api/v1/vm.snapshot handler +pub struct VmSnapshot {} + +impl EndpointHandler for VmSnapshot { + 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 VmSnapshotConfig + let vm_snapshot_data: VmSnapshotConfig = + match serde_json::from_slice(body.raw()) + .map_err(HttpError::SerdeJsonDeserialize) + { + Ok(data) => data, + Err(e) => return error_response(e, StatusCode::BadRequest), + }; + + // Call vm_snapshot() + match vm_snapshot(api_notifier, api_sender, Arc::new(vm_snapshot_data)) + .map_err(HttpError::VmSnapshot) + { + 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/vmm.info handler pub struct VmmPing {} diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index c16e05a5b..62cf8560f 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -171,6 +171,24 @@ paths: 404: description: The device could not be removed from the VM instance. + /vm.snapshot: + put: + summary: Returns a VM snapshot. + requestBody: + description: The snapshot configuration + content: + application/json: + schema: + $ref: '#/components/schemas/VmSnapshotConfig' + required: true + responses: + 204: + description: The VM instance was successfully snapshotted. + 404: + description: The VM instance could not be snapshotted because it is not created. + 405: + description: The VM instance could not be snapshotted because it is not booted. + /vm.add-disk: put: summary: Add a new disk to the VM @@ -542,3 +560,9 @@ components: properties: id: type: string + + VmSnapshotConfig: + type: object + properties: + destination_url: + type: string