Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Disk size in Kibibyte #305

Merged
merged 9 commits into from
Feb 5, 2024
14 changes: 9 additions & 5 deletions proxmox/config_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,11 +872,8 @@ func (newConfig ConfigQemu) setAdvanced(currentConfig *ConfigQemu, rebootIfNeede
return
}
}
for _, e := range markedDisks.Resize { // increase Disks in size
_, err = e.resize(vmr, client)
if err != nil {
return false, err
}
if err = resizeDisks(vmr, client, markedDisks.Resize); err != nil { // increase Disks in size
return false, err
}
itemsToDeleteBeforeUpdate = newConfig.Disks.cloudInitRemove(*currentConfig.Disks)
}
Expand Down Expand Up @@ -938,6 +935,10 @@ func (newConfig ConfigQemu) setAdvanced(currentConfig *ConfigQemu, rebootIfNeede
}
}

if err = resizeNewDisks(vmr, client, newConfig.Disks, currentConfig.Disks); err != nil {
return
}

if stopped { // start vm if it was stopped
if rebootIfNeeded {
if err = GuestStart(vmr, client); err != nil {
Expand Down Expand Up @@ -967,6 +968,9 @@ func (newConfig ConfigQemu) setAdvanced(currentConfig *ConfigQemu, rebootIfNeede
if err != nil {
return false, fmt.Errorf("error creating VM: %v, error status: %s (params: %v)", err, exitStatus, params)
}
if err = resizeNewDisks(vmr, client, newConfig.Disks, nil); err != nil {
return
}
}

_, err = client.UpdateVMHA(vmr, newConfig.HaState, newConfig.HaGroup)
Expand Down
171 changes: 128 additions & 43 deletions proxmox/config_qemu_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const (
type IsoFile struct {
File string `json:"file"`
Storage string `json:"storage"`
// Size can only be retrieved, setting it has no effect
Size string `json:"size"`
// SizeInKibibytes can only be retrieved, setting it has no effect
SizeInKibibytes string `json:"size"`
}

const (
Expand Down Expand Up @@ -62,9 +62,9 @@ func (QemuCdRom) mapToStruct(settings qemuCdRom) *QemuCdRom {
if settings.File != "" {
return &QemuCdRom{
Iso: &IsoFile{
Storage: settings.Storage,
File: settings.File,
Size: settings.Size,
Storage: settings.Storage,
File: settings.File,
SizeInKibibytes: settings.SizeInKibibytes,
},
}
}
Expand All @@ -87,11 +87,11 @@ func (cdRom QemuCdRom) Validate() error {
type qemuCdRom struct {
CdRom bool
// "local:iso/debian-11.0.0-amd64-netinst.iso,media=cdrom,size=377M"
Passthrough bool
Storage string
Format QemuDiskFormat // Only set for Cloud-init drives
File string
Size string
Passthrough bool
Storage string
Format QemuDiskFormat // Only set for Cloud-init drives
File string
SizeInKibibytes string
}

func (qemuCdRom) mapToStruct(diskData string, settings map[string]interface{}) *qemuCdRom {
Expand Down Expand Up @@ -125,10 +125,10 @@ func (qemuCdRom) mapToStruct(diskData string, settings map[string]interface{}) *
if fileType == "iso" {
if setting, isSet := settings["size"]; isSet {
return &qemuCdRom{
CdRom: true,
Storage: tmpStorage[0],
File: tmpFile[1],
Size: setting.(string),
CdRom: true,
Storage: tmpStorage[0],
File: tmpFile[1],
SizeInKibibytes: setting.(string),
}
}
} else {
Expand Down Expand Up @@ -191,16 +191,16 @@ type qemuDisk struct {
Disk bool // true = disk, false = passthrough
EmulateSSD bool // Only set for ide,sata,scsi
// TODO custom type
File string // Only set for Passthrough.
fileSyntax diskSyntaxEnum // private enum to determine the syntax of the file path, as this changes depending on the type of backing storage. ie nfs, lvm, local, etc.
Format QemuDiskFormat // Only set for Disk
Id uint // Only set for Disk
IOThread bool // Only set for scsi,virtio
LinkedDiskId *uint // Only set for Disk
ReadOnly bool // Only set for scsi,virtio
Replicate bool
Serial QemuDiskSerial
Size uint
File string // Only set for Passthrough.
fileSyntax diskSyntaxEnum // private enum to determine the syntax of the file path, as this changes depending on the type of backing storage. ie nfs, lvm, local, etc.
Format QemuDiskFormat // Only set for Disk
Id uint // Only set for Disk
IOThread bool // Only set for scsi,virtio
LinkedDiskId *uint // Only set for Disk
ReadOnly bool // Only set for scsi,virtio
Replicate bool
Serial QemuDiskSerial
SizeInKibibytes QemuDiskSize
// TODO custom type
Storage string // Only set for Disk
Type qemuDiskType
Expand All @@ -210,7 +210,6 @@ type qemuDisk struct {
const (
Error_QemuDisk_File string = "file may not be empty"
Error_QemuDisk_MutuallyExclusive string = "settings cdrom,cloudinit,disk,passthrough are mutually exclusive"
Error_QemuDisk_Size string = "size must be greater then 0"
Error_QemuDisk_Storage string = "storage may not be empty"
)

Expand Down Expand Up @@ -253,7 +252,11 @@ func (disk qemuDisk) formatDisk(vmID, LinkedVmId uint, currentStorage string, cu
func (disk qemuDisk) mapToApiValues(vmID, LinkedVmId uint, currentStorage string, currentFormat QemuDiskFormat, syntax diskSyntaxEnum, create bool) (settings string) {
if disk.Storage != "" {
if create {
settings = disk.Storage + ":" + strconv.Itoa(int(disk.Size))
if disk.SizeInKibibytes%gibibyte == 0 {
settings = disk.Storage + ":" + strconv.FormatInt(int64(disk.SizeInKibibytes/gibibyte), 10)
} else {
settings = disk.Storage + ":0.001"
}
} else {
settings = disk.formatDisk(vmID, LinkedVmId, currentStorage, currentFormat, syntax)
}
Expand Down Expand Up @@ -421,16 +424,7 @@ func (qemuDisk) mapToStruct(diskData string, settings map[string]interface{}, li
disk.Serial = QemuDiskSerial(value.(string))
}
if value, isSet := settings["size"]; isSet {
sizeString := value.(string)
if len(sizeString) > 1 {
diskSize, _ := strconv.Atoi(sizeString[0 : len(sizeString)-1])
switch sizeString[len(sizeString)-1:] {
case "G":
disk.Size = uint(diskSize)
case "T":
disk.Size = uint(diskSize * 1024)
}
}
disk.SizeInKibibytes = QemuDiskSize(0).parse(value.(string))
}
if value, isSet := settings["ssd"]; isSet {
disk.EmulateSSD, _ = strconv.ParseBool(value.(string))
Expand Down Expand Up @@ -532,8 +526,8 @@ func (disk *qemuDisk) validate() (err error) {
if err = disk.Format.Validate(); err != nil {
return
}
if disk.Size == 0 {
return errors.New(Error_QemuDisk_Size)
if err = disk.SizeInKibibytes.Validate(); err != nil {
return
}
if disk.Storage == "" {
return errors.New(Error_QemuDisk_Storage)
Expand Down Expand Up @@ -795,7 +789,7 @@ func (id QemuDiskId) Validate() error {
type qemuDiskMark struct {
Format QemuDiskFormat
Id QemuDiskId
Size uint
Size QemuDiskSize
Storage string
Type qemuDiskType
}
Expand All @@ -811,7 +805,7 @@ func (disk *qemuDiskMark) markChanges(currentDisk *qemuDiskMark, id QemuDiskId,
if disk.Size > currentDisk.Size {
changes.Resize = append(changes.Resize, qemuDiskResize{
Id: id,
SizeInGigaBytes: disk.Size,
SizeInKibibytes: disk.Size,
})
}
if disk.Storage != currentDisk.Storage || disk.Format != currentDisk.Format {
Expand Down Expand Up @@ -848,15 +842,49 @@ func (serial QemuDiskSerial) Validate() error {
return nil
}

// Amount of Kibibytes the disk should be.
// Disk size must be greater then 4096.
type QemuDiskSize uint

const (
QemuDiskSize_Error_Minimum string = "disk size must be greater then 4096"
qemuDiskSize_Minimum QemuDiskSize = 4097
mebibyte QemuDiskSize = 1024
gibibyte QemuDiskSize = 1048576
tebibyte QemuDiskSize = 1073741824
)

func (QemuDiskSize) parse(rawSize string) (size QemuDiskSize) {
tmpSize, _ := strconv.ParseInt(rawSize[:len(rawSize)-1], 10, 0)
switch rawSize[len(rawSize)-1:] {
case "T":
size = QemuDiskSize(tmpSize) * tebibyte
case "G":
size = QemuDiskSize(tmpSize) * gibibyte
case "M":
size = QemuDiskSize(tmpSize) * mebibyte
case "K":
size = QemuDiskSize(tmpSize)
}
return
}

func (size QemuDiskSize) Validate() error {
if size < qemuDiskSize_Minimum {
return errors.New(QemuDiskSize_Error_Minimum)
}
return nil
}

type qemuDiskResize struct {
Id QemuDiskId
SizeInGigaBytes uint
SizeInKibibytes QemuDiskSize
}

// Increase the disk size to the specified amount in gigabytes
// Decrease of disk size is not permitted.
func (disk qemuDiskResize) resize(vmr *VmRef, client *Client) (exitStatus string, err error) {
return client.PutWithTask(map[string]interface{}{"disk": disk.Id, "size": strconv.Itoa(int(disk.SizeInGigaBytes)) + "G"}, fmt.Sprintf("/nodes/%s/%s/%d/resize", vmr.node, vmr.vmType, vmr.vmId))
return client.PutWithTask(map[string]interface{}{"disk": disk.Id, "size": strconv.FormatInt(int64(disk.SizeInKibibytes), 10) + "K"}, fmt.Sprintf("/nodes/%s/%s/%d/resize", vmr.node, vmr.vmType, vmr.vmId))
}

type qemuDiskMove struct {
Expand Down Expand Up @@ -952,7 +980,7 @@ func (storage *qemuStorage) mapToApiValues(currentStorage *qemuStorage, vmID, li
// Create
params[string(id)] = storage.Disk.mapToApiValues(vmID, 0, "", "", false, true)
} else {
if storage.Disk.Size >= currentStorage.Disk.Size {
if storage.Disk.SizeInKibibytes >= currentStorage.Disk.SizeInKibibytes {
// Update
storage.Disk.Id = currentStorage.Disk.Id
storage.Disk.LinkedDiskId = currentStorage.Disk.LinkedDiskId
Expand Down Expand Up @@ -1083,6 +1111,38 @@ func (storages QemuStorages) markDiskChanges(currentStorages QemuStorages) *qemu
return changes
}

// Select all new disks that do not have their size in gibigytes for resizing to the specified size
func (newStorages QemuStorages) selectInitialResize(currentStorages *QemuStorages) (resize []qemuDiskResize) {
if currentStorages == nil {
if newStorages.Ide != nil {
resize = newStorages.Ide.selectInitialResize(nil)
}
if newStorages.Sata != nil {
resize = append(resize, newStorages.Sata.selectInitialResize(nil)...)
}
if newStorages.Scsi != nil {
resize = append(resize, newStorages.Scsi.selectInitialResize(nil)...)
}
if newStorages.VirtIO != nil {
resize = append(resize, newStorages.VirtIO.selectInitialResize(nil)...)
}
return
}
if newStorages.Ide != nil {
resize = newStorages.Ide.selectInitialResize(currentStorages.Ide)
}
if newStorages.Sata != nil {
resize = append(resize, newStorages.Sata.selectInitialResize(currentStorages.Sata)...)
}
if newStorages.Scsi != nil {
resize = append(resize, newStorages.Scsi.selectInitialResize(currentStorages.Scsi)...)
}
if newStorages.VirtIO != nil {
resize = append(resize, newStorages.VirtIO.selectInitialResize(currentStorages.VirtIO)...)
}
return
}

func (storages QemuStorages) Validate() (err error) {
var numberOfCloudInitDevices uint8
var CloudInit uint8
Expand Down Expand Up @@ -1165,3 +1225,28 @@ func MoveQemuDisk(format *QemuDiskFormat, diskId QemuDiskId, storage string, del
_, err = disk.move(deleteAfterMove, vmr, client)
return
}

// increase Disks in size
func resizeDisks(vmr *VmRef, client *Client, disks []qemuDiskResize) (err error) {
for _, e := range disks {
_, err = e.resize(vmr, client)
if err != nil {
return
}
}
return
}

// Resize newly created disks
func resizeNewDisks(vmr *VmRef, client *Client, newDisks, currentDisks *QemuStorages) (err error) {
if newDisks == nil {
return
}
resize := newDisks.selectInitialResize(currentDisks)
if len(resize) > 0 {
if err = resizeDisks(vmr, client, resize); err != nil {
return
}
}
return
}
Loading
Loading