Merge pull request #3939 from matt-deboer/master

vsphere-provider: Added folder handling for folder-qualified vm names
This commit is contained in:
Paul Hinze 2015-12-09 09:55:06 -06:00
commit e065bec624
7 changed files with 814 additions and 26 deletions

View File

@ -39,6 +39,7 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"vsphere_folder": resourceVSphereFolder(),
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
},

View File

@ -0,0 +1,233 @@
package vsphere
import (
"fmt"
"log"
"path"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"golang.org/x/net/context"
)
type folder struct {
datacenter string
existingPath string
path string
}
func resourceVSphereFolder() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereFolderCreate,
Read: resourceVSphereFolderRead,
Delete: resourceVSphereFolderDelete,
Schema: map[string]*schema.Schema{
"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"existing_path": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceVSphereFolderCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*govmomi.Client)
f := folder{
path: strings.TrimRight(d.Get("path").(string), "/"),
}
if v, ok := d.GetOk("datacenter"); ok {
f.datacenter = v.(string)
}
createFolder(client, &f)
d.Set("existing_path", f.existingPath)
d.SetId(fmt.Sprintf("%v/%v", f.datacenter, f.path))
log.Printf("[INFO] Created folder: %s", f.path)
return resourceVSphereFolderRead(d, meta)
}
func createFolder(client *govmomi.Client, f *folder) error {
finder := find.NewFinder(client.Client, true)
dc, err := finder.Datacenter(context.TODO(), f.datacenter)
if err != nil {
return fmt.Errorf("error %s", err)
}
finder = finder.SetDatacenter(dc)
si := object.NewSearchIndex(client.Client)
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
folder := dcFolders.VmFolder
var workingPath string
pathParts := strings.Split(f.path, "/")
for _, pathPart := range pathParts {
if len(workingPath) > 0 {
workingPath += "/"
}
workingPath += pathPart
subfolder, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, workingPath))
if err != nil {
return fmt.Errorf("error %s", err)
} else if subfolder == nil {
log.Printf("[DEBUG] folder not found; creating: %s", workingPath)
folder, err = folder.CreateFolder(context.TODO(), pathPart)
if err != nil {
return fmt.Errorf("Failed to create folder at %s; %s", workingPath, err)
}
} else {
log.Printf("[DEBUG] folder already exists: %s", workingPath)
f.existingPath = workingPath
folder = subfolder.(*object.Folder)
}
}
return nil
}
func resourceVSphereFolderRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] reading folder: %#v", d)
client := meta.(*govmomi.Client)
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
return err
}
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", d.Get("datacenter").(string),
d.Get("path").(string)))
if err != nil {
return err
}
if folder == nil {
d.SetId("")
}
return nil
}
func resourceVSphereFolderDelete(d *schema.ResourceData, meta interface{}) error {
f := folder{
path: strings.TrimRight(d.Get("path").(string), "/"),
existingPath: d.Get("existing_path").(string),
}
if v, ok := d.GetOk("datacenter"); ok {
f.datacenter = v.(string)
}
client := meta.(*govmomi.Client)
deleteFolder(client, &f)
d.SetId("")
return nil
}
func deleteFolder(client *govmomi.Client, f *folder) error {
dc, err := getDatacenter(client, f.datacenter)
if err != nil {
return err
}
var folder *object.Folder
currentPath := f.path
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, f.path))
if err != nil {
return fmt.Errorf("[ERROR] Could not locate folder %s: %v", f.path, err)
} else {
folder = folderRef.(*object.Folder)
}
log.Printf("[INFO] Deleting empty sub-folders of existing path: %s", f.existingPath)
for currentPath != f.existingPath {
log.Printf("[INFO] Deleting folder: %s", currentPath)
children, err := folder.Children(context.TODO())
if err != nil {
return err
}
if len(children) > 0 {
return fmt.Errorf("Folder %s is non-empty and will not be deleted", currentPath)
} else {
log.Printf("[DEBUG] current folder: %#v", folder)
currentPath = path.Dir(currentPath)
if currentPath == "." {
currentPath = ""
}
log.Printf("[INFO] parent path of %s is calculated as %s", f.path, currentPath)
task, err := folder.Destroy(context.TODO())
if err != nil {
return err
}
err = task.Wait(context.TODO())
if err != nil {
return err
}
folderRef, err = si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", f.datacenter, currentPath))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
}
return nil
}
// getDatacenter gets datacenter object
func getDatacenter(c *govmomi.Client, dc string) (*object.Datacenter, error) {
finder := find.NewFinder(c.Client, true)
if dc != "" {
d, err := finder.Datacenter(context.TODO(), dc)
return d, err
} else {
d, err := finder.DefaultDatacenter(context.TODO())
return d, err
}
}

View File

