vmm: api: Be more specific on "Still pending remove vcpu" errors

Although the CPU manager gives us a quite descriptive error, on the
application side (the part calling Cloud Hypervisor) we have absolutely
no way to distinguish such error from any other error that may happen
when resizing a VM.

With this in mind, let's be more specific and return a TooManyRequests
(429) error, allowing the caller to have a chance to decide whether they
want to retry the operation or not.

https://datatracker.ietf.org/doc/html/rfc6585#section-4

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
This commit is contained in:
Fabiano Fidêncio 2025-04-25 20:21:18 +02:00 committed by Bo Chen
parent 87007a288f
commit d0225fe68f
3 changed files with 38 additions and 2 deletions

View file

@ -15,12 +15,14 @@ use crate::api::http::{error_response, EndpointHandler, HttpError};
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
use crate::api::VmCoredump;
use crate::api::{
AddDisk, ApiAction, ApiRequest, NetConfig, VmAddDevice, VmAddFs, VmAddNet, VmAddPmem,
AddDisk, ApiAction, ApiError, ApiRequest, NetConfig, VmAddDevice, VmAddFs, VmAddNet, VmAddPmem,
VmAddUserDevice, VmAddVdpa, VmAddVsock, VmBoot, VmConfig, VmCounters, VmDelete, VmNmi, VmPause,
VmPowerButton, VmReboot, VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeZone, VmRestore,
VmResume, VmSendMigration, VmShutdown, VmSnapshot,
};
use crate::config::RestoreConfig;
use crate::cpu::Error as CpuError;
use crate::vm::Error as VmError;
// /api/v1/vm.create handler
pub struct VmCreate {}
@ -183,7 +185,6 @@ vm_action_put_handler_body!(VmAddVdpa);
vm_action_put_handler_body!(VmAddVsock);
vm_action_put_handler_body!(VmAddUserDevice);
vm_action_put_handler_body!(VmRemoveDevice);
vm_action_put_handler_body!(VmResize);
vm_action_put_handler_body!(VmResizeZone);
vm_action_put_handler_body!(VmSnapshot);
vm_action_put_handler_body!(VmReceiveMigration);
@ -220,6 +221,34 @@ impl PutHandler for VmAddNet {
impl GetHandler for VmAddNet {}
impl PutHandler for VmResize {
fn handle_request(
&'static self,
api_notifier: EventFd,
api_sender: Sender<ApiRequest>,
body: &Option<Body>,
_files: Vec<File>,
) -> std::result::Result<Option<Body>, HttpError> {
if let Some(body) = body {
self.send(
api_notifier,
api_sender,
serde_json::from_slice(body.raw())?,
)
.map_err(|e| match e {
ApiError::VmResize(VmError::CpuManager(CpuError::VcpuPendingRemovedVcpu)) => {
HttpError::TooManyRequests
}
_ => HttpError::ApiError(e),
})
} else {
Err(HttpError::BadRequest)
}
}
}
impl GetHandler for VmResize {}
impl PutHandler for VmRestore {
fn handle_request(
&'static self,

View file

@ -52,6 +52,9 @@ pub enum HttpError {
/// Undefined endpoints
NotFound,
/// Too many requests
TooManyRequests,
/// Internal Server Error
InternalServerError,
@ -65,6 +68,7 @@ impl Display for HttpError {
match self {
BadRequest => write!(f, "Bad Request"),
NotFound => write!(f, "Not Found"),
TooManyRequests => write!(f, "Too Many Requests"),
InternalServerError => write!(f, "Internal Server Error"),
SerdeJsonDeserialize(serde_error) => write!(f, "{}", serde_error),
ApiError(api_error) => write!(f, "{}", api_error),
@ -125,6 +129,7 @@ pub trait EndpointHandler {
Err(e @ HttpError::SerdeJsonDeserialize(_)) => {
error_response(e, StatusCode::BadRequest)
}
Err(e @ HttpError::TooManyRequests) => error_response(e, StatusCode::TooManyRequests),
Err(e) => error_response(e, StatusCode::InternalServerError),
}
}

View file

@ -160,6 +160,8 @@ paths:
description: The VM instance was successfully resized.
404:
description: The VM instance could not be resized because it is not created.
429:
description: The VM instance could not be resized because a cpu removal is still pending.
/vm.resize-zone:
put: