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

Add update support for network block in vcd_vapp_vm, allow force guest customization #310

Merged
merged 34 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
26dec49
vm.network block supports update (with VM shutdown)
Didainius Jul 31, 2019
8fbaaf5
WIP
Didainius Aug 7, 2019
7de6ab5
Tests for network update
Didainius Aug 7, 2019
94255fd
make fmt
Didainius Aug 8, 2019
7b5fcbf
Stuck
Didainius Aug 9, 2019
312e25a
Working example
Didainius Aug 12, 2019
f97e151
Update website
Didainius Aug 12, 2019
643e85b
merge master
Didainius Aug 12, 2019
64b4aba
make fmt
Didainius Aug 12, 2019
ae97686
CHANGELOG
Didainius Aug 12, 2019
b0ae2aa
Pull in govcd
Didainius Aug 12, 2019
2d4d762
bump govcd
Didainius Aug 14, 2019
727a956
merge master
Didainius Aug 14, 2019
82416f4
bump govcd
Didainius Aug 14, 2019
1e6ee4d
Add function for handling maxRetryTimeout minimum value
Didainius Aug 16, 2019
061dfcb
cleanup changelog
Didainius Aug 16, 2019
3871106
Hack - WIP
Didainius Aug 16, 2019
311c072
Fix in place
Didainius Aug 19, 2019
2e5e5b7
Remove commented block
Didainius Aug 19, 2019
85757ad
Tune docs
Didainius Aug 19, 2019
066aa7f
Update description
Didainius Aug 19, 2019
f52752f
WIP docs
Didainius Aug 19, 2019
8e7525e
Add forced customization demo steps
Didainius Aug 21, 2019
348612b
Merge master
Didainius Aug 21, 2019
db66f6a
Fix for latest govcd master
Didainius Aug 21, 2019
16dbffa
Paralellize guest customization tests
Didainius Aug 21, 2019
af9fe0f
With parallel tests
Didainius Aug 21, 2019
1744902
isForcedConfiguration() -> isForcedCustomization()
Didainius Aug 21, 2019
9bc4db8
Add 'make seqtestacc' for forcing sequential acceptance tests
Didainius Aug 22, 2019
cdb7101
Improve runtest.sh
Didainius Aug 22, 2019
255479b
Add note to testing.md
Didainius Aug 22, 2019
ed6b2be
Add reboot warning for 'terraform plan'
Didainius Aug 22, 2019
3eb1839
Tune documentation
Didainius Aug 22, 2019
72b86f9
Point correct govcd
Didainius Aug 22, 2019
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ IMPROVEMENTS:
* `vcd_org` Add import capability
* `resource/catalog_item` added catalog item metadata support [#298]
* `resource/catalog_media` added catalog media item metadata support [#298]
* `resource/vcd_vapp_vm` supports update for `network` block [#310]
* `resource/vcd_vapp_vm` allows to force guest customization [#310]
* Upgrade Terraform SDK dependency to 0.12.6 [#302]

BUG FIXES:
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ require (
github.com/hashicorp/terraform v0.12.6
github.com/vmware/go-vcloud-director/v2 v2.4.0-alpha-2
)

replace github.com/vmware/go-vcloud-director/v2 => github.com/Didainius/go-vcloud-director/v2 v2.3.2-0.20190816092212-45ef284f6029
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ github.com/Azure/go-autorest v10.15.4+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxS
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
github.com/Didainius/go-vcloud-director/v2 v2.3.2-0.20190816092212-45ef284f6029 h1:KzobNk8fjbklHWpnv1NyZbPCiXT2AYlUSPPqr9ic9Jc=
github.com/Didainius/go-vcloud-director/v2 v2.3.2-0.20190816092212-45ef284f6029/go.mod h1:+Hq7ryFfgZqsO6mXH29RQFnpIMSujCOMI57otHoXHhQ=
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
Expand Down Expand Up @@ -313,8 +315,6 @@ github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmware/go-vcloud-director/v2 v2.4.0-alpha-2 h1:OxMdBmbnYcO1fkX30c4JmJETEYDJTJmfqousMnKmdik=
github.com/vmware/go-vcloud-director/v2 v2.4.0-alpha-2/go.mod h1:+Hq7ryFfgZqsO6mXH29RQFnpIMSujCOMI57otHoXHhQ=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
Expand Down
11 changes: 11 additions & 0 deletions vcd/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,14 @@ func createTemporaryVCDConnection() *VCDClient {
}
return conn
}

// minIfLess returns:
// `min` if `value` is less than min
// `value` if `value` > `min`
func minIfLess(min, value int) int {
if value < min {
return min
}

return value
}
138 changes: 125 additions & 13 deletions vcd/resource_vcd_vapp_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,17 @@ func resourceVcdVAppVm() *schema.Resource {
},
"network": {
ConflictsWith: []string{"ip", "network_name", "vapp_network_name", "network_href"},
ForceNew: true,
Optional: true,
Type: schema.TypeList,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
ForceNew: true,
Required: true,
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"vapp", "org", "none"}, false),
Description: "Network type to use: 'vapp', 'org' or 'none'. Use 'vapp' for vApp network, 'org' to attach Org VDC network. 'none' for empty NIC.",
},
"ip_allocation_mode": {
ForceNew: true,
Optional: true,
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"POOL", "DHCP", "MANUAL", "NONE"}, false),
Expand All @@ -140,14 +137,12 @@ func resourceVcdVAppVm() *schema.Resource {
},
"ip": {
Computed: true,
ForceNew: true,
Optional: true,
Type: schema.TypeString,
ValidateFunc: checkEmptyOrSingleIP(), // Must accept empty string to ease using HCL interpolation
},
"is_primary": {
Default: false,
ForceNew: true,
Optional: true,
// By default if the value is omitted it will report schema change
// on every terraform operation. The below function
Expand Down Expand Up @@ -202,6 +197,26 @@ func resourceVcdVAppVm() *schema.Resource {
Default: false,
Description: "Expose hardware-assisted CPU virtualization to guest OS.",
},
"customization": &schema.Schema{
Optional: true,
MinItems: 1,
MaxItems: 1,
Type: schema.TypeList,
Description: "Guest customization block",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"force": {
Type: schema.TypeBool,
Optional: true,
Default: false,
// This settings is used as a 'flag' and it does not matter what is set in the
// state. If it is 'true' - then it means that 'update' procedure must set the
// VM for customization at next boot and reboot it.
DiffSuppressFunc: suppressFalse(),
},
},
},
},
},
}
}
Expand Down Expand Up @@ -246,6 +261,13 @@ func falseBoolSuppress() schema.SchemaDiffSuppressFunc {
}
}

