From ce54a351a3499480f6f6f71dc330b317d7297845 Mon Sep 17 00:00:00 2001 From: Janos Bonic <86970079+janosdebugs@users.noreply.github.com> Date: Thu, 9 Jun 2022 10:16:51 +0200 Subject: [PATCH] Added ovirt_disk_resize_all resource --- docs/resources/vm_disks_resize.md | 30 ++++ internal/ovirt/provider.go | 1 + .../ovirt/resource_ovirt_vm_disks_resize.go | 138 ++++++++++++++++++ .../resource_ovirt_vm_disks_resize_test.go | 128 ++++++++++++++++ 4 files changed, 297 insertions(+) create mode 100644 docs/resources/vm_disks_resize.md create mode 100644 internal/ovirt/resource_ovirt_vm_disks_resize.go create mode 100644 internal/ovirt/resource_ovirt_vm_disks_resize_test.go diff --git a/docs/resources/vm_disks_resize.md b/docs/resources/vm_disks_resize.md new file mode 100644 index 00000000..0481ba92 --- /dev/null +++ b/docs/resources/vm_disks_resize.md @@ -0,0 +1,30 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "ovirt_vm_disks_resize Resource - terraform-provider-ovirt" +subcategory: "" +description: |- + The ovirtvmdisks_resize resource resizes all disks in an oVirt VM to the specified size. + ~> Only use this resource with disks created from templates. Otherwise, two terraform resources will handle the same disk resource. +--- + +# ovirt_vm_disks_resize (Resource) + +The ovirt_vm_disks_resize resource resizes all disks in an oVirt VM to the specified size. + +~> Only use this resource with disks created from templates. Otherwise, two terraform resources will handle the same disk resource. + + + + +## Schema + +### Required + +- `size` (Number) Disk size in bytes to set all disks to. +- `vm_id` (String) Resize all disks in this VM to the specified size. + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/internal/ovirt/provider.go b/internal/ovirt/provider.go index b0988bac..ff5f2157 100644 --- a/internal/ovirt/provider.go +++ b/internal/ovirt/provider.go @@ -124,6 +124,7 @@ func (p *provider) getProvider() *schema.Provider { "ovirt_vm_optimize_cpu_settings": p.vmOptimizeCPUSettingsResource(), "ovirt_disk": p.diskResource(), "ovirt_disk_resize": p.diskResizeResource(), + "ovirt_vm_disks_resize": p.vmDisksResizeResource(), "ovirt_disk_from_image": p.diskFromImageResource(), "ovirt_disk_attachment": p.diskAttachmentResource(), "ovirt_disk_attachments": p.diskAttachmentsResource(), diff --git a/internal/ovirt/resource_ovirt_vm_disks_resize.go b/internal/ovirt/resource_ovirt_vm_disks_resize.go new file mode 100644 index 00000000..1fd2a01a --- /dev/null +++ b/internal/ovirt/resource_ovirt_vm_disks_resize.go @@ -0,0 +1,138 @@ +package ovirt + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + ovirtclient "github.com/ovirt/go-ovirt-client" +) + +var vmDisksResizeSchema = map[string]*schema.Schema{ + "vm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Resize all disks in this VM to the specified size.", + ValidateDiagFunc: validateUUID, + }, + "size": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "Disk size in bytes to set all disks to.", + ValidateDiagFunc: validateDiskSize, + }, +} + +func (p *provider) vmDisksResizeResource() *schema.Resource { + return &schema.Resource{ + CreateContext: p.vmDisksResizeCreate, + ReadContext: p.vmDisksResizeRead, + DeleteContext: p.vmDisksResizeDelete, + Schema: vmDisksResizeSchema, + Description: `The ovirt_vm_disks_resize resource resizes all disks in an oVirt VM to the specified size. + +~> Only use this resource with disks created from templates. Otherwise, two terraform resources will handle the same disk resource. + `, + } +} + +func (p *provider) vmDisksResizeCreate( + ctx context.Context, + data *schema.ResourceData, + _ interface{}, +) diag.Diagnostics { + client := p.client.WithContext(ctx) + return resizeAllDisks(client, data) +} + +func (p *provider) vmDisksResizeRead(ctx context.Context, data *schema.ResourceData, _ interface{}) diag.Diagnostics { + client := p.client.WithContext(ctx) + + desiredSize := uint64(data.Get("size").(int)) + size := desiredSize + + vmID := ovirtclient.VMID(data.Get("vm_id").(string)) + + diskAttachments, err := client.ListDiskAttachments(vmID) + if err != nil { + return errorToDiags(fmt.Sprintf("list disk attachments of VM %s", vmID), err) + } + for _, diskAttachment := range diskAttachments { + disk, err := diskAttachment.Disk() + if err != nil { + return errorToDiags(fmt.Sprintf("get disk %s", diskAttachment.DiskID()), err) + } + if disk.ProvisionedSize() != desiredSize { + // Set the reported size to the size differing so the resource can be refreshed and the disks resized. + size = disk.ProvisionedSize() + } + } + + data.SetId(string(vmID)) + diags := diag.Diagnostics{} + diags = setResourceField(data, "size", size, diags) + + return diags +} + +func (p *provider) vmDisksResizeDelete(_ context.Context, data *schema.ResourceData, _ interface{}) diag.Diagnostics { + data.SetId("") + return nil +} + +func resizeAllDisks(client ovirtclient.Client, data *schema.ResourceData) diag.Diagnostics { + vmID := ovirtclient.VMID(data.Get("vm_id").(string)) + desiredSize := uint64(data.Get("size").(int)) + + diskAttachments, err := client.ListDiskAttachments(vmID) + if err != nil { + return errorToDiags(fmt.Sprintf("list disk attachments of VM %s", vmID), err) + } + var diags diag.Diagnostics + for _, diskAttachment := range diskAttachments { + disk, err := diskAttachment.Disk() + if err != nil { + return errorToDiags(fmt.Sprintf("get disk %s", diskAttachment.DiskID()), err) + } + if disk.ProvisionedSize() == desiredSize { + continue + } + params := ovirtclient.UpdateDiskParams() + if _, err := params.WithProvisionedSize(desiredSize); err != nil { + diags = append( + diags, + diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("Failed to set parameters for updating disk %s size.", disk.ID()), + Detail: err.Error(), + }, + ) + continue + } + updateFailedDiag := diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("Failed to update disk %s size.", disk.ID()), + } + diskUpdate, err := client.StartUpdateDisk(disk.ID(), params) + if err != nil { + updateFailedDiag.Detail = err.Error() + diags = append(diags, updateFailedDiag) + continue + } + _, err = diskUpdate.Wait() + if err != nil { + updateFailedDiag.Detail = err.Error() + diags = append(diags, updateFailedDiag) + continue + } + } + + data.SetId(string(vmID)) + if !diags.HasError() { + diags = setResourceField(data, "size", desiredSize, diags) + } + return diags +} diff --git a/internal/ovirt/resource_ovirt_vm_disks_resize_test.go b/internal/ovirt/resource_ovirt_vm_disks_resize_test.go new file mode 100644 index 00000000..581c75f0 --- /dev/null +++ b/internal/ovirt/resource_ovirt_vm_disks_resize_test.go @@ -0,0 +1,128 @@ +package ovirt + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + ovirtclient "github.com/ovirt/go-ovirt-client" +) + +func TestVMDisksResize(t *testing.T) { + t.Parallel() + + testData := []struct { + name string + diskCount int + startingDiskSize uint64 + desiredDiskSize uint64 + }{ + { + "empty", + 0, + uint64(1024 * 1024), + 2 * uint64(1024*1024), + }, + { + "single", + 1, + uint64(1024 * 1024), + 2 * uint64(1024*1024), + }, + { + "double", + 2, + uint64(1024 * 1024), + 2 * uint64(1024*1024), + }, + } + + for _, testCase := range testData { + t.Run( + testCase.name, func(t *testing.T) { + p := newProvider(newTestLogger(t)) + + helper := p.getTestHelper() + client := helper.GetClient().WithContext(context.Background()) + vm, err := client.CreateVM( + helper.GetClusterID(), + helper.GetBlankTemplateID(), + helper.GenerateTestResourceName(t), + nil, + ) + if err != nil { + t.Fatal(err) + } + + startingDiskSize := testCase.startingDiskSize + desiredDiskSize := testCase.desiredDiskSize + + disks := make([]ovirtclient.Disk, testCase.diskCount) + + for i := 0; i < testCase.diskCount; i++ { + disk, err := client.CreateDisk( + helper.GetStorageDomainID(), + ovirtclient.ImageFormatRaw, + startingDiskSize, + nil, + ) + if err != nil { + t.Fatal(err) + } + + _, err = client.CreateDiskAttachment(vm.ID(), disk.ID(), ovirtclient.DiskInterfaceVirtIO, nil) + if err != nil { + t.Fatal(err) + } + + disks[i] = disk + } + + config := fmt.Sprintf( + ` + provider "ovirt" { + mock = true + } + resource "ovirt_vm_disks_resize" "resized" { + vm_id = "%s" + size = %d + }`, + vm.ID(), + desiredDiskSize, + ) + + resource.UnitTest( + t, resource.TestCase{ + ProviderFactories: p.getProviderFactories(), + Steps: []resource.TestStep{ + { + Config: config, + Check: func(state *terraform.State) error { + for _, disk := range disks { + disk, err := client.GetDisk(disk.ID()) + if err != nil { + return err + } + if disk.ProvisionedSize() != desiredDiskSize { + return fmt.Errorf( + "incorrect disk size after apply: %d", + disk.ProvisionedSize(), + ) + } + } + return nil + }, + }, + { + Config: config, + Destroy: true, + }, + }, + }, + ) + }, + ) + } +}