Can resize disk image when adding a new disk image to a VM
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@@ -394,6 +394,40 @@ impl DiskFileInfo {
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get disk virtual size, if available
 | 
			
		||||
    pub fn virtual_size(&self) -> Option<FileSize> {
 | 
			
		||||
        match self.format {
 | 
			
		||||
            DiskFileFormat::Raw { .. } => Some(self.file_size),
 | 
			
		||||
            DiskFileFormat::QCow2 { virtual_size } => Some(virtual_size),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Resize disk
 | 
			
		||||
    pub fn resize(&self, new_size: FileSize) -> anyhow::Result<()> {
 | 
			
		||||
        let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
 | 
			
		||||
        cmd.arg("resize")
 | 
			
		||||
            .arg("-f")
 | 
			
		||||
            .arg(match self.format {
 | 
			
		||||
                DiskFileFormat::QCow2 { .. } => "qcow2",
 | 
			
		||||
                DiskFileFormat::Raw { .. } => "raw",
 | 
			
		||||
                f => anyhow::bail!("Unsupported disk format for resize: {f:?}"),
 | 
			
		||||
            })
 | 
			
		||||
            .arg(&self.file_path)
 | 
			
		||||
            .arg(new_size.as_bytes().to_string());
 | 
			
		||||
 | 
			
		||||
        let output = cmd.output()?;
 | 
			
		||||
        if !output.status.success() {
 | 
			
		||||
            anyhow::bail!(
 | 
			
		||||
                "{} info failed, status: {}, stderr: {}",
 | 
			
		||||
                constants::PROGRAM_QEMU_IMAGE,
 | 
			
		||||
                output.status,
 | 
			
		||||
                String::from_utf8_lossy(&output.stderr)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,9 @@ pub struct VMFileDisk {
 | 
			
		||||
    /// When creating a new disk, specify the disk image template to use
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub from_image: Option<String>,
 | 
			
		||||
    /// Set this variable to true to resize disk image
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub resize: Option<bool>,
 | 
			
		||||
    /// Set this variable to true to delete the disk
 | 
			
		||||
    pub delete: bool,
 | 
			
		||||
}
 | 
			
		||||
@@ -78,6 +81,7 @@ impl VMFileDisk {
 | 
			
		||||
 | 
			
		||||
            delete: false,
 | 
			
		||||
            from_image: None,
 | 
			
		||||
            resize: None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -144,28 +148,40 @@ impl VMFileDisk {
 | 
			
		||||
 | 
			
		||||
        if file.exists() {
 | 
			
		||||
            log::debug!("File {file:?} does not exists, so it was not touched");
 | 
			
		||||
            return Ok(());
 | 
			
		||||
        }
 | 
			
		||||
        // Create disk if required
 | 
			
		||||
        else {
 | 
			
		||||
            // Determine file format
 | 
			
		||||
            let format = match self.format {
 | 
			
		||||
                VMDiskFormat::Raw { is_sparse } => DiskFileFormat::Raw { is_sparse },
 | 
			
		||||
                VMDiskFormat::QCow2 => DiskFileFormat::QCow2 {
 | 
			
		||||
                    virtual_size: self.size,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Create / Restore disk file
 | 
			
		||||
            match &self.from_image {
 | 
			
		||||
                // Create disk file
 | 
			
		||||
                None => {
 | 
			
		||||
                    DiskFileInfo::create(&file, format, self.size)?;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Restore disk image template
 | 
			
		||||
                Some(disk_img) => {
 | 
			
		||||
                    let src_file =
 | 
			
		||||
                        DiskFileInfo::load_file(&AppConfig::get().disk_images_file_path(disk_img))?;
 | 
			
		||||
                    src_file.convert(&file, format)?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let format = match self.format {
 | 
			
		||||
            VMDiskFormat::Raw { is_sparse } => DiskFileFormat::Raw { is_sparse },
 | 
			
		||||
            VMDiskFormat::QCow2 => DiskFileFormat::QCow2 {
 | 
			
		||||
                virtual_size: self.size,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        // Resize disk file if requested
 | 
			
		||||
        if self.resize == Some(true) {
 | 
			
		||||
            let disk = DiskFileInfo::load_file(&file)?;
 | 
			
		||||
 | 
			
		||||
        // Create / Restore disk file
 | 
			
		||||
        match &self.from_image {
 | 
			
		||||
            // Create disk file
 | 
			
		||||
            None => {
 | 
			
		||||
                DiskFileInfo::create(&file, format, self.size)?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Restore disk image template
 | 
			
		||||
            Some(disk_img) => {
 | 
			
		||||
                let src_file =
 | 
			
		||||
                    DiskFileInfo::load_file(&AppConfig::get().disk_images_file_path(disk_img))?;
 | 
			
		||||
                src_file.convert(&file, format)?;
 | 
			
		||||
            // Can only increase disk size
 | 
			
		||||
            if let Err(e) = disk.resize(self.size) {
 | 
			
		||||
                log::error!("Failed to resize disk file {}: {e:?}", self.name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,9 @@ export interface BaseFileVMDisk {
 | 
			
		||||
  // For new disk only
 | 
			
		||||
  from_image?: string;
 | 
			
		||||
 | 
			
		||||
  // Resize disk image after clone
 | 
			
		||||
  resize?: boolean;
 | 
			
		||||
 | 
			
		||||
  // application attributes
 | 
			
		||||
  new?: boolean;
 | 
			
		||||
  deleteType?: "keepfile" | "deletefile";
 | 
			
		||||
 
 | 
			
		||||
@@ -218,24 +218,40 @@ function DiskInfo(p: {
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <TextInput
 | 
			
		||||
        editable={true}
 | 
			
		||||
        label="Disk size (GB)"
 | 
			
		||||
        size={{
 | 
			
		||||
          min:
 | 
			
		||||
            ServerApi.Config.constraints.disk_size.min / (1000 * 1000 * 1000),
 | 
			
		||||
          max:
 | 
			
		||||
            ServerApi.Config.constraints.disk_size.max / (1000 * 1000 * 1000),
 | 
			
		||||
        }}
 | 
			
		||||
        value={(p.disk.size / (1000 * 1000 * 1000)).toString()}
 | 
			
		||||
        onValueChange={(v) => {
 | 
			
		||||
          p.disk.size = Number(v ?? "0") * 1000 * 1000 * 1000;
 | 
			
		||||
          p.onChange?.();
 | 
			
		||||
        }}
 | 
			
		||||
        type="number"
 | 
			
		||||
        disabled={!!p.disk.from_image}
 | 
			
		||||
      />
 | 
			
		||||
      {/* Resize disk image */}
 | 
			
		||||
      {!!p.disk.from_image && (
 | 
			
		||||
        <CheckboxInput
 | 
			
		||||
          editable
 | 
			
		||||
          checked={p.disk.resize}
 | 
			
		||||
          label="Resize disk file"
 | 
			
		||||
          onValueChange={(v) => {
 | 
			
		||||
            p.disk.resize = v;
 | 
			
		||||
            p.onChange?.();
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {/* Disk size */}
 | 
			
		||||
      {(!p.disk.from_image || p.disk.resize === true) && (
 | 
			
		||||
        <TextInput
 | 
			
		||||
          editable={true}
 | 
			
		||||
          label="Disk size (GB)"
 | 
			
		||||
          size={{
 | 
			
		||||
            min:
 | 
			
		||||
              ServerApi.Config.constraints.disk_size.min / (1000 * 1000 * 1000),
 | 
			
		||||
            max:
 | 
			
		||||
              ServerApi.Config.constraints.disk_size.max / (1000 * 1000 * 1000),
 | 
			
		||||
          }}
 | 
			
		||||
          value={(p.disk.size / (1000 * 1000 * 1000)).toString()}
 | 
			
		||||
          onValueChange={(v) => {
 | 
			
		||||
            p.disk.size = Number(v ?? "0") * 1000 * 1000 * 1000;
 | 
			
		||||
            p.onChange?.();
 | 
			
		||||
          }}
 | 
			
		||||
          type="number"
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {/* Disk image selection */}
 | 
			
		||||
      <DiskImageSelect
 | 
			
		||||
        label="Use disk image as template"
 | 
			
		||||
        list={p.diskImagesList}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user