Implement update logic for compute resource
This commit is contained in:
@@ -35,3 +35,9 @@ const MaxExtraDisksPerCompute=12
|
|||||||
|
|
||||||
// MaxNetworksPerCompute sets maximum number of vNICs per compute
|
// MaxNetworksPerCompute sets maximum number of vNICs per compute
|
||||||
const MaxNetworksPerCompute=8
|
const MaxNetworksPerCompute=8
|
||||||
|
|
||||||
|
// MaxCpusPerCompute sets maximum number of vCPUs per compute
|
||||||
|
const MaxCpusPerCompute=128
|
||||||
|
|
||||||
|
// MinRamPerCompute sets minimum amount of RAM per compute in MB
|
||||||
|
const MinRamPerCompute=128
|
||||||
@@ -136,6 +136,21 @@ func parseBootDiskSize(disks []DiskRecord) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBootDiskId(disks []DiskRecord) uint {
|
||||||
|
// this return value will be used to d.Set("boot_disk_id",) item of dataSourceCompute schema
|
||||||
|
if len(disks) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range disks {
|
||||||
|
if value.Type == "B" {
|
||||||
|
return value.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the list of interfaces from compute/get response into a list of networks
|
// Parse the list of interfaces from compute/get response into a list of networks
|
||||||
// attached to this compute
|
// attached to this compute
|
||||||
func parseComputeInterfacesToNetworks(ifaces []InterfaceRecord) []interface{} {
|
func parseComputeInterfacesToNetworks(ifaces []InterfaceRecord) []interface{} {
|
||||||
@@ -157,6 +172,7 @@ func parseComputeInterfacesToNetworks(ifaces []InterfaceRecord) []interface{} {
|
|||||||
elem["net_id"] = value.NetID
|
elem["net_id"] = value.NetID
|
||||||
elem["net_type"] = value.NetType
|
elem["net_type"] = value.NetType
|
||||||
elem["ip_address"] = value.IPAddress
|
elem["ip_address"] = value.IPAddress
|
||||||
|
elem["mac"] = value.MAC
|
||||||
|
|
||||||
result[i] = elem
|
result[i] = elem
|
||||||
}
|
}
|
||||||
@@ -233,6 +249,7 @@ func flattenCompute(d *schema.ResourceData, compFacts string) error {
|
|||||||
d.Set("ram", model.Ram)
|
d.Set("ram", model.Ram)
|
||||||
// d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way
|
// d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way
|
||||||
d.Set("boot_disk_size", parseBootDiskSize(model.Disks))
|
d.Set("boot_disk_size", parseBootDiskSize(model.Disks))
|
||||||
|
d.Set("boot_disk_id", parseBootDiskId(model.Disks)) // we may need boot disk ID in resize operations
|
||||||
d.Set("image_id", model.ImageID)
|
d.Set("image_id", model.ImageID)
|
||||||
d.Set("description", model.Desc)
|
d.Set("description", model.Desc)
|
||||||
d.Set("status", model.Status)
|
d.Set("status", model.Status)
|
||||||
@@ -360,6 +377,12 @@ func dataSourceCompute() *schema.Resource {
|
|||||||
Description: "This compute instance boot disk size in GB.",
|
Description: "This compute instance boot disk size in GB.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"boot_disk_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
Description: "This compute instance boot disk ID.",
|
||||||
|
},
|
||||||
|
|
||||||
"extra_disks": {
|
"extra_disks": {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|||||||
@@ -282,6 +282,8 @@ const ComputeListAPI = "/restmachine/cloudapi/compute/list"
|
|||||||
|
|
||||||
type ComputeListResp []ComputeRecord
|
type ComputeListResp []ComputeRecord
|
||||||
|
|
||||||
|
const ComputeResizeAPI = "/restmachine/cloudapi/compute/resize"
|
||||||
|
|
||||||
//
|
//
|
||||||
// structures related to /cloudapi/compute/get
|
// structures related to /cloudapi/compute/get
|
||||||
//
|
//
|
||||||
@@ -462,6 +464,12 @@ const ComputePfwDelAPI = "/restmachine/cloudapi/compute/pfwDel"
|
|||||||
//
|
//
|
||||||
// structures related to /cloudapi/compute/net Attach/Detach API
|
// structures related to /cloudapi/compute/net Attach/Detach API
|
||||||
//
|
//
|
||||||
|
type ComputeNetMgmtRecord struct { // used to "cache" network specs when preparing to manage compute networks
|
||||||
|
ID int
|
||||||
|
Type string
|
||||||
|
IPAddress string
|
||||||
|
MAC string
|
||||||
|
}
|
||||||
const ComputeNetAttachAPI = "/restmachine/cloudapi/compute/netAttach"
|
const ComputeNetAttachAPI = "/restmachine/cloudapi/compute/netAttach"
|
||||||
|
|
||||||
const ComputeNetDetachAPI = "/restmachine/cloudapi/compute/netDetach"
|
const ComputeNetDetachAPI = "/restmachine/cloudapi/compute/netDetach"
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ func networkSubresourceSchemaMake() map[string]*schema.Schema {
|
|||||||
Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.",
|
Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"mac": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
Description: "MAC address associated with this connection. MAC address is assigned automatically.",
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
return rets
|
return rets
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2019-2020 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||||
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -168,10 +168,77 @@ func resourceComputeRead(d *schema.ResourceData, m interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func resourceComputeUpdate(d *schema.ResourceData, m interface{}) error {
|
func resourceComputeUpdate(d *schema.ResourceData, m interface{}) error {
|
||||||
log.Debugf("resourceComputeUpdate: called for Compute name %s, RGID %d",
|
log.Debugf("resourceComputeUpdate: called for Compute ID %s / name %s, RGID %d",
|
||||||
d.Get("name").(string), d.Get("rg_id").(int))
|
d.Id(), d.Get("name").(string), d.Get("rg_id").(int))
|
||||||
|
|
||||||
log.Printf("resourceComputeUpdate: NOT IMPLEMENTED YET!")
|
controller := m.(*ControllerCfg)
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Resize CPU/RAM
|
||||||
|
2. Resize (grow) boot disk
|
||||||
|
3. Update extra disks
|
||||||
|
4. Update networks
|
||||||
|
5. Update port forwards
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 1. Resize CPU/RAM
|
||||||
|
params := &url.Values{}
|
||||||
|
doUpdate := false
|
||||||
|
params.Add("computeId", d.Id())
|
||||||
|
|
||||||
|
oldCpu, newCpu := d.GetChange("cpu")
|
||||||
|
if oldCpu.(int) != newCpu.(int) {
|
||||||
|
params.Add("cpu", fmt.Sprintf("%d", newCpu.(int)))
|
||||||
|
doUpdate = true
|
||||||
|
} else {
|
||||||
|
params.Add("cpu", "0") // no change to CPU allocation
|
||||||
|
}
|
||||||
|
|
||||||
|
oldRam, newRam := d.GetChange("ram")
|
||||||
|
if oldRam.(int) != newRam.(int) {
|
||||||
|
params.Add("ram", fmt.Sprintf("%d", newRam.(int)))
|
||||||
|
doUpdate = true
|
||||||
|
} else {
|
||||||
|
params.Add("ram", "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if doUpdate {
|
||||||
|
log.Debugf("resourceComputeUpdate: changing CPU %d -> %d and/or RAM %d -> %d",
|
||||||
|
oldCpu.(int), newCpu.(int),
|
||||||
|
oldRam.(int), newRam.(int))
|
||||||
|
_, err := controller.decortAPICall("POST", ComputeResizeAPI, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Resize (grow) Boot disk
|
||||||
|
oldSize, newSize := d.GetChange("boot_disk_size")
|
||||||
|
if oldSize.(int) < newSize.(int) {
|
||||||
|
bdsParams := &url.Values{}
|
||||||
|
bdsParams.Add("diskId", fmt.Sprintf("%d", d.Get("boot_disk_id").(int)))
|
||||||
|
bdsParams.Add("size", fmt.Sprintf("%d", newSize.(int)))
|
||||||
|
log.Debugf("resourceComputeUpdate: compute ID %s, boot disk ID %d resize %d -> %d",
|
||||||
|
d.Id(), d.Get("boot_disk_id").(int), oldSize.(int), newSize.(int))
|
||||||
|
_, err := controller.decortAPICall("POST", DisksResizeAPI, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if oldSize.(int) > newSize.(int) {
|
||||||
|
log.Warnf("resourceComputeUpdate: compute ID %d - shrinking boot disk is not allowed", d.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Calculate and apply changes to data disks
|
||||||
|
err := controller.utilityComputeExtraDisksConfigure(d, true) // pass do_delta = true to apply changes, if any
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Calculate and apply changes to network connections
|
||||||
|
err = controller.utilityComputeNetworksConfigure(d, true) // pass do_delta = true to apply changes, if any
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// we may reuse dataSourceComputeRead here as we maintain similarity
|
// we may reuse dataSourceComputeRead here as we maintain similarity
|
||||||
// between Compute resource and Compute data source schemas
|
// between Compute resource and Compute data source schemas
|
||||||
@@ -267,14 +334,14 @@ func resourceCompute() *schema.Resource {
|
|||||||
"cpu": {
|
"cpu": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
ValidateFunc: validation.IntBetween(1, 64),
|
ValidateFunc: validation.IntBetween(1, MaxCpusPerCompute),
|
||||||
Description: "Number of CPUs to allocate to this compute instance.",
|
Description: "Number of CPUs to allocate to this compute instance.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ram": {
|
"ram": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
ValidateFunc: validation.IntAtLeast(512),
|
ValidateFunc: validation.IntAtLeast(MinRamPerCompute),
|
||||||
Description: "Amount of RAM in MB to allocate to this compute instance.",
|
Description: "Amount of RAM in MB to allocate to this compute instance.",
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -346,6 +413,12 @@ func resourceCompute() *schema.Resource {
|
|||||||
Description: "Name of the account this compute instance belongs to.",
|
Description: "Name of the account this compute instance belongs to.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"boot_disk_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
Description: "This compute instance boot disk ID.",
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
"disks": {
|
"disks": {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ func (ctrl *ControllerCfg) utilityComputeExtraDisksConfigure(d *schema.ResourceD
|
|||||||
}
|
}
|
||||||
|
|
||||||
if apiErrCount > 0 {
|
if apiErrCount > 0 {
|
||||||
log.Errorf("utilityComputeExtraDisksConfigure: there were %d error(s) when managing disks on Compute ID %s. Last error was: %s",
|
log.Errorf("utilityComputeExtraDisksConfigure: there were %d error(s) when managing disks of Compute ID %s. Last error was: %s",
|
||||||
apiErrCount, d.Id(), lastSavedError)
|
apiErrCount, d.Id(), lastSavedError)
|
||||||
return lastSavedError
|
return lastSavedError
|
||||||
}
|
}
|
||||||
@@ -162,14 +162,32 @@ func (ctrl *ControllerCfg) utilityComputeNetworksConfigure(d *schema.ResourceDat
|
|||||||
// "d" is filled with data according to computeResource schema, so extra networks config is retrieved via "network" key
|
// "d" is filled with data according to computeResource schema, so extra networks config is retrieved via "network" key
|
||||||
// If do_delta is true, this function will identify changes between new and existing specs for network and try to
|
// If do_delta is true, this function will identify changes between new and existing specs for network and try to
|
||||||
// update compute configuration accordingly
|
// update compute configuration accordingly
|
||||||
|
|
||||||
|
/*
|
||||||
argVal, argSet := d.GetOk("network")
|
argVal, argSet := d.GetOk("network")
|
||||||
if !argSet || len(argVal.([]interface{})) < 1 {
|
if !argSet || len(argVal.([]interface{})) < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
net_list := argVal.([]interface{}) // network is ar array of maps; for keys see func networkSubresourceSchemaMake() definition
|
net_list := argVal.([]interface{}) // network is ar array of maps; for keys see func networkSubresourceSchemaMake() definition
|
||||||
|
*/
|
||||||
|
|
||||||
for _, net := range net_list {
|
old_set, new_set := d.GetChange("network")
|
||||||
|
|
||||||
|
oldNets := make([]interface{},0,0)
|
||||||
|
if old_set != nil {
|
||||||
|
oldNets = old_set.([]interface{}) // network is ar array of maps; for keys see func networkSubresourceSchemaMake() definition
|
||||||
|
}
|
||||||
|
|
||||||
|
newNets := make([]interface{},0,0)
|
||||||
|
if new_set != nil {
|
||||||
|
newNets = new_set.([]interface{}) // network is ar array of maps; for keys see func networkSubresourceSchemaMake() definition
|
||||||
|
}
|
||||||
|
|
||||||
|
apiErrCount := 0
|
||||||
|
var lastSavedError error
|
||||||
|
|
||||||
|
if !do_delta {
|
||||||
|
for _, net := range newNets {
|
||||||
urlValues := &url.Values{}
|
urlValues := &url.Values{}
|
||||||
net_data := net.(map[string]interface{})
|
net_data := net.(map[string]interface{})
|
||||||
urlValues.Add("computeId", d.Id())
|
urlValues.Add("computeId", d.Id())
|
||||||
@@ -182,9 +200,107 @@ func (ctrl *ControllerCfg) utilityComputeNetworksConfigure(d *schema.ResourceDat
|
|||||||
_, err := ctrl.decortAPICall("POST", ComputeNetAttachAPI, urlValues)
|
_, err := ctrl.decortAPICall("POST", ComputeNetAttachAPI, urlValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// failed to attach network - partial resource update
|
// failed to attach network - partial resource update
|
||||||
return err
|
apiErrCount++
|
||||||
|
lastSavedError = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apiErrCount > 0 {
|
||||||
|
log.Errorf("utilityComputeNetworksConfigure: there were %d error(s) when managing networks of Compute ID %s. Last error was: %s",
|
||||||
|
apiErrCount, d.Id(), lastSavedError)
|
||||||
|
return lastSavedError
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attachList := make([]ComputeNetMgmtRecord, 0, MaxNetworksPerCompute)
|
||||||
|
detachList := make([]ComputeNetMgmtRecord, 0, MaxNetworksPerCompute)
|
||||||
|
attIdx := 0
|
||||||
|
detIdx := 0
|
||||||
|
match := false
|
||||||
|
|
||||||
|
for _, oRunner := range oldNets {
|
||||||
|
match = false
|
||||||
|
oSpecs := oRunner.(map[string]interface{})
|
||||||
|
for _, nRunner := range newNets {
|
||||||
|
nSpecs := nRunner.(map[string]interface{})
|
||||||
|
if oSpecs["net_id"].(int) == nSpecs["net_id"].(int) && oSpecs["net_type"].(string) == nSpecs["net_type"].(string) {
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
detachList[attIdx].ID = oSpecs["net_id"].(int)
|
||||||
|
detachList[detIdx].Type = oSpecs["net_type"].(string)
|
||||||
|
detachList[detIdx].IPAddress = oSpecs["ip_address"].(string)
|
||||||
|
detachList[detIdx].MAC = oSpecs["mac"].(string)
|
||||||
|
detIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("utilityComputeNetworksConfigure: detach list has %d items for Compute ID %s", len(detachList), d.Id())
|
||||||
|
|
||||||
|
for _, nRunner := range newNets {
|
||||||
|
match = false
|
||||||
|
nSpecs := nRunner.(map[string]interface{})
|
||||||
|
for _, oRunner := range oldNets {
|
||||||
|
oSpecs := oRunner.(map[string]interface{})
|
||||||
|
if nSpecs["net_id"].(int) == oSpecs["net_id"].(int) && nSpecs["net_type"].(string) == oSpecs["net_type"].(string) {
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
attachList[attIdx].ID = nSpecs["net_id"].(int)
|
||||||
|
attachList[detIdx].Type = nSpecs["net_type"].(string)
|
||||||
|
if nSpecs["ip_address"] != nil {
|
||||||
|
attachList[detIdx].IPAddress = nSpecs["ip_address"].(string)
|
||||||
|
} else {
|
||||||
|
attachList[detIdx].IPAddress = "" // make sure it is empty, if not coming from the schema
|
||||||
|
}
|
||||||
|
attIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("utilityComputeNetworksConfigure: attach list has %d items for Compute ID %s", len(attachList), d.Id())
|
||||||
|
|
||||||
|
for _, netRec := range detachList {
|
||||||
|
urlValues := &url.Values{}
|
||||||
|
urlValues.Add("computeId", d.Id())
|
||||||
|
urlValues.Add("ipAddr", netRec.IPAddress)
|
||||||
|
urlValues.Add("mac", netRec.MAC)
|
||||||
|
_, err := ctrl.decortAPICall("POST", ComputeNetDetachAPI, urlValues)
|
||||||
|
if err != nil {
|
||||||
|
// failed to detach this network - there will be partial resource update
|
||||||
|
log.Debugf("utilityComputeNetworksConfigure: failed to detach net ID %d of type %s from Compute ID %s: %s",
|
||||||
|
netRec.ID, netRec.Type, d.Id(), err)
|
||||||
|
apiErrCount++
|
||||||
|
lastSavedError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, netRec := range attachList {
|
||||||
|
urlValues := &url.Values{}
|
||||||
|
urlValues.Add("computeId", d.Id())
|
||||||
|
urlValues.Add("netId", fmt.Sprintf("%d",netRec.ID))
|
||||||
|
urlValues.Add("netType", netRec.Type)
|
||||||
|
if netRec.IPAddress != "" {
|
||||||
|
urlValues.Add("ipAddr", netRec.IPAddress)
|
||||||
|
}
|
||||||
|
_, err := ctrl.decortAPICall("POST", ComputeNetAttachAPI, urlValues)
|
||||||
|
if err != nil {
|
||||||
|
// failed to attach this network - there will be partial resource update
|
||||||
|
log.Debugf("utilityComputeNetworksConfigure: failed to attach net ID %d of type %s from Compute ID %s: %s",
|
||||||
|
netRec.ID, netRec.Type, d.Id(), err)
|
||||||
|
apiErrCount++
|
||||||
|
lastSavedError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiErrCount > 0 {
|
||||||
|
log.Errorf("utilityComputeNetworksConfigure: there were %d error(s) when managing networks of Compute ID %s. Last error was: %s",
|
||||||
|
apiErrCount, d.Id(), lastSavedError)
|
||||||
|
return lastSavedError
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user