diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index 2752e0071..5984f29ff 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -42,6 +42,7 @@ type hardDisk struct { size int64 iops int64 initType string + vmdkPath string } //Additional options Vsphere can use clones of windows machines @@ -81,6 +82,7 @@ type virtualMachine struct { timeZone string dnsSuffixes []string dnsServers []string + bootableVmdk bool linkedClone bool windowsOptionalConfig windowsOptConfig customConfigurations map[string](types.AnyType) @@ -342,6 +344,21 @@ func resourceVSphereVirtualMachine() *schema.Resource { Optional: true, ForceNew: true, }, + + "vmdk": &schema.Schema{ + // TODO: Add ValidateFunc to confirm path exists + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "", + }, + + "bootable": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, }, }, }, @@ -508,8 +525,13 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{ } else { if v, ok := disk["size"].(int); ok && v != 0 { disks[i].size = int64(v) + } else if v, ok := disk["vmdk"].(string); ok && v != "" { + disks[i].vmdkPath = v + if v, ok := disk["bootable"].(bool); ok { + vm.bootableVmdk = v + } } else { - return fmt.Errorf("If template argument is not specified, size argument is required.") + return fmt.Errorf("template, size, or vmdk argument is required") } } if v, ok := disk["datastore"].(string); ok && v != "" { @@ -518,8 +540,10 @@ func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{ } else { if v, ok := disk["size"].(int); ok && v != 0 { disks[i].size = int64(v) + } else if v, ok := disk["vmdk"].(string); ok && v != "" { + disks[i].vmdkPath = v } else { - return fmt.Errorf("Size argument is required.") + return fmt.Errorf("size or vmdk argument is required") } } @@ -774,7 +798,7 @@ func waitForNetworkingActive(client *govmomi.Client, datacenter, name string) re } // addHardDisk adds a new Hard Disk to the VirtualMachine. -func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) error { +func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string, datastore *object.Datastore, diskPath string) error { devices, err := vm.Device(context.TODO()) if err != nil { return err @@ -787,7 +811,8 @@ func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) e } log.Printf("[DEBUG] disk controller: %#v\n", controller) - disk := devices.CreateDisk(controller, "") + // TODO Check if diskPath & datastore exist + disk := devices.CreateDisk(controller, fmt.Sprintf("[%v] %v", datastore.Name(), diskPath)) existing := devices.SelectByBackingInfo(disk.Backing) log.Printf("[DEBUG] disk: %#v\n", disk) @@ -1214,7 +1239,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error { for _, hd := range vm.hardDisks { log.Printf("[DEBUG] add hard disk: %v", hd.size) log.Printf("[DEBUG] add hard disk: %v", hd.iops) - err = addHardDisk(newVM, hd.size, hd.iops, "thin") + err = addHardDisk(newVM, hd.size, hd.iops, "thin", datastore, hd.vmdkPath) if err != nil { return err } @@ -1225,6 +1250,15 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error { return err } + if vm.bootableVmdk { + newVM.PowerOn(context.TODO()) + ip, err := newVM.WaitForIP(context.TODO()) + if err != nil { + return err + } + log.Printf("[DEBUG] ip address: %v", ip) + } + return nil } @@ -1546,7 +1580,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { log.Printf("[DEBUG] VM customization finished") for i := 1; i < len(vm.hardDisks); i++ { - err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, vm.hardDisks[i].initType) + err = addHardDisk(newVM, vm.hardDisks[i].size, vm.hardDisks[i].iops, vm.hardDisks[i].initType, datastore, vm.hardDisks[i].vmdkPath) if err != nil { return err } diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go index d98876fd6..414393408 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go @@ -455,6 +455,68 @@ func TestAccVSphereVirtualMachine_createWithCdrom(t *testing.T) { }) } +func TestAccVSphereVirtualMachine_createWithExistingVmdk(t *testing.T) { + vmdk_path := os.Getenv("VSPHERE_VMDK_PATH") + gateway := os.Getenv("VSPHERE_NETWORK_GATEWAY") + label := os.Getenv("VSPHERE_NETWORK_LABEL") + ip_address := os.Getenv("VSPHERE_NETWORK_IP_ADDRESS") + + var vm virtualMachine + var locationOpt string + var datastoreOpt string + + if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { + locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", 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) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVSphereVirtualMachineDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf( + testAccCheckVSphereVirtualMachineConfig_withExistingVmdk, + locationOpt, + gateway, + label, + ip_address, + datastoreOpt, + vmdk_path, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.with_existing_vmdk", &vm), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "name", "terraform-test-with-existing-vmdk"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "vcpu", "2"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "memory", "4096"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "disk.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "disk.0.vmdk", vmdk_path), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "disk.0.bootable", "true"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "network_interface.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.with_existing_vmdk", "network_interface.0.label", label), + ), + }, + }, + }) +} + func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*govmomi.Client) finder := find.NewFinder(client.Client, true) @@ -771,3 +833,23 @@ resource "vsphere_virtual_machine" "with_cdrom" { } } ` + +const testAccCheckVSphereVirtualMachineConfig_withExistingVmdk = ` +resource "vsphere_virtual_machine" "with_existing_vmdk" { + name = "terraform-test-with-existing-vmdk" +%s + vcpu = 2 + memory = 4096 + gateway = "%s" + network_interface { + label = "%s" + ipv4_address = "%s" + ipv4_prefix_length = 24 + } + disk { +%s + vmdk = "%s" + bootable = true + } +} +` diff --git a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown index f1f0b3ba8..9027bbc36 100644 --- a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown +++ b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown @@ -106,11 +106,13 @@ The `windows_opt_config` block supports: The `disk` block supports: -* `template` - (Required if size not provided) Template for this disk. +* `template` - (Required if size and bootable_vmdk_path not provided) Template for this disk. * `datastore` - (Optional) Datastore for this disk -* `size` - (Required if template not provided) Size of this disk (in GB). +* `size` - (Required if template and bootable_vmdks_path not provided) Size of this disk (in GB). * `iops` - (Optional) Number of virtual iops to allocate for this disk. * `type` - (Optional) 'eager_zeroed' (the default), or 'thin' are supported options. +* `vmdk` - (Required if template and size not provided) Path to a vmdk in a vSphere datastore. +* `bootable` - (Optional) Set to 'true' if a vmdk was given and it should attempt to boot after creation. ## CDROM