Skip to content

Commit bb73c74

Browse files
thetuxkeeperstack72
authored andcommitted
provider/vsphere: added update function with support for vcpu and memory (hashicorp#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
1 parent b005709 commit bb73c74

2 files changed

Lines changed: 314 additions & 2 deletions

File tree

builtin/providers/vsphere/resource_vsphere_virtual_machine.go

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func resourceVSphereVirtualMachine() *schema.Resource {
105105
return &schema.Resource{
106106
Create: resourceVSphereVirtualMachineCreate,
107107
Read: resourceVSphereVirtualMachineRead,
108+
Update: resourceVSphereVirtualMachineUpdate,
108109
Delete: resourceVSphereVirtualMachineDelete,
109110

110111
Schema: map[string]*schema.Schema{
@@ -123,13 +124,11 @@ func resourceVSphereVirtualMachine() *schema.Resource {
123124
"vcpu": &schema.Schema{
124125
Type: schema.TypeInt,
125126
Required: true,
126-
ForceNew: true,
127127
},
128128

129129
"memory": &schema.Schema{
130130
Type: schema.TypeInt,
131131
Required: true,
132-
ForceNew: true,
133132
},
134133

135134
"memory_reservation": &schema.Schema{
@@ -401,6 +400,93 @@ func resourceVSphereVirtualMachine() *schema.Resource {
401400
}
402401
}
403402

403+
func resourceVSphereVirtualMachineUpdate(d *schema.ResourceData, meta interface{}) error {
404+
// flag if changes have to be applied
405+
hasChanges := false
406+
// flag if changes have to be done when powered off
407+
rebootRequired := false
408+
409+
// make config spec
410+
configSpec := types.VirtualMachineConfigSpec{}
411+
412+
if d.HasChange("vcpu") {
413+
configSpec.NumCPUs = d.Get("vcpu").(int)
414+
hasChanges = true
415+
rebootRequired = true
416+
}
417+
418+
if d.HasChange("memory") {
419+
configSpec.MemoryMB = int64(d.Get("memory").(int))
420+
hasChanges = true
421+
rebootRequired = true
422+
}
423+
424+
// do nothing if there are no changes
425+
if !hasChanges {
426+
return nil
427+
}
428+
429+
client := meta.(*govmomi.Client)
430+
dc, err := getDatacenter(client, d.Get("datacenter").(string))
431+
if err != nil {
432+
return err
433+
}
434+
finder := find.NewFinder(client.Client, true)
435+
finder = finder.SetDatacenter(dc)
436+
437+
vm, err := finder.VirtualMachine(context.TODO(), vmPath(d.Get("folder").(string), d.Get("name").(string)))
438+
if err != nil {
439+
return err
440+
}
441+
log.Printf("[DEBUG] virtual machine config spec: %v", configSpec)
442+
443+
if rebootRequired {
444+
log.Printf("[INFO] Shutting down virtual machine: %s", d.Id())
445+
446+
task, err := vm.PowerOff(context.TODO())
447+
if err != nil {
448+
return err
449+
}
450+
451+
err = task.Wait(context.TODO())
452+
if err != nil {
453+
return err
454+
}
455+
}
456+
457+
log.Printf("[INFO] Reconfiguring virtual machine: %s", d.Id())
458+
459+
task, err := vm.Reconfigure(context.TODO(), configSpec)
460+
if err != nil {
461+
log.Printf("[ERROR] %s", err)
462+
}
463+
464+
err = task.Wait(context.TODO())
465+
if err != nil {
466+
log.Printf("[ERROR] %s", err)
467+
}
468+
469+
if rebootRequired {
470+
task, err = vm.PowerOn(context.TODO())
471+
if err != nil {
472+
return err
473+
}
474+
475+
err = task.Wait(context.TODO())
476+
if err != nil {
477+
log.Printf("[ERROR] %s", err)
478+
}
479+
}
480+
481+
ip, err := vm.WaitForIP(context.TODO())
482+
if err != nil {
483+
return err
484+
}
485+
log.Printf("[DEBUG] ip address: %v", ip)
486+
487+
return resourceVSphereVirtualMachineRead(d, meta)
488+
}
489+
404490
func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error {
405491
client := meta.(*govmomi.Client)
406492

builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,168 @@ func TestAccVSphereVirtualMachine_createWithExistingVmdk(t *testing.T) {
517517
})
518518
}
519519

520+
func TestAccVSphereVirtualMachine_updateMemory(t *testing.T) {
521+
var vm virtualMachine
522+
var locationOpt string
523+
var datastoreOpt string
524+
525+
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
526+
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
527+
}
528+
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
529+
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
530+
}
531+
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
532+
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
533+
}
534+
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
535+
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
536+
}
537+
template := os.Getenv("VSPHERE_TEMPLATE")
538+
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
539+
540+
resource.Test(t, resource.TestCase{
541+
PreCheck: func() { testAccPreCheck(t) },
542+
Providers: testAccProviders,
543+
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
544+
Steps: []resource.TestStep{
545+
resource.TestStep{
546+
Config: fmt.Sprintf(
547+
testAccCheckVSphereVirtualMachineConfig_updateMemoryInitial,
548+
locationOpt,
549+
label,
550+
datastoreOpt,
551+
template,
552+
),
553+
Check: resource.ComposeTestCheckFunc(
554+
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm),
555+
resource.TestCheckResourceAttr(
556+
"vsphere_virtual_machine.bar", "name", "terraform-test"),
557+
resource.TestCheckResourceAttr(
558+
"vsphere_virtual_machine.bar", "vcpu", "2"),
559+
resource.TestCheckResourceAttr(
560+
"vsphere_virtual_machine.bar", "memory", "4096"),
561+
resource.TestCheckResourceAttr(
562+
"vsphere_virtual_machine.bar", "disk.#", "1"),
563+
resource.TestCheckResourceAttr(
564+
"vsphere_virtual_machine.bar", "disk.0.template", template),
565+
resource.TestCheckResourceAttr(
566+
"vsphere_virtual_machine.bar", "network_interface.#", "1"),
567+
resource.TestCheckResourceAttr(
568+
"vsphere_virtual_machine.bar", "network_interface.0.label", label),
569+
),
570+
},
571+
resource.TestStep{
572+
Config: fmt.Sprintf(
573+
testAccCheckVSphereVirtualMachineConfig_updateMemoryUpdate,
574+
locationOpt,
575+
label,
576+
datastoreOpt,
577+
template,
578+
),
579+
Check: resource.ComposeTestCheckFunc(
580+
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm),
581+
resource.TestCheckResourceAttr(
582+
"vsphere_virtual_machine.bar", "name", "terraform-test"),
583+
resource.TestCheckResourceAttr(
584+
"vsphere_virtual_machine.bar", "vcpu", "2"),
585+
resource.TestCheckResourceAttr(
586+
"vsphere_virtual_machine.bar", "memory", "2048"),
587+
resource.TestCheckResourceAttr(
588+
"vsphere_virtual_machine.bar", "disk.#", "1"),
589+
resource.TestCheckResourceAttr(
590+
"vsphere_virtual_machine.bar", "disk.0.template", template),
591+
resource.TestCheckResourceAttr(
592+
"vsphere_virtual_machine.bar", "network_interface.#", "1"),
593+
resource.TestCheckResourceAttr(
594+
"vsphere_virtual_machine.bar", "network_interface.0.label", label),
595+
),
596+
},
597+
},
598+
})
599+
}
600+
601+
func TestAccVSphereVirtualMachine_updateVcpu(t *testing.T) {
602+
var vm virtualMachine
603+
var locationOpt string
604+
var datastoreOpt string
605+
606+
if v := os.Getenv("VSPHERE_DATACENTER"); v != "" {
607+
locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v)
608+
}
609+
if v := os.Getenv("VSPHERE_CLUSTER"); v != "" {
610+
locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v)
611+
}
612+
if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" {
613+
locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v)
614+
}
615+
if v := os.Getenv("VSPHERE_DATASTORE"); v != "" {
616+
datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v)
617+
}
618+
template := os.Getenv("VSPHERE_TEMPLATE")
619+
label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP")
620+
621+
resource.Test(t, resource.TestCase{
622+
PreCheck: func() { testAccPreCheck(t) },
623+
Providers: testAccProviders,
624+
CheckDestroy: testAccCheckVSphereVirtualMachineDestroy,
625+
Steps: []resource.TestStep{
626+
resource.TestStep{
627+
Config: fmt.Sprintf(
628+
testAccCheckVSphereVirtualMachineConfig_updateVcpuInitial,
629+
locationOpt,
630+
label,
631+
datastoreOpt,
632+
template,
633+
),
634+
Check: resource.ComposeTestCheckFunc(
635+
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm),
636+
resource.TestCheckResourceAttr(
637+
"vsphere_virtual_machine.bar", "name", "terraform-test"),
638+
resource.TestCheckResourceAttr(
639+
"vsphere_virtual_machine.bar", "vcpu", "2"),
640+
resource.TestCheckResourceAttr(
641+
"vsphere_virtual_machine.bar", "memory", "4096"),
642+
resource.TestCheckResourceAttr(
643+
"vsphere_virtual_machine.bar", "disk.#", "1"),
644+
resource.TestCheckResourceAttr(
645+
"vsphere_virtual_machine.bar", "disk.0.template", template),
646+
resource.TestCheckResourceAttr(
647+
"vsphere_virtual_machine.bar", "network_interface.#", "1"),
648+
resource.TestCheckResourceAttr(
649+
"vsphere_virtual_machine.bar", "network_interface.0.label", label),
650+
),
651+
},
652+
resource.TestStep{
653+
Config: fmt.Sprintf(
654+
testAccCheckVSphereVirtualMachineConfig_updateVcpuUpdate,
655+
locationOpt,
656+
label,
657+
datastoreOpt,
658+
template,
659+
),
660+
Check: resource.ComposeTestCheckFunc(
661+
testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm),
662+
resource.TestCheckResourceAttr(
663+
"vsphere_virtual_machine.bar", "name", "terraform-test"),
664+
resource.TestCheckResourceAttr(
665+
"vsphere_virtual_machine.bar", "vcpu", "4"),
666+
resource.TestCheckResourceAttr(
667+
"vsphere_virtual_machine.bar", "memory", "4096"),
668+
resource.TestCheckResourceAttr(
669+
"vsphere_virtual_machine.bar", "disk.#", "1"),
670+
resource.TestCheckResourceAttr(
671+
"vsphere_virtual_machine.bar", "disk.0.template", template),
672+
resource.TestCheckResourceAttr(
673+
"vsphere_virtual_machine.bar", "network_interface.#", "1"),
674+
resource.TestCheckResourceAttr(
675+
"vsphere_virtual_machine.bar", "network_interface.0.label", label),
676+
),
677+
},
678+
},
679+
})
680+
}
681+
520682
func testAccCheckVSphereVirtualMachineDestroy(s *terraform.State) error {
521683
client := testAccProvider.Meta().(*govmomi.Client)
522684
finder := find.NewFinder(client.Client, true)
@@ -853,3 +1015,67 @@ resource "vsphere_virtual_machine" "with_existing_vmdk" {
8531015
}
8541016
}
8551017
`
1018+
1019+
const testAccCheckVSphereVirtualMachineConfig_updateMemoryInitial = `
1020+
resource "vsphere_virtual_machine" "bar" {
1021+
name = "terraform-test"
1022+
%s
1023+
vcpu = 2
1024+
memory = 4096
1025+
network_interface {
1026+
label = "%s"
1027+
}
1028+
disk {
1029+
%s
1030+
template = "%s"
1031+
}
1032+
}
1033+
`
1034+
1035+
const testAccCheckVSphereVirtualMachineConfig_updateMemoryUpdate = `
1036+
resource "vsphere_virtual_machine" "bar" {
1037+
name = "terraform-test"
1038+
%s
1039+
vcpu = 2
1040+
memory = 2048
1041+
network_interface {
1042+
label = "%s"
1043+
}
1044+
disk {
1045+
%s
1046+
template = "%s"
1047+
}
1048+
}
1049+
`
1050+
1051+
const testAccCheckVSphereVirtualMachineConfig_updateVcpuInitial = `
1052+
resource "vsphere_virtual_machine" "bar" {
1053+
name = "terraform-test"
1054+
%s
1055+
vcpu = 2
1056+
memory = 4096
1057+
network_interface {
1058+
label = "%s"
1059+
}
1060+
disk {
1061+
%s
1062+
template = "%s"
1063+
}
1064+
}
1065+
`
1066+
1067+
const testAccCheckVSphereVirtualMachineConfig_updateVcpuUpdate = `
1068+
resource "vsphere_virtual_machine" "bar" {
1069+
name = "terraform-test"
1070+
%s
1071+
vcpu = 4
1072+
memory = 4096
1073+
network_interface {
1074+
label = "%s"
1075+
}
1076+
disk {
1077+
%s
1078+
template = "%s"
1079+
}
1080+
}
1081+
`

0 commit comments

Comments
 (0)