Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for issue 159: Changing a disk image is now propagated to the VMs #161

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ module github.com/terraform-providers/terraform-provider-nutanix

require (
github.com/client9/misspell v0.3.4
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a
github.com/golangci/golangci-lint v1.25.0
github.com/hashicorp/terraform v0.12.28
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/mitchellh/gox v1.0.1
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a h1:yU/FENpkHYISWsQrbr3pcZOBj0EuRjPzNc1+dTCLu44=
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a/go.mod h1:AEugkNu3BjBxyz958nJ5holD9PRjta6iprcoUauDbU4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.4.1 h1:4DTQfT1wWwLg/hzxwD9bkdhDQrdJtxe6DUTadPlrIeE=
github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g=
Expand Down Expand Up @@ -338,6 +340,8 @@ github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZ
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
Expand Down
139 changes: 118 additions & 21 deletions nutanix/resource_nutanix_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/jinzhu/copier"
)

var (
vmTimeout = 1 * time.Minute
vmDelay = 3 * time.Second
vmMinTimeout = 3 * time.Second
IDE = "IDE"
SCSI = "SCSI"
useHotAdd = true
)

Expand Down Expand Up @@ -1087,7 +1089,7 @@ func resourceNutanixVirtualMachineUpdate(d *schema.ResourceData, meta interface{
preFillGTUpdateRequest(guestTool, response)
preFillGUpdateRequest(guest, response)
preFillPWUpdateRequest(pw, response)

toUpdateresourceList := []*v3.VMResources{res}
if err != nil {
if strings.Contains(fmt.Sprint(err), "ENTITY_NOT_FOUND") {
d.SetId("")
Expand Down Expand Up @@ -1266,6 +1268,15 @@ func resourceNutanixVirtualMachineUpdate(d *schema.ResourceData, meta interface{
}

res.DiskList = expandDiskListUpdate(d, response)
imageMismatch := false
toUpdateresourceList, imageMismatch, err = ParseDiskImageChange(response, d, res.DiskList, toUpdateresourceList)
if err != nil {
return err
}

if imageMismatch {
hotPlugChange = false
}

postCdromCount, err := CountDiskListCdrom(res.DiskList)
if err != nil {
Expand Down Expand Up @@ -1338,33 +1349,44 @@ func resourceNutanixVirtualMachineUpdate(d *schema.ResourceData, meta interface{
mySpec += 2
metadata.SpecVersion = &mySpec
}
toUpdateresourceListlength := len(toUpdateresourceList)
for i := 0; i < toUpdateresourceListlength; i++ {
spec.Resources = toUpdateresourceList[i]
request.Metadata = metadata
request.Spec = spec

spec.Resources = res
request.Metadata = metadata
request.Spec = spec
log.Printf("[DEBUG] Updating Virtual Machine: %s, %s", d.Get("name").(string), d.Id())

log.Printf("[DEBUG] Updating Virtual Machine: %s, %s", d.Get("name").(string), d.Id())
resp, err2 := conn.V3.UpdateVM(d.Id(), request)
if err2 != nil {
return fmt.Errorf("error updating Virtual Machine UUID(%s): %s", d.Id(), err2)
}

resp, err2 := conn.V3.UpdateVM(d.Id(), request)
if err2 != nil {
return fmt.Errorf("error updating Virtual Machine UUID(%s): %s", d.Id(), err2)
}
stateConf := &resource.StateChangeConf{
Pending: []string{"QUEUED", "RUNNING"},
Target: []string{"SUCCEEDED"},
Refresh: taskStateRefreshFunc(conn, resp.Status.ExecutionContext.TaskUUID.(string)),
Timeout: vmTimeout,
Delay: vmDelay,
MinTimeout: vmMinTimeout,
}

stateConf := &resource.StateChangeConf{
Pending: []string{"QUEUED", "RUNNING"},
Target: []string{"SUCCEEDED"},
Refresh: taskStateRefreshFunc(conn, resp.Status.ExecutionContext.TaskUUID.(string)),
Timeout: vmTimeout,
Delay: vmDelay,
MinTimeout: vmMinTimeout,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf(
"error waiting for vm (%s) to update: %s", d.Id(), err)
}

if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf(
"error waiting for vm (%s) to update: %s", d.Id(), err)
// Do not do a new get in final iteration
if i < toUpdateresourceListlength-1 {
responseAfterUpdate, err := conn.V3.GetVM(d.Id())
if err != nil {
return err
}
metadata.SpecVersion = responseAfterUpdate.Metadata.SpecVersion
}
}

//Tehn, Turn On the VM.
//Then, Turn On the VM.
if err := changePowerState(conn, d.Id(), "ON"); err != nil {
return fmt.Errorf("internal error: cannot turn ON the VM with UUID(%s): %s", d.Id(), err)
}
Expand Down Expand Up @@ -2067,6 +2089,81 @@ func setVMTimeout(meta interface{}) {
}
}

func hasChangedDiskImage(orgDiskUUID string, orgImageUUID string, newDiskListInt interface{}) bool {
reflectedNew := reflect.ValueOf(newDiskListInt)
for i := 0; i < reflectedNew.Len(); i++ {
ndisk := reflectedNew.Index(i).Interface().(map[string]interface{})
if orgDiskUUID == ndisk["uuid"].(string) {
if datasourcereference, ok := ndisk["data_source_reference"]; ok {
if imageUUID, ok := datasourcereference.(map[string]interface{})["uuid"]; ok {
if orgImageUUID != imageUUID {
return true
}
}
}
}
}
return false
}

func GetChangedImageDiskUUIDs(d *schema.ResourceData) []string {
old, new := d.GetChange("disk_list")
tmpDiskUUIDList := make([]string, 0)
odiskUUID := ""
reflectedOld := reflect.ValueOf(old)
for i := 0; i < reflectedOld.Len(); i++ {
odisk := reflectedOld.Index(i).Interface().(map[string]interface{})
if odiskUUIDInt, ok := odisk["uuid"]; ok {
odiskUUID = odiskUUIDInt.(string)
if datasourcereference, ok := odisk["data_source_reference"]; ok {
if imageUUID, ok := datasourcereference.(map[string]interface{})["uuid"]; ok {
if hasChangedDiskImage(odiskUUID, imageUUID.(string), new) {
tmpDiskUUIDList = append(tmpDiskUUIDList, odiskUUID)
}
}
}
}
}
return tmpDiskUUIDList
}

func ParseDiskImageChange(vmOutput *v3.VMIntentResponse, d *schema.ResourceData, expandedDiskList []*v3.VMDisk, toUpdateresourceList []*v3.VMResources) ([]*v3.VMResources, bool, error) {
// Check if there is an image mismatch
changedImageDiskListUUIDs := GetChangedImageDiskUUIDs(d)
imageMismatch := len(changedImageDiskListUUIDs) > 0
//if there is no mismatch, just return
if !imageMismatch {
return toUpdateresourceList, imageMismatch, nil
}
// if there is a change, create a new vmResource object without the image disks (need to be removed from the vm)
tmpDiskList := make([]*v3.VMDisk, 0)
previousResourceDefinition := toUpdateresourceList[0]
utils.PrintToJSON(expandedDiskList, "expandedDiskList: ")
for _, edisk := range expandedDiskList {
// if disk has no uuid, it is new and must be added
if *edisk.UUID == "" || utils.FindIndexOfValueInStringSlice(changedImageDiskListUUIDs, *edisk.UUID) == -1 {
tmpDiskList = append(tmpDiskList, edisk)
}
}
//Need to force OFF powerstate, otherwise this will poweron the vm between changes
powerState := "OFF"
previousResourceDefinition.PowerState = &powerState
for _, rdisk := range previousResourceDefinition.DiskList {
if *rdisk.UUID != "" && utils.FindIndexOfValueInStringSlice(changedImageDiskListUUIDs, *rdisk.UUID) > -1 {
rdisk.UUID = nil
}
}

// Clone the resource
resourceClone := v3.VMResources{}
copier.Copy(&resourceClone, &previousResourceDefinition)
resourceClone.DiskList = tmpDiskList
//Add resource to the list of resources to be updated
toUpdateresourceList = append([]*v3.VMResources{&resourceClone}, toUpdateresourceList...)

return toUpdateresourceList, imageMismatch, nil
}

func resourceNutanixVirtualMachineInstanceResourceV0() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down
Loading