// suppressNewFalse always suppresses when new value is false
func suppressFalse() schema.SchemaDiffSuppressFunc {
return func(k string, old string, new string, d *schema.ResourceData) bool {
return new == "false"
}
}

func resourceVcdVAppVmCreate(d *schema.ResourceData, meta interface{}) error {
vcdClient := meta.(*VCDClient)

Expand Down Expand Up @@ -500,11 +522,14 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("error getting VM2: %#v", err)
}

status, err := vm.GetStatus()
vmStatusBeforeUpdate, err := vm.GetStatus()
if err != nil {
return fmt.Errorf("error getting VM status: %#v", err)
return fmt.Errorf("error getting VM status before update: %#v", err)
}

// Check if the user requested for forced customization of VM
customizationNeeded := isForcedConfiguration(d.Get("customization"))

// VM does not have to be in POWERED_OFF state for metadata operations
if d.HasChange("metadata") {
oldRaw, newRaw := d.GetChange("metadata")
Expand Down Expand Up @@ -541,8 +566,16 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) er
}

if d.HasChange("memory") || d.HasChange("cpus") || d.HasChange("cpu_cores") || d.HasChange("power_on") || d.HasChange("disk") ||
d.HasChange("expose_hardware_virtualization") {
if status != "POWERED_OFF" {
d.HasChange("expose_hardware_virtualization") || d.HasChange("network") {

log.Printf("[TRACE] VM %s has changes: memory(%t), cpus(%t), cpu_cores(%t), power_on(%t), disk(%t), expose_hardware_virtualization(%t), network(%t)",
vm.VM.Name, d.HasChange("memory"), d.HasChange("cpus"), d.HasChange("cpu_cores"), d.HasChange("power_on"), d.HasChange("disk"),
d.HasChange("expose_hardware_virtualization"), d.HasChange("network"))

// If customization is not requested then a simple shutdown is enough
if vmStatusBeforeUpdate != "POWERED_OFF" && !customizationNeeded {
log.Printf("[DEBUG] Powering off VM %s for offline update. Previous state %s",
vm.VM.Name, vmStatusBeforeUpdate)
task, err := vm.PowerOff()
if err != nil {
return fmt.Errorf("error Powering Off: %#v", err)
Expand All @@ -553,6 +586,20 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) er
}
}

// If customization was requested then a shutdown with undeploy is needed
if vmStatusBeforeUpdate != "POWERED_OFF" && customizationNeeded {
log.Printf("[DEBUG] Un-deploying VM %s for offline update. Previous state %s",
vm.VM.Name, vmStatusBeforeUpdate)
task, err := vm.Undeploy()
if err != nil {
return fmt.Errorf("error triggering undeploy for VM %s: %s", vm.VM.Name, err)
}
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf("error waiting for undeploy task for VM %s: %s", vm.VM.Name, err)
}
}

// detaching independent disks - only possible when VM power off
if d.HasChange("disk") {
err = attachDetachDisks(d, vm, vdc)
Expand Down Expand Up @@ -611,19 +658,62 @@ func resourceVcdVAppVmUpdateExecute(d *schema.ResourceData, meta interface{}) er
}
}

