diff --git a/docs/resources/disk.md b/docs/resources/disk.md index fbd406a..41529e2 100644 --- a/docs/resources/disk.md +++ b/docs/resources/disk.md @@ -55,6 +55,7 @@ import { ### Optional +- `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` diff --git a/docs/resources/iso.md b/docs/resources/iso.md new file mode 100644 index 0000000..7746c41 --- /dev/null +++ b/docs/resources/iso.md @@ -0,0 +1,70 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "hypercore_iso Resource - hypercore" +subcategory: "" +description: |- + Hypercore ISO resource to manage ISO images. To use this resource, it's recommended to set the environment variable TF_CLI_ARGS_apply="-parallelism=1" or pass the -parallelism parameter to the terraform apply. +--- + +# hypercore_iso (Resource) + +Hypercore ISO resource to manage ISO images.

To use this resource, it's recommended to set the environment variable `TF_CLI_ARGS_apply="-parallelism=1"` or pass the `-parallelism` parameter to the `terraform apply`. + +## Example Usage + +```terraform +locals { + vm_name = "myvm" +} + +data "hypercore_vm" "isovm" { + name = local.vm_name +} + +// Upload ISO from local machine +resource "hypercore_iso" "iso_upload_local" { + name = "testiso-local.iso" + source_url = "file:////home/bla/Downloads/mytestiso.iso" +} + +// Upload ISO from remote machine/storage +resource "hypercore_iso" "iso_upload_from_url" { + name = "testiso-remote.iso" + source_url = "https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/aarch64/alpine-virt-3.21.3-aarch64.iso" +} + + +output "uploaded_iso_LOCAL" { + value = hypercore_iso.iso_upload_local +} + +output "uploaded_iso_EXTERNAL" { + value = hypercore_iso.iso_upload_from_url +} + +// We can then use this ISO for IDE_CDROM disk +resource "hypercore_disk" "iso_attach" { + vm_uuid = local.vm_name + type = "IDE_CDROM" + iso_uuid = hypercore_iso.iso_upload_local.id +} + +output "iso_attach" { + value = hypercore_disk.iso_attach +} +``` + + +## Schema + +### Required + +- `name` (String) Desired name of the ISO to upload. ISO name must end with '.iso'. + +### Optional + +- `source_url` (String) Source URL from where to fetch that disk from. URL can start with: `http://`, `https://`, `file:///` + +### Read-Only + +- `id` (String) ISO identifier diff --git a/examples/resources/hypercore_iso/resource.tf b/examples/resources/hypercore_iso/resource.tf index 76635bf..0bfa74c 100644 --- a/examples/resources/hypercore_iso/resource.tf +++ b/examples/resources/hypercore_iso/resource.tf @@ -6,11 +6,13 @@ data "hypercore_vm" "isovm" { name = local.vm_name } +// Upload ISO from local machine resource "hypercore_iso" "iso_upload_local" { name = "testiso-local.iso" source_url = "file:////home/bla/Downloads/mytestiso.iso" } +// Upload ISO from remote machine/storage resource "hypercore_iso" "iso_upload_from_url" { name = "testiso-remote.iso" source_url = "https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/aarch64/alpine-virt-3.21.3-aarch64.iso" @@ -25,3 +27,13 @@ output "uploaded_iso_EXTERNAL" { value = hypercore_iso.iso_upload_from_url } +// We can then use this ISO for IDE_CDROM disk +resource "hypercore_disk" "iso_attach" { + vm_uuid = local.vm_name + type = "IDE_CDROM" + iso_uuid = hypercore_iso.iso_upload_local.id +} + +output "iso_attach" { + value = hypercore_disk.iso_attach +} diff --git a/internal/provider/hypercore_disk_resource.go b/internal/provider/hypercore_disk_resource.go index a0ebc14..59a5462 100644 --- a/internal/provider/hypercore_disk_resource.go +++ b/internal/provider/hypercore_disk_resource.go @@ -39,6 +39,7 @@ type HypercoreDiskResourceModel struct { Type types.String `tfsdk:"type"` Size types.Float64 `tfsdk:"size"` SourceVirtualDiskID types.String `tfsdk:"source_virtual_disk_id"` + IsoUUID types.String `tfsdk:"iso_uuid"` } func (r *HypercoreDiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -79,6 +80,10 @@ func (r *HypercoreDiskResource) Schema(ctx context.Context, req resource.SchemaR MarkdownDescription: "UUID of the virtual disk to use to clone and attach to the VM.", Optional: true, }, + "iso_uuid": schema.StringAttribute{ + MarkdownDescription: "ISO UUID we want to attach to the disk, only available with disk type IDE_CDROM.", + Optional: true, + }, }, } } @@ -128,20 +133,29 @@ func (r *HypercoreDiskResource) Create(ctx context.Context, req resource.CreateR tflog.Info(ctx, fmt.Sprintf("TTRT Create: vm_uuid=%s, type=%s, slot=%d, size=%d", data.VmUUID.ValueString(), data.Type.ValueString(), data.Slot.ValueInt64(), data.Slot.ValueInt64())) - diagDiskType := utils.ValidateDiskType(data.Type.ValueString()) + var diskUUID string + var disk map[string]any + isAttachingISO := data.IsoUUID.ValueString() != "" + + diagDiskType := utils.ValidateDiskType(data.Type.ValueString(), data.IsoUUID.ValueString()) if diagDiskType != nil { resp.Diagnostics.AddError(diagDiskType.Summary(), diagDiskType.Detail()) return } + diagISOAttach, iso := utils.ValidateISOAttach(*r.client, data.IsoUUID.ValueString(), isAttachingISO) + if diagISOAttach != nil { + resp.Diagnostics.AddError(diagISOAttach.Summary(), diagISOAttach.Detail()) + return + } createPayload := map[string]any{ "virDomainUUID": data.VmUUID.ValueString(), "type": data.Type.ValueString(), "capacity": data.Size.ValueFloat64() * 1000 * 1000 * 1000, // GB to B } - - var diskUUID string - var disk map[string]any + if isAttachingISO { + createPayload["path"] = (*iso)["path"] + } sourceVirtualDiskID := data.SourceVirtualDiskID.ValueString() if sourceVirtualDiskID != "" { @@ -164,6 +178,7 @@ func (r *HypercoreDiskResource) Create(ctx context.Context, req resource.CreateR "capacity": originalVDSizeBytes, }, } + diskUUID, disk = utils.AttachVirtualDisk( *r.client, attachPayload, @@ -257,6 +272,8 @@ func (r *HypercoreDiskResource) Update(ctx context.Context, req resource.UpdateR restClient := *r.client diskUUID := data.Id.ValueString() vmUUID := data.VmUUID.ValueString() + isAttachingISO := data.IsoUUID.ValueString() != "" + tflog.Debug( ctx, fmt.Sprintf( "TTRT HypercoreDiskResource Update vm_uuid=%s disk_uuid=%s REQUESTED slot=%d type=%s size=%v\n", @@ -287,17 +304,29 @@ func (r *HypercoreDiskResource) Update(ctx context.Context, req resource.UpdateR } // Validate the type - diagDiskType := utils.ValidateDiskType(data.Type.ValueString()) + diagDiskType := utils.ValidateDiskType(data.Type.ValueString(), data.IsoUUID.String()) if diagDiskType != nil { resp.Diagnostics.AddError(diagDiskType.Summary(), diagDiskType.Detail()) return } + diagISOAttach, iso := utils.ValidateISOAttach(*r.client, data.IsoUUID.ValueString(), isAttachingISO) + if diagISOAttach != nil { + resp.Diagnostics.AddError(diagISOAttach.Summary(), diagISOAttach.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 } + if isAttachingISO { + updatePayload["path"] = (*iso)["path"] + } else if isDetachingISO { + updatePayload["path"] = "" + } diag := utils.UpdateDisk(restClient, diskUUID, updatePayload, ctx) if diag != nil { resp.Diagnostics.AddWarning(diag.Summary(), diag.Detail()) diff --git a/internal/utils/vm_disk.go b/internal/utils/vm_disk.go index 1b0f695..f37d320 100644 --- a/internal/utils/vm_disk.go +++ b/internal/utils/vm_disk.go @@ -311,16 +311,37 @@ func CreateDisk( return diskUUID, *disk } -func ValidateDiskType(diskType string) diag.Diagnostic { +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), ) } + if isoUUID != "" && diskType != "IDE_CDROM" { + return diag.NewErrorDiagnostic( + "Invalid disk type", + fmt.Sprintf("Disk type '%s' is not compatible with ISO, for ISO attach action type of IDE_CDROM is needed.", diskType), + ) + } return nil } +func ValidateISOAttach(restClient RestClient, isoUUID string, isAttachingISO bool) (diag.Diagnostic, *map[string]any) { + if isAttachingISO { + iso := GetISOByUUID(restClient, isoUUID) + if iso == nil { + return diag.NewErrorDiagnostic( + "Invalid ISO UUID", + fmt.Sprintf("ISO with UUID '%s' not found.", isoUUID), + ), + nil + } + return nil, iso + } + return nil, nil +} + func ValidateDiskSize(diskUUID string, oldSize float64, newSize float64) diag.Diagnostic { if newSize < oldSize { return diag.NewErrorDiagnostic(