opentofu/builtin/providers/vsphere/resource_vsphere_virtual_disk.go
dkalleg 81673a27e7 Graceful read miss (#7220)
For both the file and virtual_disk resource, Stat is used during read,
but if Stat returns an error, read() will return that error. In doing
so, if a resource is deleted manually, the TF user would then not be
able to destroy the resource because the read would block the Delete()
call. With this patch, read() will only return an error if that error
is NOT a DatastoreNoSuchFileError.
2016-06-23 07:47:08 +01:00

265 lines
6.2 KiB
Go

package vsphere
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/net/context"
)
type virtualDisk struct {
size int
vmdkPath string
initType string
adapterType string
datacenter string
datastore string
}
// Define VirtualDisk args
func resourceVSphereVirtualDisk() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereVirtualDiskCreate,
Read: resourceVSphereVirtualDiskRead,
Delete: resourceVSphereVirtualDiskDelete,
Schema: map[string]*schema.Schema{
// Size in GB
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true, //TODO Can this be optional (resize)?
},
"vmdk_path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true, //TODO Can this be optional (move)?
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "eagerZeroedThick",
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "thin" && value != "eagerZeroedThick" {
errors = append(errors, fmt.Errorf(
"only 'thin' and 'eagerZeroedThick' are supported values for 'type'"))
}
return
},
},
"adapter_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "ide",
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "ide" && value != "busLogic" && value != "lsiLogic" {
errors = append(errors, fmt.Errorf(
"only 'ide', 'busLogic', and 'lsiLogic' are supported values for 'adapter_type'"))
}
return
},
},
"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"datastore": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceVSphereVirtualDiskCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Creating Virtual Disk")
client := meta.(*govmomi.Client)
vDisk := virtualDisk{
size: d.Get("size").(int),
}
if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}
if v, ok := d.GetOk("type"); ok {
vDisk.initType = v.(string)
}
if v, ok := d.GetOk("adapter_type"); ok {
vDisk.adapterType = v.(string)
}
if v, ok := d.GetOk("datacenter"); ok {
vDisk.datacenter = v.(string)
}
if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}
diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath)
err := createHardDisk(client, vDisk.size, diskPath, vDisk.initType, vDisk.adapterType, vDisk.datacenter)
if err != nil {
return err
}
d.SetId(diskPath)
log.Printf("[DEBUG] Virtual Disk id: %v", diskPath)
return resourceVSphereVirtualDiskRead(d, meta)
}
func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Reading virtual disk.")
client := meta.(*govmomi.Client)
vDisk := virtualDisk{
size: d.Get("size").(int),
}
if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}
if v, ok := d.GetOk("type"); ok {
vDisk.initType = v.(string)
}
if v, ok := d.GetOk("adapter_type"); ok {
vDisk.adapterType = v.(string)
}
if v, ok := d.GetOk("datacenter"); ok {
vDisk.datacenter = v.(string)
}
if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
ds, err := finder.Datastore(context.TODO(), d.Get("datastore").(string))
if err != nil {
return err
}
fileInfo, err := ds.Stat(context.TODO(), vDisk.vmdkPath)
if err != nil {
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - stat failed on: %v", vDisk.vmdkPath)
d.SetId("")
_, ok := err.(object.DatastoreNoSuchFileError)
if !ok {
return err
}
return nil
}
fileInfo = fileInfo.GetFileInfo()
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo)
size := fileInfo.(*types.FileInfo).FileSize / 1024 / 1024 / 1024
d.SetId(vDisk.vmdkPath)
d.Set("size", size)
d.Set("vmdk_path", vDisk.vmdkPath)
d.Set("datacenter", d.Get("datacenter"))
d.Set("datastore", d.Get("datastore"))
// Todo collect and write type info
return nil
}
func resourceVSphereVirtualDiskDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)
vDisk := virtualDisk{}
if v, ok := d.GetOk("vmdk_path"); ok {
vDisk.vmdkPath = v.(string)
}
if v, ok := d.GetOk("datastore"); ok {
vDisk.datastore = v.(string)
}
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}
diskPath := fmt.Sprintf("[%v] %v", vDisk.datastore, vDisk.vmdkPath)
virtualDiskManager := object.NewVirtualDiskManager(client.Client)
task, err := virtualDiskManager.DeleteVirtualDisk(context.TODO(), diskPath, dc)
if err != nil {
return err
}
_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[INFO] Failed to delete disk: %v", err)
return err
}
log.Printf("[INFO] Deleted disk: %v", diskPath)
d.SetId("")
return nil
}
// createHardDisk creates a new Hard Disk.
func createHardDisk(client *govmomi.Client, size int, diskPath string, diskType string, adapterType string, dc string) error {
virtualDiskManager := object.NewVirtualDiskManager(client.Client)
spec := &types.FileBackedVirtualDiskSpec{
VirtualDiskSpec: types.VirtualDiskSpec{
AdapterType: adapterType,
DiskType: diskType,
},
CapacityKb: int64(1024 * 1024 * size),
}
datacenter, err := getDatacenter(client, dc)
if err != nil {
return err
}
log.Printf("[DEBUG] Disk spec: %v", spec)
task, err := virtualDiskManager.CreateVirtualDisk(context.TODO(), diskPath, datacenter, spec)
if err != nil {
return err
}
_, err = task.WaitForResult(context.TODO(), nil)
if err != nil {
log.Printf("[INFO] Failed to create disk: %v", err)
return err
}
log.Printf("[INFO] Created disk.")
return nil
}