if d.Get("power_on").(bool) {
if d.HasChange("network") {
networkConnectionSection, err := networksToConfig(d.Get("network").([]interface{}), vdc, vapp, vcdClient)
if err != nil {
return fmt.Errorf("unable to setup network configuration for update: %s", err)
}
err = vm.UpdateNetworkConnectionSection(&networkConnectionSection)
if err != nil {
return fmt.Errorf("unable to update network configuration: %s", err)
}
}

}

// If the VM was powered off during update but it has to be powered off
if d.Get("power_on").(bool) {
vmStatus, err := vm.GetStatus()
if err != nil {
return fmt.Errorf("error getting VM status before ensuring it is powered on: %s", err)
}
log.Printf("[DEBUG] Powering on VM %s after update. Previous state %s", vm.VM.Name, vmStatus)

// Simply power on if customization is not requested
if !customizationNeeded && vmStatus != "POWERED_ON" {
task, err := vm.PowerOn()
if err != nil {
return fmt.Errorf("error Powering Up: %#v", err)
return fmt.Errorf("error powering on: %s", err)
}

err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf(errorCompletingTask, err)
}
}

// When customization is requested VM must be un-deployed before starting it
if customizationNeeded {
log.Printf("[TRACE] forced customization for VM %s was requested. Current state %s",
vm.VM.Name, vmStatus)

if vmStatus != "POWERED_OFF" {
log.Printf("[TRACE] VM %s is in state %s. Un-deploying", vm.VM.Name, vmStatus)
task, err := vm.Undeploy()
lvirbalas marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return fmt.Errorf("error triggering undeploy for VM %s: %s", vm.VM.Name, err)
}
err = task.WaitTaskCompletion()
if err != nil {
return fmt.Errorf("error waiting for undeploy task for VM %s: %s", vm.VM.Name, err)
}
}

log.Printf("[TRACE] Powering on VM %s with forced customization", vm.VM.Name)
err = vm.PowerOnAndForceCustomization()
if err != nil {
return fmt.Errorf("failed powering on with customization: %s", err)
}
}
}

return resourceVcdVAppVmRead(d, meta)
Expand Down Expand Up @@ -830,7 +920,7 @@ func resourceVcdVAppVmDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error getting VM status: %#v", err)
}

log.Printf("[TRACE] VM Status:: %s", status)
log.Printf("[TRACE] VM Status: %s", status)
if status != "POWERED_OFF" {
log.Printf("[TRACE] Undeploying VM: %s", vm.VM.Name)
task, err := vm.Undeploy()
Expand Down Expand Up @@ -865,6 +955,7 @@ func resourceVcdVAppVmDelete(d *schema.ResourceData, meta interface{}) error {
}

log.Printf("[TRACE] Removing VM: %s", vm.VM.Name)

err = vapp.RemoveVM(vm)
if err != nil {
return fmt.Errorf("error deleting: %#v", err)
Expand Down Expand Up @@ -1086,3 +1177,24 @@ func readNetworks(vm govcd.VM, vapp govcd.VApp) ([]map[string]interface{}, error
}
return nets, nil
}

// isForcedConfiguration checks "customization" block in resource and checks if the value of field "force"
// is set to "true". It returns false if the value is not set or is set to false
func isForcedConfiguration(customizationBlock interface{}) bool {
vbauzys marked this conversation as resolved.
Show resolved Hide resolved
customizationSlice := customizationBlock.([]interface{})

if len(customizationSlice) != 1 {
return false
}

cust := customizationSlice[0]
fc := cust.(map[string]interface{})
forceCust, ok := fc["force"]
forceCustBool := forceCust.(bool)

if !ok || !forceCustBool {
return false
}

return true
}
Loading