From 0f325c938f4c99657aa8dadaddae914ead97c1fb Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:15:58 +0000 Subject: [PATCH 1/5] feat: KMGT disk size --- proxmox/converters.go | 49 +++++++++++++++++ proxmox/resource_vm_qemu.go | 106 ++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 47 deletions(-) create mode 100644 proxmox/converters.go diff --git a/proxmox/converters.go b/proxmox/converters.go new file mode 100644 index 00000000..c6c4d599 --- /dev/null +++ b/proxmox/converters.go @@ -0,0 +1,49 @@ +package proxmox + +import ( + "strconv" +) + +const ( + kibibyte int64 = 1 + mebibyte int64 = 1024 + gibibyte int64 = 1048576 + tebibyte int64 = 1073741824 +) + +func convert_KibibytesToString(kibibytes int64) string { + if kibibytes%tebibyte == 0 { + return strconv.FormatInt(kibibytes/tebibyte, 10) + "T" + } + if kibibytes%gibibyte == 0 { + return strconv.FormatInt(kibibytes/gibibyte, 10) + "G" + } + if kibibytes%mebibyte == 0 { + return strconv.FormatInt(kibibytes/mebibyte, 10) + "K" + } + return strconv.FormatInt(kibibytes, 10) + "K" +} + +// Relies on the input being validated +func convert_SizeStringToKibibytes_Unsafe(size string) int { + if len(size) > 1 { + switch size[len(size)-1:] { + case "T": + return parseSize_Unsafe(size, tebibyte) + case "G": + return parseSize_Unsafe(size, gibibyte) + case "M": + return parseSize_Unsafe(size, mebibyte) + case "K": + return parseSize_Unsafe(size, kibibyte) + } + } + tmpSize, _ := strconv.ParseInt(size, 10, 0) + return int(tmpSize * gibibyte) +} + +// Relies on the input being validated +func parseSize_Unsafe(size string, multiplier int64) int { + tmpSize, _ := strconv.ParseInt(size[:len(size)-1], 10, 0) + return int(tmpSize * multiplier) +} diff --git a/proxmox/resource_vm_qemu.go b/proxmox/resource_vm_qemu.go index 3d7373bd..2af58f2a 100755 --- a/proxmox/resource_vm_qemu.go +++ b/proxmox/resource_vm_qemu.go @@ -2464,7 +2464,7 @@ func mapFromStruct_QemuIdeStorage(config *pxapi.QemuIdeStorage, setting string) "linked_disk_id": mapFromStruct_LinkedCloneId(config.Disk.LinkedDiskId), "replicate": config.Disk.Replicate, "serial": string(config.Disk.Serial), - "size": int(config.Disk.Size), + "size": convert_KibibytesToString(int64(config.Disk.SizeInKibibytes)), "storage": string(config.Disk.Storage), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Disk.Bandwidth) @@ -2484,7 +2484,7 @@ func mapFromStruct_QemuIdeStorage(config *pxapi.QemuIdeStorage, setting string) "file": config.Passthrough.File, "replicate": config.Passthrough.Replicate, "serial": string(config.Passthrough.Serial), - "size": int(config.Passthrough.Size), + "size": convert_KibibytesToString(int64(config.Passthrough.SizeInKibibytes)), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Passthrough.Bandwidth) return []interface{}{ @@ -2528,7 +2528,7 @@ func mapFromStruct_QemuSataStorage(config *pxapi.QemuSataStorage, setting string "linked_disk_id": mapFromStruct_LinkedCloneId(config.Disk.LinkedDiskId), "replicate": config.Disk.Replicate, "serial": string(config.Disk.Serial), - "size": int(config.Disk.Size), + "size": convert_KibibytesToString(int64(config.Disk.SizeInKibibytes)), "storage": string(config.Disk.Storage), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Disk.Bandwidth) @@ -2548,7 +2548,7 @@ func mapFromStruct_QemuSataStorage(config *pxapi.QemuSataStorage, setting string "file": config.Passthrough.File, "replicate": config.Passthrough.Replicate, "serial": string(config.Passthrough.Serial), - "size": int(config.Passthrough.Size), + "size": convert_KibibytesToString(int64(config.Disk.SizeInKibibytes)), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Passthrough.Bandwidth) return []interface{}{ @@ -2619,7 +2619,7 @@ func mapFromStruct_QemuScsiStorage(config *pxapi.QemuScsiStorage, setting string "readonly": config.Disk.ReadOnly, "replicate": config.Disk.Replicate, "serial": string(config.Disk.Serial), - "size": int(config.Disk.Size), + "size": convert_KibibytesToString(int64(config.Disk.SizeInKibibytes)), "storage": string(config.Disk.Storage), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Disk.Bandwidth) @@ -2641,7 +2641,7 @@ func mapFromStruct_QemuScsiStorage(config *pxapi.QemuScsiStorage, setting string "readonly": config.Passthrough.ReadOnly, "replicate": config.Passthrough.Replicate, "serial": string(config.Passthrough.Serial), - "size": int(config.Passthrough.Size), + "size": convert_KibibytesToString(int64(config.Passthrough.SizeInKibibytes)), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Passthrough.Bandwidth) return []interface{}{ @@ -2697,7 +2697,7 @@ func mapFromStruct_QemuVirtIOStorage(config *pxapi.QemuVirtIOStorage, setting st "readonly": config.Disk.ReadOnly, "replicate": config.Disk.Replicate, "serial": string(config.Disk.Serial), - "size": int(config.Disk.Size), + "size": convert_KibibytesToString(int64(config.Disk.SizeInKibibytes)), "storage": string(config.Disk.Storage), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Disk.Bandwidth) @@ -2718,7 +2718,7 @@ func mapFromStruct_QemuVirtIOStorage(config *pxapi.QemuVirtIOStorage, setting st "readonly": config.Passthrough.ReadOnly, "replicate": config.Passthrough.Replicate, "serial": string(config.Passthrough.Serial), - "size": int(config.Passthrough.Size), + "size": convert_KibibytesToString(int64(config.Passthrough.SizeInKibibytes)), } mapFormStruct_QemuDiskBandwidth(mapParams, config.Passthrough.Bandwidth) return []interface{}{ @@ -2808,14 +2808,14 @@ func mapToStruct_QemuIdeStorage(ide *pxapi.QemuIdeStorage, key string, schema ma if ok && len(tmpDisk) == 1 && tmpDisk[0] != nil { disk := tmpDisk[0].(map[string]interface{}) ide.Disk = &pxapi.QemuIdeDisk{ - Backup: disk["backup"].(bool), - Bandwidth: mapToStruct_QemuDiskBandwidth(disk), - Discard: disk["discard"].(bool), - EmulateSSD: disk["emulatessd"].(bool), - Format: pxapi.QemuDiskFormat(disk["format"].(string)), - Replicate: disk["replicate"].(bool), - Size: uint(disk["size"].(int)), - Storage: disk["storage"].(string), + Backup: disk["backup"].(bool), + Bandwidth: mapToStruct_QemuDiskBandwidth(disk), + Discard: disk["discard"].(bool), + EmulateSSD: disk["emulatessd"].(bool), + Format: pxapi.QemuDiskFormat(disk["format"].(string)), + Replicate: disk["replicate"].(bool), + SizeInKibibytes: pxapi.QemuDiskSize(convert_SizeStringToKibibytes_Unsafe(disk["size"].(string))), + Storage: disk["storage"].(string), } if asyncIO, ok := disk["asyncio"].(string); ok { ide.Disk.AsyncIO = pxapi.QemuDiskAsyncIO(asyncIO) @@ -2877,14 +2877,14 @@ func mapToStruct_QemuSataStorage(sata *pxapi.QemuSataStorage, key string, schema if ok && len(tmpDisk) == 1 && tmpDisk[0] != nil { disk := tmpDisk[0].(map[string]interface{}) sata.Disk = &pxapi.QemuSataDisk{ - Backup: disk["backup"].(bool), - Bandwidth: mapToStruct_QemuDiskBandwidth(disk), - Discard: disk["discard"].(bool), - EmulateSSD: disk["emulatessd"].(bool), - Format: pxapi.QemuDiskFormat(disk["format"].(string)), - Replicate: disk["replicate"].(bool), - Size: uint(disk["size"].(int)), - Storage: disk["storage"].(string), + Backup: disk["backup"].(bool), + Bandwidth: mapToStruct_QemuDiskBandwidth(disk), + Discard: disk["discard"].(bool), + EmulateSSD: disk["emulatessd"].(bool), + Format: pxapi.QemuDiskFormat(disk["format"].(string)), + Replicate: disk["replicate"].(bool), + SizeInKibibytes: pxapi.QemuDiskSize(convert_SizeStringToKibibytes_Unsafe(disk["size"].(string))), + Storage: disk["storage"].(string), } if asyncIO, ok := disk["asyncio"].(string); ok { sata.Disk.AsyncIO = pxapi.QemuDiskAsyncIO(asyncIO) @@ -2971,16 +2971,16 @@ func mapToStruct_QemuScsiStorage(scsi *pxapi.QemuScsiStorage, key string, schema if ok && len(tmpDisk) == 1 && tmpDisk[0] != nil { disk := tmpDisk[0].(map[string]interface{}) scsi.Disk = &pxapi.QemuScsiDisk{ - Backup: disk["backup"].(bool), - Bandwidth: mapToStruct_QemuDiskBandwidth(disk), - Discard: disk["discard"].(bool), - EmulateSSD: disk["emulatessd"].(bool), - Format: pxapi.QemuDiskFormat(disk["format"].(string)), - IOThread: disk["iothread"].(bool), - ReadOnly: disk["readonly"].(bool), - Replicate: disk["replicate"].(bool), - Size: uint(disk["size"].(int)), - Storage: disk["storage"].(string), + Backup: disk["backup"].(bool), + Bandwidth: mapToStruct_QemuDiskBandwidth(disk), + Discard: disk["discard"].(bool), + EmulateSSD: disk["emulatessd"].(bool), + Format: pxapi.QemuDiskFormat(disk["format"].(string)), + IOThread: disk["iothread"].(bool), + ReadOnly: disk["readonly"].(bool), + Replicate: disk["replicate"].(bool), + SizeInKibibytes: pxapi.QemuDiskSize(convert_SizeStringToKibibytes_Unsafe(disk["size"].(string))), + Storage: disk["storage"].(string), } if asyncIO, ok := disk["asyncio"].(string); ok { scsi.Disk.AsyncIO = pxapi.QemuDiskAsyncIO(asyncIO) @@ -3135,15 +3135,15 @@ func mapToStruct_VirtIOStorage(virtio *pxapi.QemuVirtIOStorage, key string, sche if ok && len(tmpDisk) == 1 && tmpDisk[0] != nil { disk := tmpDisk[0].(map[string]interface{}) virtio.Disk = &pxapi.QemuVirtIODisk{ - Backup: disk["backup"].(bool), - Bandwidth: mapToStruct_QemuDiskBandwidth(disk), - Discard: disk["discard"].(bool), - Format: pxapi.QemuDiskFormat(disk["format"].(string)), - IOThread: disk["iothread"].(bool), - ReadOnly: disk["readonly"].(bool), - Replicate: disk["replicate"].(bool), - Size: uint(disk["size"].(int)), - Storage: disk["storage"].(string), + Backup: disk["backup"].(bool), + Bandwidth: mapToStruct_QemuDiskBandwidth(disk), + Discard: disk["discard"].(bool), + Format: pxapi.QemuDiskFormat(disk["format"].(string)), + IOThread: disk["iothread"].(bool), + ReadOnly: disk["readonly"].(bool), + Replicate: disk["replicate"].(bool), + SizeInKibibytes: pxapi.QemuDiskSize(convert_SizeStringToKibibytes_Unsafe(disk["size"].(string))), + Storage: disk["storage"].(string), } if asyncIO, ok := disk["asyncio"].(string); ok { virtio.Disk.AsyncIO = pxapi.QemuDiskAsyncIO(asyncIO) @@ -3622,9 +3622,21 @@ func schema_DiskSerial() *schema.Schema { func schema_DiskSize() *schema.Schema { return &schema.Schema{ - Type: schema.TypeInt, - Required: true, - ValidateDiagFunc: uintValidator(), + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: func(i interface{}, k cty.Path) diag.Diagnostics { + v, ok := i.(string) + if !ok { + return diag.Errorf(errorString, k) + } + if !regexp.MustCompile(`^[123456789]\d*[KMGT]?$`).MatchString(v) { + return diag.Errorf("%s must match the following regex ^[123456789]\\d*[KMGT]?$", k) + } + return nil + }, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return convert_SizeStringToKibibytes_Unsafe(old) == convert_SizeStringToKibibytes_Unsafe(new) + }, } } @@ -3668,7 +3680,7 @@ func schema_PassthroughFile() *schema.Schema { func schema_PassthroughSize() *schema.Schema { return &schema.Schema{ - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, } } From 8fa94fd78bbed09d875caad6d29ed4eb56115183 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:22:21 +0000 Subject: [PATCH 2/5] fix: Mebibite set as Kibibyte --- proxmox/converters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmox/converters.go b/proxmox/converters.go index c6c4d599..27666ff0 100644 --- a/proxmox/converters.go +++ b/proxmox/converters.go @@ -19,7 +19,7 @@ func convert_KibibytesToString(kibibytes int64) string { return strconv.FormatInt(kibibytes/gibibyte, 10) + "G" } if kibibytes%mebibyte == 0 { - return strconv.FormatInt(kibibytes/mebibyte, 10) + "K" + return strconv.FormatInt(kibibytes/mebibyte, 10) + "M" } return strconv.FormatInt(kibibytes, 10) + "K" } From d4f83a16299f0a17939fb7c5a967de66314ed4c2 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:13:44 +0000 Subject: [PATCH 3/5] docs: Add disk size KMGT --- docs/resources/vm_qemu.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/resources/vm_qemu.md b/docs/resources/vm_qemu.md index 7844109a..75362f56 100644 --- a/docs/resources/vm_qemu.md +++ b/docs/resources/vm_qemu.md @@ -163,6 +163,7 @@ details. | `rate` | `int` | `0` | Network device rate limit in mbps (megabytes per second) as floating point number. Set to `0` to disable rate limiting. | | `queues` | `int` | `1` | Number of packet queues to be used on the device. Requires `virtio` model to have an effect. | | `link_down` | `bool` | `false` | Whether this interface should be disconnected (like pulling the plug). | + ### Disks Block The `disks` block is used to configure the disk devices. It may be specified once. There are four types of disk `ide`,`sata`,`scsi` and `virtio`. Configuration for these sub types can be found in their respective chapters: @@ -367,7 +368,7 @@ See the [docs about disks](https://pve.proxmox.com/pve-docs/chapter-qm.html#qm_h | `readonly` | `bool` | `false` | `scsi`, `virtio` | Whether the drive should be readonly. | | `replicate` | `bool` | `false` | `all` | Whether the drive should considered for replication jobs. | | `serial` | `str` | | `all` | The serial number of the disk. | -| `size` | `int` | | `all` | **Required** The size of the created disk in Gigabytes. | +| `size` | `string`| | `all` | **Required** The size of the created disk. Accepts `K` for kibibytes, `M` for mebibytes, `G` for gibibytes, `T` for tibibytes. When only a number is provided gibibytes is assumed.| | `storage` | `str` | | `all` | **Required** The name of the storage pool on which to store the disk. | ### Disks.x.Passthrough Block @@ -396,7 +397,7 @@ See the [docs about disks](https://pve.proxmox.com/pve-docs/chapter-qm.html#qm_h | `readonly` | `bool` | `false` | `scsi`, `virtio` | Whether the drive should be readonly. | | `replicate` | `bool` | `false` | `all` | Whether the drive should considered for replication jobs. | | `serial` | `str` | | `all` | The serial number of the disk. | -| `size` | `int` | | `all` | **Computed** Size of the disk. | +| `size` | `string`| | `all` | **Computed** Size of the disk, `K` for kibibytes, `M` for mebibytes, `G` for gibibytes, `T` for tibibytes.| ### EFI Disk Block From 940e8d9c5296720053025d7e488f979c3f4654d3 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:16:04 +0000 Subject: [PATCH 4/5] chore: update `proxmox-api-go` version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9f5a9cba..d5293527 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Telmate/terraform-provider-proxmox/v2 go 1.20 require ( - github.com/Telmate/proxmox-api-go v0.0.0-20240123211857-586199ea9da2 + github.com/Telmate/proxmox-api-go v0.0.0-20240205124300-ede76bab601e github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 diff --git a/go.sum b/go.sum index 53bd2706..94a14573 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/Telmate/proxmox-api-go v0.0.0-20240123211857-586199ea9da2 h1:GDpTqoB3mCIpS+UFnGbj3yVrE/23HtWLotkmYZTBa1M= -github.com/Telmate/proxmox-api-go v0.0.0-20240123211857-586199ea9da2/go.mod h1:xOwyTd8uC2IiYfmjwCVU2fTTVToFCm9yxJzn4cd7rPw= +github.com/Telmate/proxmox-api-go v0.0.0-20240205124300-ede76bab601e h1:ojWFe4idcU9W/0GzBjoZQBaTp0ugRfG4XC7mfWHV0Xk= +github.com/Telmate/proxmox-api-go v0.0.0-20240205124300-ede76bab601e/go.mod h1:xOwyTd8uC2IiYfmjwCVU2fTTVToFCm9yxJzn4cd7rPw= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= From 9843909766c4795bb4fc914fb372cf410da401a4 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:26:32 +0000 Subject: [PATCH 5/5] remove unused func --- proxmox/validators.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/proxmox/validators.go b/proxmox/validators.go index 3e6ed5b4..b51280d3 100644 --- a/proxmox/validators.go +++ b/proxmox/validators.go @@ -93,13 +93,3 @@ func VMStateValidator() schema.SchemaValidateDiagFunc { "stopped", }, false)) } - -func uintValidator() schema.SchemaValidateDiagFunc { - return func(i interface{}, k cty.Path) diag.Diagnostics { - v, ok := i.(int) - if !ok || v < 0 { - return diag.Errorf(errorUint, k) - } - return nil - } -}