@ -0,0 +1,279 @@
package vsphere
import (
"fmt"
"os"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"golang.org/x/net/context"
)
// Basic top-level folder creation
func TestAccVSphereFolder_basic(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "basic"
resourceName := "vsphere_folder." + testMethod
path := "tf_test_basic"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereFolderDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", ""),
),
},
},
})
}
func TestAccVSphereFolder_nested(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "nested"
resourceName := "vsphere_folder." + testMethod
path := "tf_test_nested/tf_test_folder"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVSphereFolderDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", ""),
),
},
},
})
}
func TestAccVSphereFolder_dontDeleteExisting(t *testing.T) {
var f folder
datacenter := os.Getenv("VSPHERE_DATACENTER")
testMethod := "dontDeleteExisting"
resourceName := "vsphere_folder." + testMethod
existingPath := "tf_test_dontDeleteExisting/tf_existing"
path := existingPath + "/tf_nested/tf_test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
assertVSphereFolderExists(datacenter, existingPath),
removeVSphereFolder(datacenter, existingPath, ""),
),
Steps: []resource.TestStep{
resource.TestStep{
PreConfig: func() {
createVSphereFolder(datacenter, existingPath)
},
Config: fmt.Sprintf(
testAccCheckVSphereFolderConfig,
testMethod,
path,
datacenter,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereFolderExistingPathExists(resourceName, &f),
resource.TestCheckResourceAttr(
resourceName, "path", path),
resource.TestCheckResourceAttr(
resourceName, "existing_path", existingPath),
),
},
},
})
}
func testAccCheckVSphereFolderDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
for _, rs := range s.RootModule().Resources {
if rs.Type != "vsphere_folder" {
continue
}
dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
if err != nil {
return fmt.Errorf("error %s", err)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["path"])
if err == nil {
return fmt.Errorf("Record still exists")
}
}
return nil
}
func testAccCheckVSphereFolderExists(n string, f *folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Resource not found: %s", n)
}
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)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["path"])
*f = folder{
path: rs.Primary.Attributes["path"],
}
return nil
}
}
func testAccCheckVSphereFolderExistingPathExists(n string, f *folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Resource %s not found in %#v", n, s.RootModule().Resources)
}
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)
}
dcFolders, err := dc.Folders(context.TODO())
if err != nil {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["existing_path"])
*f = folder{
path: rs.Primary.Attributes["path"],
}
return nil
}
}
func assertVSphereFolderExists(datacenter string, folder_name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if err != nil {
return fmt.Errorf("Error: %s", err)
} else if folder == nil {
return fmt.Errorf("Folder %s does not exist!", folder_name)
}
return nil
}
}
func createVSphereFolder(datacenter string, folder_name string) error {
client := testAccProvider.Meta().(*govmomi.Client)
f := folder{path: folder_name, datacenter: datacenter,}
folder, err := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if err != nil {
return fmt.Errorf("error %s", err)
}
if folder == nil {
createFolder(client, &f)
} else {
return fmt.Errorf("Folder %s already exists", folder_name)
}
return nil
}
func removeVSphereFolder(datacenter string, folder_name string, existing_path string) resource.TestCheckFunc {
f := folder{path: folder_name, datacenter: datacenter, existingPath: existing_path,}
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
// finder := find.NewFinder(client.Client, true)
folder, _ := object.NewSearchIndex(client.Client).FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", datacenter, folder_name))
if folder != nil {
deleteFolder(client, &f)
}
return nil
}
}
const testAccCheckVSphereFolderConfig = `
resource "vsphere_folder" "%s" {
path = "%s"
datacenter = "%s"
}
`

View File

