Merge pull request #3939 from matt-deboer/master
vsphere-provider: Added folder handling for folder-qualified vm names
This commit is contained in:
commit
e065bec624
|
@ -39,6 +39,7 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"vsphere_folder": resourceVSphereFolder(),
|
||||
"vsphere_virtual_machine": resourceVSphereVirtualMachine(),
|
||||
},
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
`
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue