Skip to content

Commit

Permalink
Adding volume resource creation to vSphere provider
Browse files Browse the repository at this point in the history
Allows the user to create a vmdk in vSphere to the given path. Update
not implemented, further research required.  In the future this could
support updates for rename and move.
  • Loading branch information
dkalleg committed Apr 28, 2016
1 parent 33b39a7 commit 5a6a2f4
Show file tree
Hide file tree
Showing 5 changed files with 421 additions and 0 deletions.
1 change: 1 addition & 0 deletions builtin/providers/vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func Provider() terraform.ResourceProvider {

ResourcesMap: map[string]*schema.Resource{
"vsphere_folder": resourceVSphereFolder(),
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
},

Expand Down
250 changes: 250 additions & 0 deletions builtin/providers/vsphere/resource_vsphere_virtual_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
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("")
return err
}
log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %v", fileInfo.GetFileInfo())
// Todo collect and write size, 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(1000000 * 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
}
129 changes: 129 additions & 0 deletions builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package vsphere

import (
"fmt"
"log"
"os"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"golang.org/x/net/context"
)

func TestAccVSphereVirtualDisk_basic(t *testing.T) {
var datacenterOpt string
var datastoreOpt string
var initTypeOpt string
var adapterTypeOpt string

if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
datacenterOpt = v
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = v
}
if v := os.Getenv("VSPHERE_INIT_TYPE"); v != "" {
initTypeOpt += fmt.Sprintf(" type = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_ADAPTER_TYPE"); v != "" {
adapterTypeOpt += fmt.Sprintf(" adapter_type = \"%s\"\n", v)
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereVirtualDiskDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtuaDiskConfig_basic,
initTypeOpt,
adapterTypeOpt,
datacenterOpt,
datastoreOpt,
),
Check: resource.ComposeTestCheckFunc(
testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo"),
),
},
},
})
}

func testAccVSphereVirtualDiskExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)

dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}
finder = finder.SetDatacenter(dc)

ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"])
if err != nil {
return fmt.Errorf("error %s", err)
}

_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"])
if err != nil {
return fmt.Errorf("error %s", err)
}

return nil
}
}

func testAccCheckVSphereVirtualDiskDestroy(s *terraform.State) error {
log.Printf("[FINDME] test Destroy")
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)

for _, rs := range s.RootModule().Resources {
if rs.Type != "vsphere_virtual_disk" {
continue
}

dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}

finder = finder.SetDatacenter(dc)

ds, err := finder.Datastore(context.TODO(), rs.Primary.Attributes["datastore"])
if err != nil {
return fmt.Errorf("error %s", err)
}

_, err = ds.Stat(context.TODO(), rs.Primary.Attributes["vmdk_path"])
if err == nil {
return fmt.Errorf("error %s", err)
}
}

return nil
}

const testAccCheckVSphereVirtuaDiskConfig_basic = `
resource "vsphere_virtual_disk" "foo" {
size = 1
vmdk_path = "tfTestDisk.vmdk"
%s
%s
datacenter = "%s"
datastore = "%s"
}
`
Loading

0 comments on commit 5a6a2f4

Please sign in to comment.