Adding volume resource creation to vSphere provider (#6273)
Allows the user to create a vmdk in vSphere to the given path. In the future this could support updates for rename and move.
This commit is contained in:
parent
322c885ea6
commit
a47ad103e2
|
@ -48,6 +48,7 @@ func Provider() terraform.ResourceProvider {
|
|||
ResourcesMap: map[string]*schema.Resource{
|
||||
"vsphere_file": resourceVSphereFile(),
|
||||
"vsphere_folder": resourceVSphereFolder(),
|
||||
"vsphere_virtual_disk": resourceVSphereVirtualDisk(),
|
||||
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
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
|
||||
}
|
||||
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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
`
|
|
@ -42,6 +42,14 @@ resource "vsphere_file" "ubuntu_disk" {
|
|||
destination_file = "/my_path/disks/custom_ubuntu.vmdk"
|
||||
}
|
||||
|
||||
# Create a disk image
|
||||
resource "vsphere_virtual_disk" "extraStorage" {
|
||||
size = 2
|
||||
vmdk_path = "myDisk.vmdk"
|
||||
datacenter = "Datacenter"
|
||||
datastore = "local"
|
||||
}
|
||||
|
||||
# Create a virtual machine within the folder
|
||||
resource "vsphere_virtual_machine" "web" {
|
||||
name = "terraform-web"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
layout: "vsphere"
|
||||
page_title: "VMware vSphere: vsphere_virtual_disk"
|
||||
sidebar_current: "docs-vsphere-resource-virtual-disk"
|
||||
description: |-
|
||||
Provides a VMware virtual disk resource. This can be used to create and delete virtual disks.
|
||||
---
|
||||
|
||||
# vsphere\_virtual\_disk
|
||||
|
||||
Provides a VMware virtual disk resource. This can be used to create and delete virtual disks.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "vsphere_virtual_disk" "myDisk" {
|
||||
size = 2
|
||||
vmdk_path = "myDisk.vmdk"
|
||||
datacenter = "Datacenter"
|
||||
datastore = "local"
|
||||
init_type = "thin"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `size` - (Required) Size of the disk (in GB).
|
||||
* `vmdk_path` - (Required) The path, including filename, of the virtual disk to be created. This should end with '.vmdk'.
|
||||
* `type` - (Optional) 'eagerZeroedThick' (the default), or 'thin' are supported options.
|
||||
* `datacenter` - (Optional) The name of a Datacenter in which to create the disk.
|
||||
* `datastore` - (Required) The name of the Datastore in which to create the disk.
|
Loading…
Reference in New Issue