@ -42,6 +42,7 @@ type hardDisk struct {
type virtualMachine struct {
name string
folder string
datacenter string
cluster string
resourcePool string
@ -59,6 +60,18 @@ type virtualMachine struct {
customConfigurations map[string](types.AnyType)
}
func (v virtualMachine) Path() string {
return vmPath(v.folder, v.name)
}
func vmPath(folder string, name string) string {
var path string
if len(folder) > 0 {
path += folder + "/"
}
return path + name
}
func resourceVSphereVirtualMachine() *schema.Resource {
return &schema.Resource{
Create: resourceVSphereVirtualMachineCreate,
@ -72,6 +85,12 @@ func resourceVSphereVirtualMachine() *schema.Resource {
ForceNew: true,
},
"folder": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"vcpu": &schema.Schema{
Type: schema.TypeInt,
Required: true,
@ -228,6 +247,10 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
memoryMb: int64(d.Get("memory").(int)),
}
if v, ok := d.GetOk("folder"); ok {
vm.folder = v.(string)
}
if v, ok := d.GetOk("datacenter"); ok {
vm.datacenter = v.(string)
}
@ -344,7 +367,7 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "active",
Refresh: waitForNetworkingActive(client, vm.datacenter, vm.name),
Refresh: waitForNetworkingActive(client, vm.datacenter, vm.Path()),
Timeout: 600 * time.Second,
Delay: time.Duration(v.(int)) * time.Second,
MinTimeout: 2 * time.Second,
@ -356,13 +379,15 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{
}
}
}
d.SetId(vm.name)
d.SetId(vm.Path())
log.Printf("[INFO] Created virtual machine: %s", d.Id())
return resourceVSphereVirtualMachineRead(d, meta)
}
func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] reading virtual machine: %#v", d)
client := meta.(*govmomi.Client)
dc, err := getDatacenter(client, d.Get("datacenter").(string))
if err != nil {
@ -371,9 +396,8 @@ func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{})
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
vm, err := finder.VirtualMachine(context.TODO(), d.Id())
if err != nil {
log.Printf("[ERROR] Virtual machine not found: %s", d.Get("name").(string))
d.SetId("")
return nil
}
@ -458,7 +482,7 @@ func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{
finder := find.NewFinder(client.Client, true)
finder = finder.SetDatacenter(dc)
vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
vm, err := finder.VirtualMachine(context.TODO(), vmPath(d.Get("folder").(string), d.Get("name").(string)))
if err != nil {
return err
}
@ -522,18 +546,6 @@ func waitForNetworkingActive(client *govmomi.Client, datacenter, name string) re
}
}
// getDatacenter gets datacenter object
func getDatacenter(c *govmomi.Client, dc string) (*object.Datacenter, error) {
finder := find.NewFinder(c.Client, true)
if dc != "" {
d, err := finder.Datacenter(context.TODO(), dc)
return d, err
} else {
d, err := finder.DefaultDatacenter(context.TODO())
return d, err
}
}
// addHardDisk adds a new Hard Disk to the VirtualMachine.
func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) error {
devices, err := vm.Device(context.TODO())
@ -766,6 +778,7 @@ func findDatastore(c *govmomi.Client, sps types.StoragePlacementSpec) (*object.D
// createVirtualMchine creates a new VirtualMachine.
func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
dc, err := getDatacenter(c, vm.datacenter)
if err != nil {
return err
}
@ -798,6 +811,21 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
return err
}
log.Printf("[DEBUG] folder: %#v", vm.folder)
folder := dcFolders.VmFolder
if len(vm.folder) > 0 {
si := object.NewSearchIndex(c.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", vm.datacenter, vm.folder))
if err != nil {
return fmt.Errorf("Error reading folder %s: %s", vm.folder, err)
} else if folderRef == nil {
return fmt.Errorf("Cannot find folder %s", vm.folder)
} else {
folder = folderRef.(*object.Folder)
}
}
// network
networkDevices := []types.BaseVirtualDeviceConfigSpec{}
for _, network := range vm.networkInterfaces {
@ -886,7 +914,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
})
configSpec.Files = &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", mds.Name)}
task, err := dcFolders.VmFolder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
task, err := folder.CreateVM(context.TODO(), configSpec, resourcePool, nil)
if err != nil {
log.Printf("[ERROR] %s", err)
}
@ -896,7 +924,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
log.Printf("[ERROR] %s", err)
}
newVM, err := finder.VirtualMachine(context.TODO(), vm.name)
newVM, err := finder.VirtualMachine(context.TODO(), vm.Path())
if err != nil {
return err
}
@ -954,6 +982,21 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
if err != nil {
return err
}
log.Printf("[DEBUG] folder: %#v", vm.folder)
folder := dcFolders.VmFolder
if len(vm.folder) > 0 {
si := object.NewSearchIndex(c.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", vm.datacenter, vm.folder))
if err != nil {
return fmt.Errorf("Error reading folder %s: %s", vm.folder, err)
} else if folderRef == nil {
return fmt.Errorf("Cannot find folder %s", vm.folder)
} else {
folder = folderRef.(*object.Folder)
}
}
var datastore *object.Datastore
if vm.datastore == "" {
@ -1084,7 +1127,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
}
log.Printf("[DEBUG] clone spec: %v", cloneSpec)
task, err := template.Clone(context.TODO(), dcFolders.VmFolder, vm.name, cloneSpec)
task, err := template.Clone(context.TODO(), folder, vm.name, cloneSpec)
if err != nil {
return err
}
@ -1094,7 +1137,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
return err
}
newVM, err := finder.VirtualMachine(context.TODO(), vm.name)
newVM, err := finder.VirtualMachine(context.TODO(), vm.Path())
if err != nil {
return err
}

View File

