terraform/builtin/providers/vsphere/resource_vsphere_virtual_di...

343 lines
8.3 KiB
Go

package vsphere
import (
"fmt"
"log"
"errors"
"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"
"path"
)
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" && value != "lazy" {
errors = append(errors, fmt.Errorf(
"only 'thin', 'eagerZeroedThick', and 'lazy' 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)
}
finder := find.NewFinder(client.Client, true)
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return fmt.Errorf("Error finding Datacenter: %s: %s", vDisk.datacenter, err)
}
finder = finder.SetDatacenter(dc)
ds, err := getDatastore(finder, vDisk.datastore)
if err != nil {
return fmt.Errorf("Error finding Datastore: %s: %s", vDisk.datastore, err)
}
err = createHardDisk(client, vDisk.size, ds.Path(vDisk.vmdkPath), vDisk.initType, vDisk.adapterType, vDisk.datacenter)
if err != nil {
return err
}
d.SetId(ds.Path(vDisk.vmdkPath))
log.Printf("[DEBUG] Virtual Disk id: %v", ds.Path(vDisk.vmdkPath))
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
}
ctx := context.TODO()
b, err := ds.Browser(ctx)
if err != nil {
return err
}
// `Datastore.Stat` does not allow to query `VmDiskFileQuery`. Instead, we
// search the datastore manually.
spec := types.HostDatastoreBrowserSearchSpec{
Query: []types.BaseFileQuery{&types.VmDiskFileQuery{Details: &types.VmDiskFileQueryFlags{
CapacityKb: true,
DiskType: true,
}}},
Details: &types.FileQueryFlags{
FileSize: true,
FileType: true,
Modification: true,
FileOwner: types.NewBool(true),
},
MatchPattern: []string{path.Base(vDisk.vmdkPath)},
}
dsPath := ds.Path(path.Dir(vDisk.vmdkPath))
task, err := b.SearchDatastore(context.TODO(), dsPath, &spec)
if err != nil {
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath)
return err
}
info, err := task.WaitForResult(context.TODO(), nil)
if err != nil {
if info == nil || info.Error != nil {
_, ok := info.Error.Fault.(*types.FileNotFound)
if ok {
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath)
d.SetId("")
return nil
}
}
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath)
return err
}
res := info.Result.(types.HostDatastoreBrowserSearchResults)
log.Printf("[DEBUG] num results: %d", len(res.File))
if len(res.File) == 0 {
d.SetId("")
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath)
return nil
}
if len(res.File) != 1 {
return errors.New("Datastore search did not return exactly one result")
}
fileInfo := res.File[0]
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo)
size := fileInfo.(*types.VmDiskFileInfo).CapacityKb / 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
}
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
ds, err := getDatastore(finder, vDisk.datastore)
if err != nil {
return err
}
diskPath := ds.Path(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 {
var vDiskType string
switch diskType {
case "thin":
vDiskType = "thin"
case "eagerZeroedThick":
vDiskType = "eagerZeroedThick"
case "lazy":
vDiskType = "preallocated"
}
virtualDiskManager := object.NewVirtualDiskManager(client.Client)
spec := &types.FileBackedVirtualDiskSpec{
VirtualDiskSpec: types.VirtualDiskSpec{
AdapterType: adapterType,
DiskType: vDiskType,
},
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
}