Skip to content

Allow modifying disks' flash priority #54

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

Merged
merged 2 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions docs/resources/disk.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ resource "hypercore_disk" "disk_cloned" {
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "VIRTIO_DISK"
size = 47.2
# flash_priority will be fetched from the HC3 API and it will
# be set to that unless specifically specified otherwise
}

resource "hypercore_disk" "disk_newly_created" {
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "IDE_DISK"
size = 3.0
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "IDE_DISK"
size = 3.0
flash_priority = 5 # defaults to 4 if not provided
}

output "diskvm_uuid" {
Expand All @@ -55,10 +58,11 @@ import {

### Optional

- `iso_uuid` (String) ISO UUID we want to attach to the disk, only available with disk type IDE_CDROM.
- `flash_priority` (Number) SSD tiering priority factor for block placement. If not provided, it will default to `4`, unless imported, in which case the disk's current flash priority will be taken into account and can then be modified. This can be any **positive** value between (including) `0` and `11`.
- `iso_uuid` (String) ISO UUID we want to attach to the disk, only available with disk type `IDE_CDROM`.
- `size` (Number) Disk size in `GB`. Must be larger than the current size of the disk if specified.
- `source_virtual_disk_id` (String) UUID of the virtual disk to use to clone and attach to the VM.
- `type` (String) Disk type. Can be: `IDE_DISK`, `SCSI_DISK`, `VIRTIO_DISK`, `IDE_FLOPPY`, `NVRAM`, `VTPM`
- `type` (String) Disk type. Can be: `IDE_DISK`, `IDE_CDROM`, `SCSI_DISK`, `VIRTIO_DISK`, `IDE_FLOPPY`, `NVRAM`, `VTPM`

### Read-Only

Expand Down
9 changes: 6 additions & 3 deletions examples/resources/hypercore_disk/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ resource "hypercore_disk" "disk_cloned" {
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "VIRTIO_DISK"
size = 47.2
# flash_priority will be fetched from the HC3 API and it will
# be set to that unless specifically specified otherwise
}

resource "hypercore_disk" "disk_newly_created" {
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "IDE_DISK"
size = 3.0
vm_uuid = data.hypercore_vms.diskvm.vms.0.uuid
type = "IDE_DISK"
size = 3.0
flash_priority = 5 # defaults to 4 if not provided
}

output "diskvm_uuid" {
Expand Down
57 changes: 45 additions & 12 deletions internal/provider/hypercore_disk_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand All @@ -36,6 +38,7 @@ type HypercoreDiskResourceModel struct {
Id types.String `tfsdk:"id"`
VmUUID types.String `tfsdk:"vm_uuid"`
Slot types.Int64 `tfsdk:"slot"`
FlashPriority types.Int64 `tfsdk:"flash_priority"`
Type types.String `tfsdk:"type"`
Size types.Float64 `tfsdk:"size"`
SourceVirtualDiskID types.String `tfsdk:"source_virtual_disk_id"`
Expand Down Expand Up @@ -70,8 +73,17 @@ func (r *HypercoreDiskResource) Schema(ctx context.Context, req resource.SchemaR
MarkdownDescription: "Disk slot number. Will not do anything if the disk already exists, since HC3 doesn't change disk slots to existing disks.",
Computed: true,
},
"flash_priority": schema.Int64Attribute{
MarkdownDescription: "SSD tiering priority factor for block placement. If not provided, it will default to `4`, unless imported, in which case the disk's current flash priority will be taken into account and can then be modified. This can be any **positive** value between (including) `0` and `11`.",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(4),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
},
"type": schema.StringAttribute{
MarkdownDescription: "Disk type. Can be: `IDE_DISK`, `SCSI_DISK`, `VIRTIO_DISK`, `IDE_FLOPPY`, `NVRAM`, `VTPM`",
MarkdownDescription: "Disk type. Can be: `IDE_DISK`, `IDE_CDROM`, `SCSI_DISK`, `VIRTIO_DISK`, `IDE_FLOPPY`, `NVRAM`, `VTPM`",
Optional: true,
},
"size": schema.Float64Attribute{
Expand All @@ -83,7 +95,7 @@ func (r *HypercoreDiskResource) Schema(ctx context.Context, req resource.SchemaR
Optional: true,
},
"iso_uuid": schema.StringAttribute{
MarkdownDescription: "ISO UUID we want to attach to the disk, only available with disk type IDE_CDROM.",
MarkdownDescription: "ISO UUID we want to attach to the disk, only available with disk type `IDE_CDROM`.",
Optional: true,
},
},
Expand Down Expand Up @@ -151,11 +163,17 @@ func (r *HypercoreDiskResource) Create(ctx context.Context, req resource.CreateR
resp.Diagnostics.AddError(diagISOAttach.Summary(), diagISOAttach.Detail())
return
}
diagFlashPriority := utils.ValidateDiskFlashPriority(data.FlashPriority.ValueInt64())
if diagFlashPriority != nil {
resp.Diagnostics.AddError(diagFlashPriority.Summary(), diagFlashPriority.Detail())
return
}

createPayload := map[string]any{
"virDomainUUID": data.VmUUID.ValueString(),
"type": data.Type.ValueString(),
"capacity": data.Size.ValueFloat64() * 1000 * 1000 * 1000, // GB to B
"virDomainUUID": data.VmUUID.ValueString(),
"type": data.Type.ValueString(),
"capacity": data.Size.ValueFloat64() * 1000 * 1000 * 1000, // GB to B
"tieringPriorityFactor": utils.FROM_HUMAN_PRIORITY_FACTOR[data.FlashPriority.ValueInt64()],
}
if isAttachingISO {
createPayload["path"] = (*iso)["path"]
Expand All @@ -177,9 +195,10 @@ func (r *HypercoreDiskResource) Create(ctx context.Context, req resource.CreateR
"readOnly": false,
},
"template": map[string]any{
"virDomainUUID": data.VmUUID.ValueString(),
"type": data.Type.ValueString(),
"capacity": originalVDSizeBytes,
"virDomainUUID": data.VmUUID.ValueString(),
"type": data.Type.ValueString(),
"capacity": originalVDSizeBytes,
"tieringPriorityFactor": utils.FROM_HUMAN_PRIORITY_FACTOR[data.FlashPriority.ValueInt64()],
},
}

Expand Down Expand Up @@ -258,6 +277,10 @@ func (r *HypercoreDiskResource) Read(ctx context.Context, req resource.ReadReque
data.Slot = types.Int64Value(utils.AnyToInteger64(disk["slot"]))
data.Size = types.Float64Value(utils.AnyToFloat64(disk["capacity"]) / 1000 / 1000 / 1000)

hc3PriorityFactor := utils.AnyToInteger64(disk["tieringPriorityFactor"])
tflog.Info(ctx, fmt.Sprintf("TTRT HypercoreDiskResource: hc3PriorityFactor = %v\n", hc3PriorityFactor))
data.FlashPriority = types.Int64Value(utils.TO_HUMAN_PRIORITY_FACTOR[hc3PriorityFactor])

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Expand Down Expand Up @@ -312,7 +335,7 @@ func (r *HypercoreDiskResource) Update(ctx context.Context, req resource.UpdateR
}

// Validate the type
diagDiskType := utils.ValidateDiskType(data.Type.ValueString(), data.IsoUUID.String())
diagDiskType := utils.ValidateDiskType(data.Type.ValueString(), data.IsoUUID.ValueString())
if diagDiskType != nil {
resp.Diagnostics.AddError(diagDiskType.Summary(), diagDiskType.Detail())
return
Expand All @@ -322,13 +345,19 @@ func (r *HypercoreDiskResource) Update(ctx context.Context, req resource.UpdateR
resp.Diagnostics.AddError(diagISOAttach.Summary(), diagISOAttach.Detail())
return
}
diagFlashPriority := utils.ValidateDiskFlashPriority(data.FlashPriority.ValueInt64())
if diagFlashPriority != nil {
resp.Diagnostics.AddError(diagFlashPriority.Summary(), diagFlashPriority.Detail())
return
}

isDetachingISO := oldHc3Disk["path"] != "" && data.IsoUUID.ValueString() == "" && data.Type.ValueString() == "IDE_CDROM"

updatePayload := map[string]any{
"virDomainUUID": vmUUID,
"type": data.Type.ValueString(),
"capacity": data.Size.ValueFloat64() * 1000 * 1000 * 1000, // GB to B
"virDomainUUID": vmUUID,
"type": data.Type.ValueString(),
"capacity": data.Size.ValueFloat64() * 1000 * 1000 * 1000, // GB to B
"tieringPriorityFactor": utils.FROM_HUMAN_PRIORITY_FACTOR[data.FlashPriority.ValueInt64()],
}
if isAttachingISO {
updatePayload["path"] = (*iso)["path"]
Expand Down Expand Up @@ -410,11 +439,14 @@ func (r *HypercoreDiskResource) ImportState(ctx context.Context, req resource.Im

var diskUUID string
var size float64
var flashPriority int64
for _, disk := range hc3Disks {
if utils.AnyToInteger64(disk["slot"]) == slot &&
utils.AnyToString(disk["type"]) == diskType {
diskUUID = utils.AnyToString(disk["uuid"])
size = utils.AnyToFloat64(disk["capacity"]) / 1000 / 1000 / 1000 // hc3 has B, so convert to GB
flashPriority = utils.AnyToInteger64(disk["tieringPriorityFactor"])
tflog.Debug(ctx, fmt.Sprintf("TTRT HUMAN FLASH PRIORITY = %v", flashPriority))
break
}
}
Expand All @@ -428,5 +460,6 @@ func (r *HypercoreDiskResource) ImportState(ctx context.Context, req resource.Im
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("vm_uuid"), vmUUID)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("type"), diskType)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("slot"), slot)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("flash_priority"), flashPriority)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("size"), size)...)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestAccHypercoreDiskResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_disk.test", "size", "3"),
resource.TestCheckResourceAttr("hypercore_disk.test", "type", "IDE_DISK"),
resource.TestCheckResourceAttr("hypercore_disk.test", "flash_priority", "4"), // should default to 4 if not specified in resource config
),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestAccHypercoreVirtualDiskResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_disk.attach_vd_test", "size", "3.4"),
resource.TestCheckResourceAttr("hypercore_disk.attach_vd_test", "type", "VIRTIO_DISK"),
resource.TestCheckResourceAttr("hypercore_disk.attach_vd_test", "flash_priority", "4"),
),
},
},
Expand Down
43 changes: 42 additions & 1 deletion internal/utils/vm_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,36 @@ var ALLOWED_DISK_TYPES = map[string]bool{
"IDE_CDROM": true,
}