@ -191,6 +191,140 @@ func TestAccVSphereVirtualMachine_custom_configs(t *testing.T) {
})
}
func TestAccVSphereVirtualMachine_createInExistingFolder(t *testing.T) {
var vm virtualMachine
var locationOpt string
var datastoreOpt string
var datacenter string
folder := "tf_test_createInExistingFolder"
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
datacenter = v
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineDestroy,
removeVSphereFolder(datacenter, folder, ""),
),
Steps: []resource.TestStep{
resource.TestStep{
PreConfig: func() { createVSphereFolder(datacenter, folder) },
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_createInFolder,
folder,
locationOpt,
label,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.folder", &vm),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "name", "terraform-test-folder"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "folder", folder),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.folder", "network_interface.0.label", label),
),
},
},
})
}
func TestAccVSphereVirtualMachine_createWithFolder(t *testing.T) {
var vm virtualMachine
var f folder
var locationOpt string
var folderLocationOpt string
var datastoreOpt string
folder := "tf_test_createWithFolder"
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
folderLocationOpt = fmt.Sprintf(" datacenter = \"%s\"\n", v)
locationOpt += folderLocationOpt
}
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
}
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
}
template := os.Getenv("VSPHERE_TEMPLATE")
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineDestroy,
testAccCheckVSphereFolderDestroy,
),
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccCheckVSphereVirtualMachineConfig_createWithFolder,
folder,
folderLocationOpt,
locationOpt,
label,
datastoreOpt,
template,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.with_folder", &vm),
testAccCheckVSphereFolderExists("vsphere_folder.with_folder", &f),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "name", "terraform-test-with-folder"),
// resource.TestCheckResourceAttr(
// "vsphere_virtual_machine.with_folder", "folder", folder),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "vcpu", "2"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "memory", "4096"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "disk.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "disk.0.template", template),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "network_interface.#", "1"),
resource.TestCheckResourceAttr(
"vsphere_virtual_machine.with_folder", "network_interface.0.label", label),
),
},
},
})
}
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*govmomi.Client)
finder := find.NewFinder(client.Client, true)
@ -210,7 +344,21 @@ func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
folder := dcFolders.VmFolder
if len(rs.Primary.Attributes["folder"]) > 0 {
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
if err == nil {
return fmt.Errorf("Record still exists")
}
@ -306,9 +454,9 @@ func testAccCheckVSphereVirtualMachineExistsHasCustomConfig(n string, vm *virtua
return nil
}
}
func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
@ -331,14 +479,26 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou
return fmt.Errorf("error %s", err)
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
folder := dcFolders.VmFolder
if len(rs.Primary.Attributes["folder"]) > 0 {
si := object.NewSearchIndex(client.Client)
folderRef, err := si.FindByInventoryPath(
context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
if err != nil {
return err
} else if folderRef != nil {
folder = folderRef.(*object.Folder)
}
}
_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
*vm = virtualMachine{
name: rs.Primary.ID,
}
return nil
}
}
@ -401,3 +561,41 @@ resource "vsphere_virtual_machine" "car" {
}
}
`
const testAccCheckVSphereVirtualMachineConfig_createInFolder = `
resource "vsphere_virtual_machine" "folder" {
name = "terraform-test-folder"
folder = "%s"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
disk {
%s
template = "%s"
}
}
`
const testAccCheckVSphereVirtualMachineConfig_createWithFolder = `
resource "vsphere_folder" "with_folder" {
path = "%s"
%s
}
resource "vsphere_virtual_machine" "with_folder" {
name = "terraform-test-with-folder"
folder = "${vsphere_folder.with_folder.path}"
%s
vcpu = 2
memory = 4096
network_interface {
label = "%s"
}
disk {
%s
template = "%s"
}
}
`

View File

@ -30,9 +30,15 @@ provider "vsphere" {
vsphere_server = "${var.vsphere_server}"
}
# Create a virtual machine
# Create a folder
resource "vsphere_folder" "frontend" {
path = "frontend"
}
# Create a virtual machine within the folder
resource "vsphere_virtual_machine" "web" {
name = "terraform_web"
folder = "${vsphere_folder.frontend.path}"
vcpu = 2
memory = 4096

View File

@ -0,0 +1,28 @@
---
layout: "vsphere"
page_title: "VMware vSphere: vsphere_folder"
sidebar_current: "docs-vsphere-resource-folder"
description: |-
Provides a VMware vSphere virtual machine folder resource. This can be used to create and delete virtual machine folders.
---
# vsphere\_virtual\_machine
Provides a VMware vSphere virtual machine folder resource. This can be used to create and delete virtual machine folders.
## Example Usage
```
resource "vsphere_folder" "web" {
path = "terraform_web_folder"
}
```
## Argument Reference
The following arguments are supported:
* `path` - (Required) The path of the folder to be created (relative to the datacenter root); should not begin or end with a "/"
* `datacenter` - (Optional) The name of a Datacenter in which the folder will be created
* `existing_path` - (Computed) The path of any parent folder segments which existed at the time this folder was created; on a
destroy action, the (pre-) existing path is not removed.