diff --git a/cloud-hypervisor/src/bin/ch-remote.rs b/cloud-hypervisor/src/bin/ch-remote.rs index aa95ce58a..fd48ffab1 100644 --- a/cloud-hypervisor/src/bin/ch-remote.rs +++ b/cloud-hypervisor/src/bin/ch-remote.rs @@ -65,6 +65,8 @@ enum Error { ReadingStdin(#[source] std::io::Error), #[error("Error reading from file")] ReadingFile(#[source] std::io::Error), + #[error("Invalid disk size")] + InvalidDiskSize(#[source] ByteSizedParseError), } enum TargetApi<'a> { @@ -320,6 +322,22 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu )?; simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient) } + Some("resize-disk") => { + let resize_disk = resize_disk_config( + matches + .subcommand_matches("resize-disk") + .unwrap() + .get_one::("disk") + .unwrap(), + matches + .subcommand_matches("resize-disk") + .unwrap() + .get_one::("size") + .unwrap(), + )?; + simple_api_command(socket, "PUT", "resize-disk", Some(&resize_disk)) + .map_err(Error::HttpApiClient) + } Some("resize-zone") => { let resize_zone = resize_zone_config( matches @@ -762,6 +780,15 @@ fn resize_config( Ok(serde_json::to_string(&resize).unwrap()) } +fn resize_disk_config(id: &str, size: &str) -> Result { + let resize_disk = vmm::api::VmResizeDiskData { + id: id.to_owned(), + desired_size: size.parse::().map_err(Error::InvalidDiskSize)?.0, + }; + + Ok(serde_json::to_string(&resize_disk).unwrap()) +} + fn resize_zone_config(id: &str, size: &str) -> Result { let resize_zone = vmm::api::VmResizeZoneData { id: id.to_owned(), @@ -1022,6 +1049,20 @@ fn get_cli_commands_sorted() -> Box<[Command]> { .help("New memory size in bytes (supports K/M/G suffix)") .num_args(1), ), + Command::new("resize-disk") + .about("Resize an attached disk") + .arg( + Arg::new("disk") + .long("disk") + .help("Disk identifier") + .num_args(1), + ) + .arg( + Arg::new("size") + .long("size") + .help("New disk size") + .num_args(1), + ), Command::new("resize-zone") .about("Resize a memory zone") .arg( diff --git a/vmm/src/api/http/http_endpoint.rs b/vmm/src/api/http/http_endpoint.rs index be8118e0e..e463a2081 100644 --- a/vmm/src/api/http/http_endpoint.rs +++ b/vmm/src/api/http/http_endpoint.rs @@ -47,8 +47,8 @@ use crate::api::http::{EndpointHandler, HttpError, error_response}; use crate::api::{ 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, + VmPowerButton, VmReboot, VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeDisk, + VmResizeZone, VmRestore, VmResume, VmSendMigration, VmShutdown, VmSnapshot, }; use crate::config::RestoreConfig; use crate::cpu::Error as CpuError; @@ -424,6 +424,7 @@ 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!(VmResizeDisk); vm_action_put_handler_body!(VmResizeZone); vm_action_put_handler_body!(VmSnapshot); vm_action_put_handler_body!(VmReceiveMigration); diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index 9ba679a80..2aa52e8e3 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -30,7 +30,7 @@ use crate::api::VmCoredump; use crate::api::{ AddDisk, ApiError, ApiRequest, VmAddDevice, VmAddFs, VmAddNet, VmAddPmem, VmAddUserDevice, VmAddVdpa, VmAddVsock, VmBoot, VmCounters, VmDelete, VmNmi, VmPause, VmPowerButton, VmReboot, - VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeZone, VmRestore, VmResume, + VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeDisk, VmResizeZone, VmRestore, VmResume, VmSendMigration, VmShutdown, VmSnapshot, }; use crate::landlock::Landlock; @@ -251,6 +251,10 @@ pub static HTTP_ROUTES: LazyLock = LazyLock::new(|| { endpoint!("/vm.resize"), Box::new(VmActionHandler::new(&VmResize)), ); + r.routes.insert( + endpoint!("/vm.resize-disk"), + Box::new(VmActionHandler::new(&VmResizeDisk)), + ); r.routes.insert( endpoint!("/vm.resize-zone"), Box::new(VmActionHandler::new(&VmResizeZone)), diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 3b5fe83dd..12ca6b987 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -134,6 +134,10 @@ pub enum ApiError { #[error("The VM could not be resized")] VmResize(#[source] VmError), + /// The disk could not be resized. + #[error("The disk could not be resized")] + VmResizeDisk(#[source] VmError), + /// The memory zone could not be resized. #[error("The memory zone could not be resized")] VmResizeZone(#[source] VmError), @@ -223,6 +227,12 @@ pub struct VmResizeData { pub desired_balloon: Option, } +#[derive(Clone, Deserialize, Serialize, Default, Debug)] +pub struct VmResizeDiskData { + pub id: String, + pub desired_size: u64, +} + #[derive(Clone, Deserialize, Serialize, Default, Debug)] pub struct VmResizeZoneData { pub id: String, @@ -1139,6 +1149,41 @@ impl ApiAction for VmResize { } } +pub struct VmResizeDisk; + +impl ApiAction for VmResizeDisk { + type RequestBody = VmResizeDiskData; + type ResponseBody = Option; + + fn request( + &self, + resize_disk_data: Self::RequestBody, + response_sender: Sender, + ) -> ApiRequest { + Box::new(move |vmm| { + let response = vmm + .vm_resize_disk(resize_disk_data.id, resize_disk_data.desired_size) + .map_err(ApiError::VmResizeDisk) + .map(|_| ApiResponsePayload::Empty); + + response_sender + .send(response) + .map_err(VmmError::ApiResponseSend)?; + + Ok(false) + }) + } + + fn send( + &self, + api_evt: EventFd, + api_sender: Sender, + data: Self::RequestBody, + ) -> ApiResult { + get_response_body(self, api_evt, api_sender, data) + } +} + pub struct VmResizeZone; impl ApiAction for VmResizeZone {