github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go (about)

     1  package vsphere
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform/helper/resource"
    10  	"github.com/hashicorp/terraform/terraform"
    11  	"github.com/vmware/govmomi"
    12  	"github.com/vmware/govmomi/find"
    13  	"github.com/vmware/govmomi/object"
    14  	"github.com/vmware/govmomi/property"
    15  	"github.com/vmware/govmomi/vim25/mo"
    16  	"github.com/vmware/govmomi/vim25/types"
    17  	"golang.org/x/net/context"
    18  	"path/filepath"
    19  )
    20  
    21  ///////
    22  // Various ENV vars are used to setup these tests. Look for `os.Getenv`
    23  ///////
    24  
    25  // Base setup function to check that a template, and nic information is set
    26  // TODO needs some TLC - determine exactly how we want to do this
    27  func testBasicPreCheck(t *testing.T) {
    28  
    29  	testAccPreCheck(t)
    30  
    31  	if v := os.Getenv("VSPHERE_TEMPLATE"); v == "" {
    32  		t.Fatal("env variable VSPHERE_TEMPLATE must be set for acceptance tests")
    33  	}
    34  
    35  	if v := os.Getenv("VSPHERE_IPV4_GATEWAY"); v == "" {
    36  		t.Fatal("env variable VSPHERE_IPV4_GATEWAY must be set for acceptance tests")
    37  	}
    38  
    39  	if v := os.Getenv("VSPHERE_IPV4_ADDRESS"); v == "" {
    40  		t.Fatal("env variable VSPHERE_IPV4_ADDRESS must be set for acceptance tests")
    41  	}
    42  
    43  	if v := os.Getenv("VSPHERE_NETWORK_LABEL"); v == "" {
    44  		t.Fatal("env variable VSPHERE_NETWORK_LABEL must be set for acceptance tests")
    45  	}
    46  }
    47  
    48  ////
    49  // Collects optional env vars used in the tests
    50  ////
    51  func setupBaseVars() (string, string) {
    52  	var locationOpt string
    53  	var datastoreOpt string
    54  
    55  	if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
    56  		locationOpt += fmt.Sprintf("    datacenter = \"%s\"\n", v)
    57  	}
    58  	if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
    59  		locationOpt += fmt.Sprintf("    cluster = \"%s\"\n", v)
    60  	}
    61  	if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
    62  		locationOpt += fmt.Sprintf("    resource_pool = \"%s\"\n", v)
    63  	}
    64  	if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
    65  		datastoreOpt = fmt.Sprintf("        datastore = \"%s\"\n", v)
    66  	}
    67  
    68  	return locationOpt, datastoreOpt
    69  }
    70  
    71  ////
    72  // Structs and funcs used with DHCP data template
    73  ////
    74  type TestDHCPBodyData struct {
    75  	template     string
    76  	locationOpt  string
    77  	datastoreOpt string
    78  	label        string
    79  }
    80  
    81  func (body TestDHCPBodyData) parseDHCPTemplateConfigWithTemplate(template string) string {
    82  	return fmt.Sprintf(
    83  		template,
    84  		body.locationOpt,
    85  		body.label,
    86  		body.datastoreOpt,
    87  		body.template,
    88  	)
    89  
    90  }
    91  
    92  const testAccCheckVSphereTemplate_dhcp = `
    93  %s
    94    vcpu = 2
    95    memory = 1024
    96    network_interface {
    97      label = "%s"
    98    }
    99    disk {
   100  %s
   101      template = "%s"
   102    }
   103  }
   104  `
   105  
   106  // replaces data in the above template
   107  func (body TestDHCPBodyData) parseDHCPTemplateConfig() string {
   108  	return fmt.Sprintf(
   109  		testAccCheckVSphereTemplate_dhcp,
   110  		body.locationOpt,
   111  		body.label,
   112  		body.datastoreOpt,
   113  		body.template,
   114  	)
   115  }
   116  
   117  func (body TestDHCPBodyData) testSprintfDHCPTemplateBodySecondArgDynamic(template string, arg string) string {
   118  	return fmt.Sprintf(
   119  		template,
   120  		body.locationOpt,
   121  		arg,
   122  		body.label,
   123  		body.datastoreOpt,
   124  		body.template,
   125  	)
   126  }
   127  
   128  // returns variables that are used in DHCP tests
   129  func setupTemplateFuncDHCPData() TestDHCPBodyData {
   130  
   131  	locationOpt, datastoreOpt := setupBaseVars()
   132  	data := TestDHCPBodyData{
   133  		template:     os.Getenv("VSPHERE_TEMPLATE"),
   134  		label:        os.Getenv("VSPHERE_NETWORK_LABEL_DHCP"),
   135  		locationOpt:  locationOpt,
   136  		datastoreOpt: datastoreOpt,
   137  	}
   138  	// log.Printf("[DEBUG] basic vars= %v", data)
   139  	return data
   140  
   141  }
   142  
   143  ////
   144  // Structs and funcs used with static ip data templates
   145  ////
   146  type TemplateBasicBodyVars struct {
   147  	locationOpt   string
   148  	label         string
   149  	ipv4IpAddress string
   150  	ipv4Prefix    string
   151  	ipv4Gateway   string
   152  	datastoreOpt  string
   153  	template      string
   154  }
   155  
   156  // Takes a base template that has seven "%s" values in it, used by most fixed ip
   157  // tests
   158  func (body TemplateBasicBodyVars) testSprintfTemplateBody(template string) string {
   159  
   160  	return fmt.Sprintf(
   161  		template,
   162  		body.locationOpt,
   163  		body.label,
   164  		body.ipv4IpAddress,
   165  		body.ipv4Prefix,
   166  		body.ipv4Gateway,
   167  		body.datastoreOpt,
   168  		body.template,
   169  	)
   170  }
   171  
   172  // setups variables used by fixed ip tests
   173  func setupTemplateBasicBodyVars() TemplateBasicBodyVars {
   174  
   175  	locationOpt, datastoreOpt := setupBaseVars()
   176  	prefix := os.Getenv("VSPHERE_IPV4_PREFIX")
   177  	if prefix == "" {
   178  		prefix = "24"
   179  	}
   180  	data := TemplateBasicBodyVars{
   181  		template:      os.Getenv("VSPHERE_TEMPLATE"),
   182  		ipv4Gateway:   os.Getenv("VSPHERE_IPV4_GATEWAY"),
   183  		label:         os.Getenv("VSPHERE_NETWORK_LABEL"),
   184  		ipv4IpAddress: os.Getenv("VSPHERE_IPV4_ADDRESS"),
   185  		ipv4Prefix:    prefix,
   186  		locationOpt:   locationOpt,
   187  		datastoreOpt:  datastoreOpt,
   188  	}
   189  	// log.Printf("[DEBUG] basic vars= %v", data)
   190  	return data
   191  }
   192  
   193  ////
   194  // Basic data to create series of testing functions
   195  ////
   196  type TestFuncData struct {
   197  	vm         virtualMachine
   198  	label      string
   199  	vmName     string
   200  	vmResource string
   201  	numDisks   string
   202  	numCPU     string
   203  	mem        string
   204  }
   205  
   206  // returns TestCheckFunc's that are used in many of our tests
   207  // mem defaults to 1024
   208  // cpu defaults to 2
   209  // disks defatuls to 1
   210  // vmResource defaults to "terraform-test"
   211  // vmName defaults to "vsphere_virtual_machine.foo
   212  func (test TestFuncData) testCheckFuncBasic() (
   213  	resource.TestCheckFunc, resource.TestCheckFunc, resource.TestCheckFunc, resource.TestCheckFunc,
   214  	resource.TestCheckFunc, resource.TestCheckFunc, resource.TestCheckFunc) {
   215  	// log.Printf("[DEBUG] data= %v", test)
   216  	mem := test.mem
   217  	if mem == "" {
   218  		mem = "1024"
   219  	}
   220  	cpu := test.numCPU
   221  	if cpu == "" {
   222  		cpu = "2"
   223  	}
   224  	disks := test.numDisks
   225  	if disks == "" {
   226  		disks = "1"
   227  	}
   228  	res := test.vmResource
   229  	if res == "" {
   230  		res = "terraform-test"
   231  	}
   232  	vmName := test.vmName
   233  	if vmName == "" {
   234  		vmName = "vsphere_virtual_machine.foo"
   235  	}
   236  	return testAccCheckVSphereVirtualMachineExists(vmName, &test.vm),
   237  		resource.TestCheckResourceAttr(vmName, "name", res),
   238  		resource.TestCheckResourceAttr(vmName, "vcpu", cpu),
   239  		resource.TestCheckResourceAttr(vmName, "memory", mem),
   240  		resource.TestCheckResourceAttr(vmName, "disk.#", disks),
   241  		resource.TestCheckResourceAttr(vmName, "network_interface.#", "1"),
   242  		resource.TestCheckResourceAttr(vmName, "network_interface.0.label", test.label)
   243  }
   244  
   245  const testAccCheckVSphereVirtualMachineConfig_really_basic = `
   246  resource "vsphere_virtual_machine" "foo" {
   247      name = "terraform-test"
   248  ` + testAccTemplateBasicBodyWithEnd
   249  
   250  // WARNING this is one of the base templates.  You change this and you will
   251  // be impacting multiple tests
   252  const testAccTemplateBasicBody = `
   253  %s
   254      vcpu = 2
   255      memory = 1024
   256      network_interface {
   257          label = "%s"
   258          ipv4_address = "%s"
   259          ipv4_prefix_length = %s
   260          ipv4_gateway = "%s"
   261      }
   262       disk {
   263  %s
   264          template = "%s"
   265          iops = 500
   266      }
   267  `
   268  const testAccTemplateBasicBodyWithEnd = testAccTemplateBasicBody + `
   269  }`
   270  
   271  func TestAccVSphereVirtualMachine_basic(t *testing.T) {
   272  	var vm virtualMachine
   273  	basic_vars := setupTemplateBasicBodyVars()
   274  	config := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_really_basic)
   275  
   276  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_really_basic)
   277  	log.Printf("[DEBUG] template config= %s", config)
   278  
   279  	resource.Test(t, resource.TestCase{
   280  		PreCheck:     func() { testBasicPreCheck(t) },
   281  		Providers:    testAccProviders,
   282  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   283  		Steps: []resource.TestStep{
   284  			resource.TestStep{
   285  				Config: config,
   286  				Check: resource.ComposeTestCheckFunc(
   287  					TestFuncData{vm: vm, label: basic_vars.label}.testCheckFuncBasic(),
   288  				),
   289  			},
   290  		},
   291  	})
   292  }
   293  
   294  const testAccCheckVSphereVirtualMachineConfig_debug = `
   295  provider "vsphere" {
   296    client_debug = true
   297  }
   298  
   299  ` + testAccCheckVSphereVirtualMachineConfig_really_basic
   300  
   301  func TestAccVSphereVirtualMachine_client_debug(t *testing.T) {
   302  	var vm virtualMachine
   303  	basic_vars := setupTemplateBasicBodyVars()
   304  	config := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_debug)
   305  
   306  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_debug)
   307  	log.Printf("[DEBUG] template config= %s", config)
   308  
   309  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   310  		TestFuncData{vm: vm, label: basic_vars.label}.testCheckFuncBasic()
   311  
   312  	resource.Test(t, resource.TestCase{
   313  		PreCheck:     func() { testBasicPreCheck(t) },
   314  		Providers:    testAccProviders,
   315  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   316  		Steps: []resource.TestStep{
   317  			resource.TestStep{
   318  				Config: config,
   319  				Check: resource.ComposeTestCheckFunc(
   320  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   321  					testAccCheckDebugExists(),
   322  				),
   323  			},
   324  		},
   325  	})
   326  }
   327  
   328  const testAccCheckVSphereVirtualMachineConfig_initType = `
   329  resource "vsphere_virtual_machine" "thin" {
   330      name = "terraform-test"
   331  ` + testAccTemplateBasicBody + `
   332      disk {
   333          size = 1
   334          iops = 500
   335  	controller_type = "scsi"
   336  	name = "one"
   337      }
   338      disk {
   339          size = 1
   340  	controller_type = "ide"
   341  	type = "eager_zeroed"
   342  	name = "two"
   343      }
   344  }
   345  `
   346  
   347  func TestAccVSphereVirtualMachine_diskInitType(t *testing.T) {
   348  	var vm virtualMachine
   349  	basic_vars := setupTemplateBasicBodyVars()
   350  	config := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_initType)
   351  
   352  	vmName := "vsphere_virtual_machine.thin"
   353  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   354  		TestFuncData{vm: vm, label: basic_vars.label, vmName: vmName, numDisks: "3"}.testCheckFuncBasic()
   355  
   356  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_initType)
   357  	log.Printf("[DEBUG] template config= %s", config)
   358  
   359  	resource.Test(t, resource.TestCase{
   360  		PreCheck:     func() { testAccPreCheck(t) },
   361  		Providers:    testAccProviders,
   362  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   363  		Steps: []resource.TestStep{
   364  			resource.TestStep{
   365  				Config: config,
   366  				Check: resource.ComposeTestCheckFunc(
   367  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   368  					// FIXME dynmically calculate the hashes
   369  					resource.TestCheckResourceAttr(vmName, "disk.294918912.type", "eager_zeroed"),
   370  					resource.TestCheckResourceAttr(vmName, "disk.294918912.controller_type", "ide"),
   371  					resource.TestCheckResourceAttr(vmName, "disk.1380467090.controller_type", "scsi"),
   372  				),
   373  			},
   374  		},
   375  	})
   376  }
   377  
   378  const testAccCheckVSphereVirtualMachineConfig_dhcp = `
   379  resource "vsphere_virtual_machine" "bar" {
   380      name = "terraform-test"
   381  `
   382  
   383  func TestAccVSphereVirtualMachine_dhcp(t *testing.T) {
   384  	var vm virtualMachine
   385  	data := setupTemplateFuncDHCPData()
   386  	config := testAccCheckVSphereVirtualMachineConfig_dhcp + data.parseDHCPTemplateConfigWithTemplate(testAccCheckVSphereTemplate_dhcp)
   387  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_dhcp+testAccCheckVSphereTemplate_dhcp)
   388  	log.Printf("[DEBUG] config= %s", config)
   389  
   390  	resource.Test(t, resource.TestCase{
   391  		PreCheck:     func() { testAccPreCheck(t) },
   392  		Providers:    testAccProviders,
   393  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   394  		Steps: []resource.TestStep{
   395  			resource.TestStep{
   396  				Config: config,
   397  				Check: resource.ComposeTestCheckFunc(
   398  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.bar"}.testCheckFuncBasic(),
   399  				),
   400  			},
   401  		},
   402  	})
   403  }
   404  
   405  const testAccCheckVSphereVirtualMachineConfig_custom_configs = `
   406  resource "vsphere_virtual_machine" "car" {
   407      name = "terraform-test-custom"
   408      custom_configuration_parameters {
   409        "foo" = "bar"
   410        "car" = "ferrari"
   411        "num" = 42
   412      }
   413  
   414  `
   415  
   416  func TestAccVSphereVirtualMachine_custom_configs(t *testing.T) {
   417  
   418  	var vm virtualMachine
   419  	data := setupTemplateFuncDHCPData()
   420  	config := testAccCheckVSphereVirtualMachineConfig_custom_configs + data.parseDHCPTemplateConfigWithTemplate(testAccCheckVSphereTemplate_dhcp)
   421  	vmName := "vsphere_virtual_machine.car"
   422  	res := "terraform-test-custom"
   423  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   424  		TestFuncData{vm: vm, label: data.label, vmName: vmName, vmResource: res}.testCheckFuncBasic()
   425  
   426  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_custom_configs+testAccCheckVSphereTemplate_dhcp)
   427  	log.Printf("[DEBUG] config= %s", config)
   428  
   429  	resource.Test(t, resource.TestCase{
   430  		PreCheck:     func() { testAccPreCheck(t) },
   431  		Providers:    testAccProviders,
   432  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   433  		Steps: []resource.TestStep{
   434  			resource.TestStep{
   435  				Config: config,
   436  				Check: resource.ComposeTestCheckFunc(
   437  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   438  					testAccCheckVSphereVirtualMachineExistsHasCustomConfig(vmName, &vm),
   439  					resource.TestCheckResourceAttr(vmName, "custom_configuration_parameters.foo", "bar"),
   440  					resource.TestCheckResourceAttr(vmName, "custom_configuration_parameters.car", "ferrari"),
   441  					resource.TestCheckResourceAttr(vmName, "custom_configuration_parameters.num", "42"),
   442  				),
   443  			},
   444  		},
   445  	})
   446  }
   447  
   448  const testAccCheckVSphereVirtualMachineConfig_createInFolder = `
   449  resource "vsphere_virtual_machine" "folder" {
   450      name = "terraform-test-folder"
   451      folder = "%s"
   452  `
   453  
   454  func TestAccVSphereVirtualMachine_createInExistingFolder(t *testing.T) {
   455  	var vm virtualMachine
   456  	datacenter := os.Getenv("VSPHERE_DATACENTER")
   457  
   458  	folder := "tf_test_cpureateInExistingFolder"
   459  
   460  	data := setupTemplateFuncDHCPData()
   461  	config := fmt.Sprintf(testAccCheckVSphereVirtualMachineConfig_createInFolder,
   462  		folder,
   463  	) + data.parseDHCPTemplateConfig()
   464  
   465  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_createInFolder)
   466  	log.Printf("[DEBUG] template config= %s", config)
   467  
   468  	resource.Test(t, resource.TestCase{
   469  		PreCheck:  func() { testAccPreCheck(t) },
   470  		Providers: testAccProviders,
   471  		CheckDestroy: resource.ComposeTestCheckFunc(
   472  			testAccCheckVSphereVirtualMachineDestroy,
   473  			removeVSphereFolder(datacenter, folder, ""),
   474  		),
   475  		Steps: []resource.TestStep{
   476  			resource.TestStep{
   477  				PreConfig: func() { createVSphereFolder(datacenter, folder) },
   478  				Config:    config,
   479  				Check: resource.ComposeTestCheckFunc(
   480  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.folder", vmResource: "terraform-test-folder"}.testCheckFuncBasic(),
   481  				),
   482  			},
   483  		},
   484  	})
   485  }
   486  
   487  const testAccCheckVSphereVirtualMachineConfig_createWithFolder = `
   488  resource "vsphere_folder" "with_folder" {
   489  	path = "%s"
   490  %s
   491  }
   492  resource "vsphere_virtual_machine" "with_folder" {
   493      name = "terraform-test-with-folder"
   494      folder = "${vsphere_folder.with_folder.path}"
   495  `
   496  
   497  func TestAccVSphereVirtualMachine_createWithFolder(t *testing.T) {
   498  	var vm virtualMachine
   499  	var folderLocationOpt string
   500  	var f folder
   501  
   502  	if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
   503  		folderLocationOpt = fmt.Sprintf("    datacenter = \"%s\"\n", v)
   504  	}
   505  
   506  	folder := "tf_test_cpureateWithFolder"
   507  
   508  	data := setupTemplateFuncDHCPData()
   509  	vmName := "vsphere_virtual_machine.with_folder"
   510  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   511  		TestFuncData{vm: vm, label: data.label, vmName: vmName, vmResource: "terraform-test-with-folder"}.testCheckFuncBasic()
   512  
   513  	config := fmt.Sprintf(testAccCheckVSphereVirtualMachineConfig_createWithFolder,
   514  		folder,
   515  		folderLocationOpt,
   516  	) + data.parseDHCPTemplateConfig()
   517  
   518  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_createWithFolder+testAccCheckVSphereTemplate_dhcp)
   519  	log.Printf("[DEBUG] template config= %s", config)
   520  
   521  	resource.Test(t, resource.TestCase{
   522  		PreCheck:  func() { testAccPreCheck(t) },
   523  		Providers: testAccProviders,
   524  		CheckDestroy: resource.ComposeTestCheckFunc(
   525  			testAccCheckVSphereVirtualMachineDestroy,
   526  			testAccCheckVSphereFolderDestroy,
   527  		),
   528  		Steps: []resource.TestStep{
   529  			resource.TestStep{
   530  				Config: config,
   531  				Check: resource.ComposeTestCheckFunc(
   532  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   533  					testAccCheckVSphereFolderExists(vmName, &f),
   534  					resource.TestCheckResourceAttr(vmName, "folder", folder),
   535  				),
   536  			},
   537  		},
   538  	})
   539  }
   540  
   541  const testAccCheckVsphereVirtualMachineConfig_cdrom = `
   542  resource "vsphere_virtual_machine" "with_cdrom" {
   543      name = "terraform-test-with-cdrom"
   544      cdrom {
   545          datastore = "%s"
   546          path = "%s"
   547      }
   548  `
   549  
   550  func TestAccVSphereVirtualMachine_createWithCdrom(t *testing.T) {
   551  	var vm virtualMachine
   552  
   553  	// FIXME check that these exist
   554  	cdromDatastore := os.Getenv("VSPHERE_CDROM_DATASTORE")
   555  	cdromPath := os.Getenv("VSPHERE_CDROM_PATH")
   556  	vmName := "vsphere_virtual_machine.with_cdrom"
   557  
   558  	data := setupTemplateFuncDHCPData()
   559  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   560  		TestFuncData{vm: vm, label: data.label, vmName: vmName, vmResource: "terraform-test-with-cdrom"}.testCheckFuncBasic()
   561  
   562  	config := fmt.Sprintf(
   563  		testAccCheckVsphereVirtualMachineConfig_cdrom,
   564  		cdromDatastore,
   565  		cdromPath,
   566  	) + data.parseDHCPTemplateConfig()
   567  
   568  	log.Printf("[DEBUG] template= %s", testAccCheckVsphereVirtualMachineConfig_cdrom)
   569  	log.Printf("[DEBUG] template config= %s", config)
   570  
   571  	resource.Test(t, resource.TestCase{
   572  		PreCheck:     func() { testAccPreCheck(t) },
   573  		Providers:    testAccProviders,
   574  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   575  		Steps: []resource.TestStep{
   576  			resource.TestStep{
   577  				Config: config,
   578  				Check: resource.ComposeTestCheckFunc(
   579  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   580  					//resource.TestCheckResourceAttr(
   581  					//	"vsphere_virtual_machine.with_cdrom", "disk.4088143748.template", template),
   582  					resource.TestCheckResourceAttr(vmName, "cdrom.#", "1"),
   583  					resource.TestCheckResourceAttr(vmName, "cdrom.0.datastore", cdromDatastore),
   584  					resource.TestCheckResourceAttr(vmName, "cdrom.0.path", cdromPath),
   585  				),
   586  			},
   587  		},
   588  	})
   589  }
   590  
   591  const testAccCheckVSphereVirtualMachineConfig_withExistingVmdk = `
   592  resource "vsphere_virtual_machine" "with_existing_vmdk" {
   593      name = "terraform-test-with-existing-vmdk"
   594  %s
   595      vcpu = 2
   596      memory = 1024
   597      network_interface {
   598          label = "%s"
   599      }
   600      disk {
   601  %s
   602          vmdk = "%s"
   603  	bootable = true
   604      }
   605  }
   606  `
   607  
   608  func TestAccVSphereVirtualMachine_createWithExistingVmdk(t *testing.T) {
   609  	var vm virtualMachine
   610  	vmdk_path := os.Getenv("VSPHERE_VMDK_PATH")
   611  
   612  	data := setupTemplateFuncDHCPData()
   613  	config := fmt.Sprintf(
   614  		testAccCheckVSphereVirtualMachineConfig_withExistingVmdk,
   615  		data.locationOpt,
   616  		data.label,
   617  		data.datastoreOpt,
   618  		vmdk_path,
   619  	)
   620  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_withExistingVmdk)
   621  	log.Printf("[DEBUG] template config= %s", config)
   622  
   623  	resource.Test(t, resource.TestCase{
   624  		PreCheck:     func() { testAccPreCheck(t) },
   625  		Providers:    testAccProviders,
   626  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   627  		Steps: []resource.TestStep{
   628  			resource.TestStep{
   629  				Config: config,
   630  				Check: resource.ComposeTestCheckFunc(
   631  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.with_existing_vmdk",
   632  						vmResource: "terraform-test-with-existing-vmdk"}.testCheckFuncBasic(),
   633  					//resource.TestCheckResourceAttr(
   634  					//	"vsphere_virtual_machine.with_existing_vmdk", "disk.2393891804.vmdk", vmdk_path),
   635  					//resource.TestCheckResourceAttr(
   636  					//	"vsphere_virtual_machine.with_existing_vmdk", "disk.2393891804.bootable", "true"),
   637  				),
   638  			},
   639  		},
   640  	})
   641  }
   642  
   643  const testAccCheckVSphereVirtualMachineConfig_updateMemory = `
   644  resource "vsphere_virtual_machine" "bar" {
   645      name = "terraform-test"
   646  %s
   647      vcpu = 2
   648      memory = %s
   649      network_interface {
   650          label = "%s"
   651      }
   652      disk {
   653  %s
   654        template = "%s"
   655      }
   656  }
   657  `
   658  
   659  func TestAccVSphereVirtualMachine_updateMemory(t *testing.T) {
   660  	var vm virtualMachine
   661  	data := setupTemplateFuncDHCPData()
   662  
   663  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_updateMemory)
   664  
   665  	config := data.testSprintfDHCPTemplateBodySecondArgDynamic(testAccCheckVSphereVirtualMachineConfig_updateMemory, "1024")
   666  	log.Printf("[DEBUG] template config= %s", config)
   667  
   668  	configUpdate := data.testSprintfDHCPTemplateBodySecondArgDynamic(testAccCheckVSphereVirtualMachineConfig_updateMemory, "2048")
   669  	log.Printf("[DEBUG] template configUpdate= %s", configUpdate)
   670  
   671  	resource.Test(t, resource.TestCase{
   672  		PreCheck:     func() { testAccPreCheck(t) },
   673  		Providers:    testAccProviders,
   674  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   675  		Steps: []resource.TestStep{
   676  			resource.TestStep{
   677  				Config: config,
   678  				Check: resource.ComposeTestCheckFunc(
   679  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.bar"}.testCheckFuncBasic(),
   680  				),
   681  			},
   682  			resource.TestStep{
   683  				Config: configUpdate,
   684  				Check: resource.ComposeTestCheckFunc(
   685  					TestFuncData{vm: vm, label: data.label, mem: "2048", vmName: "vsphere_virtual_machine.bar"}.testCheckFuncBasic(),
   686  				),
   687  			},
   688  		},
   689  	})
   690  }
   691  
   692  const testAccCheckVSphereVirtualMachineConfig_updateVcpu = `
   693  resource "vsphere_virtual_machine" "bar" {
   694      name = "terraform-test"
   695  %s
   696      vcpu = %s
   697      memory = 1024
   698      network_interface {
   699          label = "%s"
   700      }
   701      disk {
   702  %s
   703          template = "%s"
   704      }
   705  }
   706  `
   707  
   708  func TestAccVSphereVirtualMachine_updateVcpu(t *testing.T) {
   709  	var vm virtualMachine
   710  	data := setupTemplateFuncDHCPData()
   711  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_updateVcpu)
   712  
   713  	config := data.testSprintfDHCPTemplateBodySecondArgDynamic(testAccCheckVSphereVirtualMachineConfig_updateVcpu, "2")
   714  	log.Printf("[DEBUG] template config= %s", config)
   715  
   716  	configUpdate := data.testSprintfDHCPTemplateBodySecondArgDynamic(testAccCheckVSphereVirtualMachineConfig_updateVcpu, "4")
   717  	log.Printf("[DEBUG] template configUpdate= %s", configUpdate)
   718  
   719  	resource.Test(t, resource.TestCase{
   720  		PreCheck:     func() { testAccPreCheck(t) },
   721  		Providers:    testAccProviders,
   722  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   723  		Steps: []resource.TestStep{
   724  			resource.TestStep{
   725  				Config: config,
   726  				Check: resource.ComposeTestCheckFunc(
   727  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.bar"}.testCheckFuncBasic(),
   728  				),
   729  			},
   730  			resource.TestStep{
   731  				Config: configUpdate,
   732  				Check: resource.ComposeTestCheckFunc(
   733  					TestFuncData{vm: vm, label: data.label, vmName: "vsphere_virtual_machine.bar", numCPU: "4"}.testCheckFuncBasic(),
   734  				),
   735  			},
   736  		},
   737  	})
   738  }
   739  
   740  const testAccCheckVSphereVirtualMachineConfig_ipv4Andipv6 = `
   741  resource "vsphere_virtual_machine" "ipv4ipv6" {
   742      name = "terraform-test-ipv4-ipv6"
   743  %s
   744      vcpu = 2
   745      memory = 1024
   746      network_interface {
   747          label = "%s"
   748          ipv4_address = "%s"
   749          ipv4_prefix_length = %s
   750          ipv4_gateway = "%s"
   751          ipv6_address = "%s"
   752          ipv6_prefix_length = 64
   753          ipv6_gateway = "%s"
   754      }
   755      disk {
   756  %s
   757          template = "%s"
   758          iops = 500
   759      }
   760      disk {
   761          size = 1
   762          iops = 500
   763  	name = "one"
   764      }
   765  }
   766  `
   767  
   768  func TestAccVSphereVirtualMachine_ipv4Andipv6(t *testing.T) {
   769  	var vm virtualMachine
   770  	data := setupTemplateBasicBodyVars()
   771  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_ipv4Andipv6)
   772  
   773  	vmName := "vsphere_virtual_machine.ipv4ipv6"
   774  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   775  		TestFuncData{vm: vm, label: data.label, vmName: vmName, numDisks: "2", vmResource: "terraform-test-ipv4-ipv6"}.testCheckFuncBasic()
   776  
   777  	// FIXME test for this or warn??
   778  	ipv6Address := os.Getenv("VSPHERE_IPV6_ADDRESS")
   779  	ipv6Gateway := os.Getenv("VSPHERE_IPV6_GATEWAY")
   780  
   781  	config := fmt.Sprintf(
   782  		testAccCheckVSphereVirtualMachineConfig_ipv4Andipv6,
   783  		data.locationOpt,
   784  		data.label,
   785  		data.ipv4IpAddress,
   786  		data.ipv4Prefix,
   787  		data.ipv4Gateway,
   788  		ipv6Address,
   789  		ipv6Gateway,
   790  		data.datastoreOpt,
   791  		data.template,
   792  	)
   793  
   794  	log.Printf("[DEBUG] template config= %s", config)
   795  
   796  	resource.Test(t, resource.TestCase{
   797  		PreCheck:     func() { testAccPreCheck(t) },
   798  		Providers:    testAccProviders,
   799  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   800  		Steps: []resource.TestStep{
   801  			resource.TestStep{
   802  				Config: config,
   803  				Check: resource.ComposeTestCheckFunc(
   804  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   805  					resource.TestCheckResourceAttr(vmName, "network_interface.0.ipv4_address", data.ipv4IpAddress),
   806  					resource.TestCheckResourceAttr(vmName, "network_interface.0.ipv4_gateway", data.ipv4Gateway),
   807  					resource.TestCheckResourceAttr(vmName, "network_interface.0.ipv6_address", ipv6Address),
   808  					resource.TestCheckResourceAttr(vmName, "network_interface.0.ipv6_gateway", ipv6Gateway),
   809  				),
   810  			},
   811  		},
   812  	})
   813  }
   814  
   815  const testAccCheckVSphereVirtualMachineConfig_updateAddDisks = `
   816  resource "vsphere_virtual_machine" "foo" {
   817      name = "terraform-test"
   818  ` + testAccTemplateBasicBody + `
   819      disk {
   820          size = 1
   821          iops = 500
   822  	name = "one"
   823      }
   824  	disk {
   825          size = 1
   826          iops = 500
   827  	name = "two"
   828      }
   829  	disk {
   830          size = 1
   831          iops = 500
   832  	name = "three"
   833      }
   834  }
   835  `
   836  const testAccCheckVSphereVirtualMachineConfig_basic = `
   837  resource "vsphere_virtual_machine" "foo" {
   838      name = "terraform-test"
   839  ` + testAccTemplateBasicBody + `
   840      disk {
   841          size = 1
   842          iops = 500
   843  	name = "one"
   844      }
   845  }
   846  `
   847  
   848  func TestAccVSphereVirtualMachine_updateDisks(t *testing.T) {
   849  	var vm virtualMachine
   850  	basic_vars := setupTemplateBasicBodyVars()
   851  	config_basic := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_basic)
   852  
   853  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_basic)
   854  	log.Printf("[DEBUG] template config= %s", config_basic)
   855  
   856  	config_add := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_updateAddDisks)
   857  
   858  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_basic)
   859  	log.Printf("[DEBUG] template config= %s", config_add)
   860  
   861  	config_del := basic_vars.testSprintfTemplateBody(testAccCheckVSphereVirtualMachineConfig_really_basic)
   862  
   863  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_really_basic)
   864  	log.Printf("[DEBUG] template config= %s", config_del)
   865  
   866  	resource.Test(t, resource.TestCase{
   867  		PreCheck:     func() { testAccPreCheck(t) },
   868  		Providers:    testAccProviders,
   869  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   870  		Steps: []resource.TestStep{
   871  			resource.TestStep{
   872  				Config: config_basic,
   873  				Check: resource.ComposeTestCheckFunc(
   874  					TestFuncData{vm: vm, label: basic_vars.label, numDisks: "2"}.testCheckFuncBasic(),
   875  				),
   876  			},
   877  			resource.TestStep{
   878  				Config: config_add,
   879  				Check: resource.ComposeTestCheckFunc(
   880  					TestFuncData{vm: vm, label: basic_vars.label, numDisks: "4"}.testCheckFuncBasic(),
   881  				),
   882  			},
   883  			resource.TestStep{
   884  				Config: config_del,
   885  				Check: resource.ComposeTestCheckFunc(
   886  					TestFuncData{vm: vm, label: basic_vars.label, numDisks: "1"}.testCheckFuncBasic(),
   887  				),
   888  			},
   889  		},
   890  	})
   891  }
   892  
   893  const testAccCheckVSphereVirtualMachineConfig_mac_address = `
   894  resource "vsphere_virtual_machine" "mac_address" {
   895      name = "terraform-mac-address"
   896  %s
   897      vcpu = 2
   898      memory = 1024
   899      network_interface {
   900          label = "%s"
   901          mac_address = "%s"
   902      }
   903      disk {
   904  %s
   905          template = "%s"
   906      }
   907  }
   908  `
   909  
   910  // VSPHERE_NETWORK_MAC_ADDRESS needs to be set to run TestAccVSphereVirtualMachine_mac_address
   911  // use a basic NIC MAC address like 6:5c:89:2b:a0:64
   912  func testMacPreCheck(t *testing.T) {
   913  
   914  	testBasicPreCheck(t)
   915  
   916  	// TODO should start do parse values to ensure they are correct
   917  	// for instance
   918  	//  func ParseMAC(s string) (hw HardwareAddr, err error)
   919  	if v := os.Getenv("VSPHERE_NETWORK_MAC_ADDRESS"); v == "" {
   920  		t.Fatal("env variable VSPHERE_NETWORK_MAC_ADDRESS must be set for this acceptance test")
   921  	}
   922  }
   923  
   924  // test new mac address feature
   925  func TestAccVSphereVirtualMachine_mac_address(t *testing.T) {
   926  	var vm virtualMachine
   927  	data := setupTemplateFuncDHCPData()
   928  	vmName := "vsphere_virtual_machine.mac_address"
   929  
   930  	macAddress := os.Getenv("VSPHERE_NETWORK_MAC_ADDRESS")
   931  
   932  	log.Printf("[DEBUG] template= %s", testAccCheckVSphereVirtualMachineConfig_mac_address)
   933  	config := fmt.Sprintf(
   934  		testAccCheckVSphereVirtualMachineConfig_mac_address,
   935  		data.locationOpt,
   936  		data.label,
   937  		macAddress,
   938  		data.datastoreOpt,
   939  		data.template,
   940  	)
   941  	log.Printf("[DEBUG] template config= %s", config)
   942  
   943  	test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label :=
   944  		TestFuncData{vm: vm, label: data.label, vmName: vmName, numDisks: "1", vmResource: "terraform-mac-address"}.testCheckFuncBasic()
   945  
   946  	resource.Test(t, resource.TestCase{
   947  		PreCheck:     func() { testMacPreCheck(t) },
   948  		Providers:    testAccProviders,
   949  		CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
   950  		Steps: []resource.TestStep{
   951  			resource.TestStep{
   952  				Config: config,
   953  				Check: resource.ComposeTestCheckFunc(
   954  					test_exists, test_name, test_cpu, test_mem, test_num_disk, test_num_of_nic, test_nic_label,
   955  					resource.TestCheckResourceAttr(vmName, "network_interface.0.mac_address", macAddress),
   956  				),
   957  			},
   958  		},
   959  	})
   960  }
   961  
   962  func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
   963  	client := testAccProvider.Meta().(*govmomi.Client)
   964  	finder := find.NewFinder(client.Client, true)
   965  
   966  	for _, rs := range s.RootModule().Resources {
   967  		if rs.Type != "vsphere_virtual_machine" {
   968  			continue
   969  		}
   970  
   971  		dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
   972  		if err != nil {
   973  			return fmt.Errorf("error %s", err)
   974  		}
   975  
   976  		dcFolders, err := dc.Folders(context.TODO())
   977  		if err != nil {
   978  			return fmt.Errorf("error %s", err)
   979  		}
   980  
   981  		folder := dcFolders.VmFolder
   982  		if len(rs.Primary.Attributes["folder"]) > 0 {
   983  			si := object.NewSearchIndex(client.Client)
   984  			folderRef, err := si.FindByInventoryPath(
   985  				context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
   986  			if err != nil {
   987  				return err
   988  			} else if folderRef != nil {
   989  				folder = folderRef.(*object.Folder)
   990  			}
   991  		}
   992  
   993  		v, err := object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
   994  
   995  		if v != nil {
   996  			return fmt.Errorf("Record still exists")
   997  		}
   998  	}
   999  
  1000  	return nil
  1001  }
  1002  
  1003  func testAccCheckVSphereVirtualMachineExistsHasCustomConfig(n string, vm *virtualMachine) resource.TestCheckFunc {
  1004  	return func(s *terraform.State) error {
  1005  
  1006  		rs, ok := s.RootModule().Resources[n]
  1007  		if !ok {
  1008  			return fmt.Errorf("Not found: %s", n)
  1009  		}
  1010  
  1011  		if rs.Primary.ID == "" {
  1012  			return fmt.Errorf("No ID is set")
  1013  		}
  1014  
  1015  		client := testAccProvider.Meta().(*govmomi.Client)
  1016  		finder := find.NewFinder(client.Client, true)
  1017  
  1018  		dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
  1019  		if err != nil {
  1020  			return fmt.Errorf("error %s", err)
  1021  		}
  1022  
  1023  		dcFolders, err := dc.Folders(context.TODO())
  1024  		if err != nil {
  1025  			return fmt.Errorf("error %s", err)
  1026  		}
  1027  
  1028  		_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"])
  1029  		if err != nil {
  1030  			return fmt.Errorf("error %s", err)
  1031  		}
  1032  
  1033  		finder = finder.SetDatacenter(dc)
  1034  		instance, err := finder.VirtualMachine(context.TODO(), rs.Primary.Attributes["name"])
  1035  		if err != nil {
  1036  			return fmt.Errorf("error %s", err)
  1037  		}
  1038  
  1039  		var mvm mo.VirtualMachine
  1040  
  1041  		collector := property.DefaultCollector(client.Client)
  1042  
  1043  		if err := collector.RetrieveOne(context.TODO(), instance.Reference(), []string{"config.extraConfig"}, &mvm); err != nil {
  1044  			return fmt.Errorf("error %s", err)
  1045  		}
  1046  
  1047  		var configMap = make(map[string]types.AnyType)
  1048  		if mvm.Config != nil && mvm.Config.ExtraConfig != nil && len(mvm.Config.ExtraConfig) > 0 {
  1049  			for _, v := range mvm.Config.ExtraConfig {
  1050  				value := v.GetOptionValue()
  1051  				configMap[value.Key] = value.Value
  1052  			}
  1053  		} else {
  1054  			return fmt.Errorf("error no ExtraConfig")
  1055  		}
  1056  
  1057  		if configMap["foo"] == nil {
  1058  			return fmt.Errorf("error no ExtraConfig for 'foo'")
  1059  		}
  1060  
  1061  		if configMap["foo"] != "bar" {
  1062  			return fmt.Errorf("error ExtraConfig 'foo' != bar")
  1063  		}
  1064  
  1065  		if configMap["car"] == nil {
  1066  			return fmt.Errorf("error no ExtraConfig for 'car'")
  1067  		}
  1068  
  1069  		if configMap["car"] != "ferrari" {
  1070  			return fmt.Errorf("error ExtraConfig 'car' != ferrari")
  1071  		}
  1072  
  1073  		if configMap["num"] == nil {
  1074  			return fmt.Errorf("error no ExtraConfig for 'num'")
  1075  		}
  1076  
  1077  		// todo this should be an int, getting back a string
  1078  		if configMap["num"] != "42" {
  1079  			return fmt.Errorf("error ExtraConfig 'num' != 42")
  1080  		}
  1081  		*vm = virtualMachine{
  1082  			name: rs.Primary.ID,
  1083  		}
  1084  
  1085  		return nil
  1086  	}
  1087  }
  1088  
  1089  func testAccCheckDebugExists() resource.TestCheckFunc {
  1090  	return func(s *terraform.State) error {
  1091  		if _, err := os.Stat(filepath.Join(os.Getenv("HOME"), ".govmomi")); os.IsNotExist(err) {
  1092  			return fmt.Errorf("Debug logs not found")
  1093  		}
  1094  
  1095  		return nil
  1096  	}
  1097  
  1098  }
  1099  func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resource.TestCheckFunc {
  1100  	return func(s *terraform.State) error {
  1101  		if n == "" {
  1102  			return fmt.Errorf("No vm name passed in")
  1103  		}
  1104  		if vm == nil {
  1105  			return fmt.Errorf("No vm obj passed in")
  1106  		}
  1107  		rs, ok := s.RootModule().Resources[n]
  1108  		if !ok {
  1109  			return fmt.Errorf("Not found: %s", n)
  1110  		}
  1111  
  1112  		if rs.Primary.ID == "" {
  1113  			return fmt.Errorf("No ID is set")
  1114  		}
  1115  
  1116  		client := testAccProvider.Meta().(*govmomi.Client)
  1117  		finder := find.NewFinder(client.Client, true)
  1118  
  1119  		dc, err := finder.Datacenter(context.TODO(), rs.Primary.Attributes["datacenter"])
  1120  		if err != nil {
  1121  			return fmt.Errorf("error %s", err)
  1122  		}
  1123  
  1124  		dcFolders, err := dc.Folders(context.TODO())
  1125  		if err != nil {
  1126  			return fmt.Errorf("error %s", err)
  1127  		}
  1128  
  1129  		folder := dcFolders.VmFolder
  1130  		if len(rs.Primary.Attributes["folder"]) > 0 {
  1131  			si := object.NewSearchIndex(client.Client)
  1132  			folderRef, err := si.FindByInventoryPath(
  1133  				context.TODO(), fmt.Sprintf("%v/vm/%v", rs.Primary.Attributes["datacenter"], rs.Primary.Attributes["folder"]))
  1134  			if err != nil {
  1135  				return err
  1136  			} else if folderRef != nil {
  1137  				folder = folderRef.(*object.Folder)
  1138  			}
  1139  		}
  1140  
  1141  		_, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), folder, rs.Primary.Attributes["name"])
  1142  
  1143  		*vm = virtualMachine{
  1144  			name: rs.Primary.ID,
  1145  		}
  1146  
  1147  		return nil
  1148  	}
  1149  }