var FROM_HUMAN_PRIORITY_FACTOR = map[int64]int64{
0: 0,
1: 1,
2: 2,
3: 4,
4: 8,
5: 16,
6: 32,
7: 64,
8: 128,
9: 256,
10: 1024,
11: 10240,
}

var TO_HUMAN_PRIORITY_FACTOR = map[int64]int64{
0: 0,
1: 1,
2: 2,
4: 3,
8: 4,
16: 5,
32: 6,
64: 7,
128: 8,
256: 9,
1024: 10,
10240: 11,
}

type VMDisk struct {
Label string
UUID string // known after creation
Expand Down Expand Up @@ -311,11 +341,22 @@ func CreateDisk(
return diskUUID, *disk
}

func ValidateDiskFlashPriority(diskFlashPriority int64) diag.Diagnostic {
if diskFlashPriority < 0 || diskFlashPriority > 11 {
return diag.NewErrorDiagnostic(
"Invalid disk flash priority",
fmt.Sprintf("Disk flash priority '%v' is invalid. Flash priority must be a positive number between (including) 0 and 11.", diskFlashPriority),
)
}

return nil
}

func ValidateDiskType(diskType string, isoUUID string) diag.Diagnostic {
if !ALLOWED_DISK_TYPES[diskType] {
return diag.NewErrorDiagnostic(
"Invalid disk type",
fmt.Sprintf("Disk type '%s' not allowed. Allowed types are: IDE_DISK, SCSI_DISK, VIRTIO_DISK, IDE_FLOPPY, NVRAM, VTPM", diskType),
fmt.Sprintf("Disk type '%s' not allowed. Allowed types are: IDE_DISK, IDE_CDROM, SCSI_DISK, VIRTIO_DISK, IDE_FLOPPY, NVRAM, VTPM", diskType),
)
}
if isoUUID != "" && diskType != "IDE_CDROM" {
Expand Down
37 changes: 21 additions & 16 deletions local/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,31 @@ terraform {
}

locals {
vm_name = "testtf-ana-tags-2"
src_vm_name = "ana"
vm_name = "testtf-ana-tags"
}

provider "hypercore" {}

data "hypercore_vms" "src_empty" {
name = local.src_vm_name
data "hypercore_vms" "testtf-ana-tags" {
name = "testtf-ana-tags"
}

resource "hypercore_vm" "vm_on" {
tags = ["ana-tftag2"]
name = local.vm_name
description = "VM created from scratch"
vcpu = 1
memory = 1234 # MiB

clone = {
source_vm_uuid = data.hypercore_vms.src_empty.vms.0.uuid
meta_data = ""
user_data = ""
}
resource "hypercore_disk" "disk_newly_created" {
vm_uuid = data.hypercore_vms.testtf-ana-tags.vms.0.uuid
type = "IDE_DISK"
size = 3.0
flash_priority = 3
}

resource "hypercore_disk" "disk_cloned" {
vm_uuid = data.hypercore_vms.testtf-ana-tags.vms.0.uuid
type = "IDE_DISK"
size = 3.0

depends_on = [hypercore_disk.disk_newly_created]
}

import {
to = hypercore_disk.disk_cloned
id = format("%s:%s:%d", data.hypercore_vms.testtf-ana-tags.vms.0.uuid, "IDE_DISK", 0)
}
Loading