| 233 | |
| 234 | #[allow(clippy::too_many_arguments)] |
| 235 | async fn apply_resource_updates( |
| 236 | &self, |
| 237 | vm_id: &str, |
| 238 | manifest: &mut Manifest, |
| 239 | vm_work_dir: &VmWorkDir, |
| 240 | vcpu: Option<u32>, |
| 241 | memory: Option<u32>, |
| 242 | disk_size: Option<u32>, |
| 243 | image: Option<&str>, |
| 244 | ) -> Result<bool> { |
| 245 | let has_updates = |
| 246 | vcpu.is_some() || memory.is_some() || disk_size.is_some() || image.is_some(); |
| 247 | if !has_updates { |
| 248 | return Ok(false); |
| 249 | } |
| 250 | |
| 251 | let vm = self.app.vm_info(vm_id).await?.context("vm not found")?; |
| 252 | if !["stopped", "exited"].contains(&vm.status.as_str()) { |
| 253 | bail!("vm should be stopped before resize: {}", vm_id); |
| 254 | } |
| 255 | |
| 256 | if let Some(vcpu) = vcpu { |
| 257 | manifest.vcpu = vcpu; |
| 258 | } |
| 259 | if let Some(memory) = memory { |
| 260 | manifest.memory = memory; |
| 261 | } |
| 262 | if let Some(image) = image { |
| 263 | manifest.image = image.to_string(); |
| 264 | } |
| 265 | if let Some(disk_size) = disk_size { |
| 266 | if disk_size < manifest.disk_size { |
| 267 | bail!("Cannot shrink disk size"); |
| 268 | } |
| 269 | manifest.disk_size = disk_size; |
| 270 | |
| 271 | info!("Resizing disk to {}GB", disk_size); |
| 272 | let hda_path = vm_work_dir.hda_path(); |
| 273 | let new_size_str = format!("{}G", disk_size); |
| 274 | let output = std::process::Command::new("qemu-img") |
| 275 | .args(["resize", &hda_path.display().to_string(), &new_size_str]) |
| 276 | .output() |
| 277 | .context("Failed to resize disk")?; |
| 278 | if !output.status.success() { |
| 279 | bail!( |
| 280 | "Failed to resize disk: {}", |
| 281 | String::from_utf8_lossy(&output.stderr) |
| 282 | ); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | Ok(true) |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | impl VmmRpc for RpcHandler { |