From bb73c74414726247b1411ade04a129bb5e7eb567 Mon Sep 17 00:00:00 2001 From: thetuxkeeper Date: Tue, 3 May 2016 17:58:33 +0200 Subject: [PATCH] provider/vsphere: added update function with support for vcpu and memory (#6356) * added update function with support for vcpu and memory * waiting for vmware tools redundant with WaitForIP * proper error handling of PowerOn task * added test cases for update memory and vcpu * reboot flag --- .../resource_vsphere_virtual_machine.go | 90 ++++++- .../resource_vsphere_virtual_machine_test.go | 226 ++++++++++++++++++ 2 files changed, 314 insertions(+), 2 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index 62fa85e4e7..cb078e8287 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -105,6 +105,7 @@ func resourceVSphereVirtualMachine() *schema.Resource { return &schema.Resource{ Create: resourceVSphereVirtualMachineCreate, Read: resourceVSphereVirtualMachineRead, + Update: resourceVSphereVirtualMachineUpdate, Delete: resourceVSphereVirtualMachineDelete, Schema: map[string]*schema.Schema{ @@ -123,13 +124,11 @@ func resourceVSphereVirtualMachine() *schema.Resource { "vcpu": &schema.Schema{ Type: schema.TypeInt, Required: true, - ForceNew: true, }, "memory": &schema.Schema{ Type: schema.TypeInt, Required: true, - ForceNew: true, }, "memory_reservation": &schema.Schema{ @@ -401,6 +400,93 @@ func resourceVSphereVirtualMachine() *schema.Resource { } } +func resourceVSphereVirtualMachineUpdate(d *schema.ResourceData, meta interface{}) error { + // flag if changes have to be applied + hasChanges := false + // flag if changes have to be done when powered off + rebootRequired := false + + // make config spec + configSpec := types.VirtualMachineConfigSpec{} + + if d.HasChange("vcpu") { + configSpec.NumCPUs = d.Get("vcpu").(int) + hasChanges = true + rebootRequired = true + } + + if d.HasChange("memory") { + configSpec.MemoryMB = int64(d.Get("memory").(int)) + hasChanges = true + rebootRequired = true + } + + // do nothing if there are no changes + if !hasChanges { + return nil + } + + 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) + + vm, err := finder.VirtualMachine(context.TODO(), vmPath(d.Get("folder").(string), d.Get("name").(string))) + if err != nil { + return err + } + log.Printf("[DEBUG] virtual machine config spec: %v", configSpec) + + if rebootRequired { + log.Printf("[INFO] Shutting down virtual machine: %s", d.Id()) + + task, err := vm.PowerOff(context.TODO()) + if err != nil { + return err + } + + err = task.Wait(context.TODO()) + if err != nil { + return err + } + } + + log.Printf("[INFO] Reconfiguring virtual machine: %s", d.Id()) + + task, err := vm.Reconfigure(context.TODO(), configSpec) + if err != nil { + log.Printf("[ERROR] %s", err) + } + + err = task.Wait(context.TODO()) + if err != nil { + log.Printf("[ERROR] %s", err) + } + + if rebootRequired { + task, err = vm.PowerOn(context.TODO()) + if err != nil { + return err + } + + err = task.Wait(context.TODO()) + if err != nil { + log.Printf("[ERROR] %s", err) + } + } + + ip, err := vm.WaitForIP(context.TODO()) + if err != nil { + return err + } + log.Printf("[DEBUG] ip address: %v", ip) + + return resourceVSphereVirtualMachineRead(d, meta) +} + func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*govmomi.Client) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go index 4143934087..ad21d53f6a 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go @@ -517,6 +517,168 @@ func TestAccVSphereVirtualMachine_createWithExistingVmdk(t *testing.T) { }) } +func TestAccVSphereVirtualMachine_updateMemory(t *testing.T) { + 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) + } + template := os.Getenv("VSPHERE_TEMPLATE") + label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVSphereVirtualMachineDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf( + testAccCheckVSphereVirtualMachineConfig_updateMemoryInitial, + locationOpt, + label, + datastoreOpt, + template, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "name", "terraform-test"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "vcpu", "2"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "memory", "4096"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.0.template", template), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.0.label", label), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf( + testAccCheckVSphereVirtualMachineConfig_updateMemoryUpdate, + locationOpt, + label, + datastoreOpt, + template, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "name", "terraform-test"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "vcpu", "2"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "memory", "2048"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.0.template", template), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.0.label", label), + ), + }, + }, + }) +} + +func TestAccVSphereVirtualMachine_updateVcpu(t *testing.T) { + 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) + } + template := os.Getenv("VSPHERE_TEMPLATE") + label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVSphereVirtualMachineDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf( + testAccCheckVSphereVirtualMachineConfig_updateVcpuInitial, + locationOpt, + label, + datastoreOpt, + template, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "name", "terraform-test"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "vcpu", "2"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "memory", "4096"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.0.template", template), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.0.label", label), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf( + testAccCheckVSphereVirtualMachineConfig_updateVcpuUpdate, + locationOpt, + label, + datastoreOpt, + template, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "name", "terraform-test"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "vcpu", "4"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "memory", "4096"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "disk.0.template", template), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.#", "1"), + resource.TestCheckResourceAttr( + "vsphere_virtual_machine.bar", "network_interface.0.label", label), + ), + }, + }, + }) +} + func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*govmomi.Client) finder := find.NewFinder(client.Client, true) @@ -853,3 +1015,67 @@ resource "vsphere_virtual_machine" "with_existing_vmdk" { } } ` + +const testAccCheckVSphereVirtualMachineConfig_updateMemoryInitial = ` +resource "vsphere_virtual_machine" "bar" { + name = "terraform-test" +%s + vcpu = 2 + memory = 4096 + network_interface { + label = "%s" + } + disk { +%s + template = "%s" + } +} +` + +const testAccCheckVSphereVirtualMachineConfig_updateMemoryUpdate = ` +resource "vsphere_virtual_machine" "bar" { + name = "terraform-test" +%s + vcpu = 2 + memory = 2048 + network_interface { + label = "%s" + } + disk { +%s + template = "%s" + } +} +` + +const testAccCheckVSphereVirtualMachineConfig_updateVcpuInitial = ` +resource "vsphere_virtual_machine" "bar" { + name = "terraform-test" +%s + vcpu = 2 + memory = 4096 + network_interface { + label = "%s" + } + disk { +%s + template = "%s" + } +} +` + +const testAccCheckVSphereVirtualMachineConfig_updateVcpuUpdate = ` +resource "vsphere_virtual_machine" "bar" { + name = "terraform-test" +%s + vcpu = 4 + memory = 4096 + network_interface { + label = "%s" + } + disk { +%s + template = "%s" + } +} +`