End of day backup commit
This commit is contained in:
@@ -63,8 +63,8 @@ type ControllerCfg struct {
|
||||
app_id string // required for oauth2 mode
|
||||
app_secret string // required for oauth2 mode
|
||||
oauth2_url string // always required
|
||||
decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification
|
||||
cc_client *http.Client // assigned when all initial check successfully passed
|
||||
decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification
|
||||
cc_client *http.Client // assigned when all initial checks successfully passed
|
||||
}
|
||||
|
||||
func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -39,24 +39,23 @@ import (
|
||||
func flattenResgroup(d *schema.ResourceData, rg_facts string) error {
|
||||
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
||||
// from resourceRsgroupExists(...) method
|
||||
log.Printf("%s", rg_facts)
|
||||
log.Printf("flattenResgroup: ready to decode response body from %q", CloudspacesGetAPI)
|
||||
details := CloudspacesGetResp{}
|
||||
log.Debugf("%s", rg_facts)
|
||||
log.Debugf("flattenResgroup: ready to decode response body from %q", CloudspacesGetAPI)
|
||||
details := ResgroupGetResp{}
|
||||
err := json.Unmarshal([]byte(rg_facts), &details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("flattenResgroup: decoded ResGroup name %q / ID %d, tenant ID %d, public IP %q",
|
||||
details.Name, details.ID, details.TenantID, details.PublicIP)
|
||||
log.Debugf("flattenResgroup: decoded ResGroup name %q / ID %d, account ID %d, public IP %q",
|
||||
details.Name, details.ID, details.AccountID, details.PublicIP)
|
||||
|
||||
d.SetId(fmt.Sprintf("%d", details.ID))
|
||||
d.Set("name", details.Name)
|
||||
d.Set("tenant_id", details.TenantID)
|
||||
d.Set("account_id", details.AccountID)
|
||||
d.Set("grid_id", details.GridID)
|
||||
d.Set("public_ip", details.PublicIP) // legacy field - this may be obsoleted when new network segments are implemented
|
||||
|
||||
log.Printf("flattenResgroup: calling flattenQuota()")
|
||||
log.Debugf("flattenResgroup: calling flattenQuota()")
|
||||
if err = d.Set("quotas", flattenQuota(details.Quotas)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -69,7 +68,7 @@ func dataSourceResgroupRead(d *schema.ResourceData, m interface{}) error {
|
||||
if rg_facts == "" {
|
||||
// if empty string is returned from utilityResgroupCheckPresence then there is no
|
||||
// such resource group and err tells so - just return it to the calling party
|
||||
d.SetId("") // ensure ID is empty
|
||||
d.SetId("") // ensure ID is empty in this case
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -95,13 +94,13 @@ func dataSourceResgroup() *schema.Resource {
|
||||
Description: "Name of this resource group. Names are case sensitive and unique within the context of a tenant.",
|
||||
},
|
||||
|
||||
"tenant": &schema.Schema {
|
||||
"account": &schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Name of the tenant, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"tenant_id": &schema.Schema {
|
||||
"account_id": &schema.Schema {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Unique ID of the tenant, which this resource group belongs to.",
|
||||
@@ -113,12 +112,6 @@ func dataSourceResgroup() *schema.Resource {
|
||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
||||
},
|
||||
|
||||
"location": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Location of this resource group.",
|
||||
},
|
||||
|
||||
"public_ip": { // this may be obsoleted as new network segments and true resource groups are implemented
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
|
||||
@@ -60,8 +60,8 @@ type AccountAclRecord struct {
|
||||
type ResgroupRecord struct {
|
||||
ACLs []UserAclRecord `json:"ACLs"`
|
||||
Owner AccountAclRecord `json:"accountAcl"`
|
||||
TenantID int `json:"accountId"`
|
||||
TenantName string `json:"accountName"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreatedTime uint64 `json:"createdTime"`
|
||||
DefaultNetID int `json:"def_net_id"`
|
||||
@@ -85,23 +85,6 @@ type ResgroupListResp []ResgroupRecord
|
||||
// structures related to /cloudapi/rg/create API call
|
||||
//
|
||||
const ResgroupCreateAPI= "/restmachine/cloudapi/rg/create"
|
||||
type ResgroupCreateParam struct {
|
||||
TenantID int `json:"accountId"`
|
||||
GridId int `json:"gid"`
|
||||
Name string `json:"name"`
|
||||
Ram int `json:"maxMemoryCapacity"`
|
||||
Disk int `json:"maxVDiskCapacity"`
|
||||
Cpu int `json:"maxCPUCapacity"`
|
||||
NetTraffic int `json:"maxNetworkPeerTransfer"`
|
||||
ExtIPs int `json:"maxNumPublicIP"`
|
||||
Owner string `json:"owner"`
|
||||
DefNet string `json:"def_net"`
|
||||
IPCidr string `json:"ipcidr"`
|
||||
Desc string `json:"decs"`
|
||||
Reason string `json:"reason"`
|
||||
ExtNetID int `json:"extNetId"`
|
||||
ExtIP string `json:"extIp"`
|
||||
}
|
||||
|
||||
//
|
||||
// structures related to /cloudapi/rg/update API call
|
||||
@@ -139,8 +122,8 @@ const ResgroupGetAPI= "/restmachine/cloudapi/rg/get"
|
||||
type ResgroupGetResp struct {
|
||||
ACLs []UserAclRecord `json:"ACLs"`
|
||||
Usage UsageRecord `json:"Resources"`
|
||||
TenantID int `json:"accountId"`
|
||||
TenantName string `json:"accountName"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreatedTime uint64 `json:"createdTime"`
|
||||
@@ -182,11 +165,6 @@ type ResgroupUpdateParam struct {
|
||||
// structures related to /cloudapi/rg/delete API
|
||||
//
|
||||
const ResgroupDeleteAPI = "/restmachine/cloudapi/rg/delete"
|
||||
type ResgroupDeleteParam struct {
|
||||
ID uint `json:"rgId"`
|
||||
Force bool `json:"force"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
//
|
||||
// structures related to /cloudapi/kvmXXX/create APIs
|
||||
@@ -245,8 +223,8 @@ type SnapSetRecord struct {
|
||||
}
|
||||
|
||||
type ComputeRecord struct {
|
||||
TenantID int `json:"accountId"`
|
||||
TenantName string `json:"accountName"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
ACLs []UserAclRecord `json:"acl"`
|
||||
Arch string `json:"arch"`
|
||||
BootDiskSize int `json:"bootdiskSize"`
|
||||
@@ -302,7 +280,7 @@ type SnapshotRecord struct {
|
||||
type DiskRecord struct {
|
||||
// ACLs `json:"ACL"` - it is a dictionary, special parsing required
|
||||
// was - Acl map[string]string `json:"acl"`
|
||||
TenantID int `json:"accountId"`
|
||||
AccountID int `json:"accountId"`
|
||||
BootPartition int `json:"bootPartition"`
|
||||
CreatedTime uint64 `json:"creationTime"`
|
||||
DeletedTime uint64 `json:"deletionTime"`
|
||||
@@ -347,8 +325,8 @@ type ComputeGetParam struct {
|
||||
}
|
||||
type ComputeGetResp struct {
|
||||
// ACLs `json:"ACL"` - it is a dictionary, special parsing required
|
||||
TenantID int `json:"accountId"`
|
||||
TenantName string `json:"accountName"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
Arch string `json:"arch"`
|
||||
BootDiskSize int `json:"bootdiskSize"`
|
||||
CloneReference int `json:"cloneReference"`
|
||||
@@ -391,7 +369,7 @@ type ComputeGetResp struct {
|
||||
// structures related to /restmachine/cloudapi/images/list API
|
||||
//
|
||||
type ImageRecord struct {
|
||||
TenantID uint `json:"accountId"`
|
||||
AccountID uint `json:"accountId"`
|
||||
Arch string `json:"architecture`
|
||||
BootType string `json:"bootType"`
|
||||
IsBootable boo `json:"bootable"`
|
||||
@@ -411,7 +389,7 @@ type ImageRecord struct {
|
||||
|
||||
const ImagesListAPI = "/restmachine/cloudapi/images/list"
|
||||
type ImagesListParam struct {
|
||||
TenantID int `json:"accountId"`
|
||||
AccountID int `json:"accountId"`
|
||||
}
|
||||
type ImagesListResp []ImageRecord
|
||||
|
||||
@@ -426,7 +404,7 @@ type ExtNetRecord struct {
|
||||
|
||||
const ExtNetListAPI = "/restmachine/cloudapi/extnet/list"
|
||||
type ExtNetListParam struct {
|
||||
TenantID int `json:"accountId"`
|
||||
AccountID int `json:"accountId"`
|
||||
}
|
||||
type ExtNetListResp []ExtNetRecord
|
||||
|
||||
@@ -434,7 +412,7 @@ type ExtNetListResp []ExtNetRecord
|
||||
//
|
||||
// structures related to /cloudapi/accounts/list API
|
||||
//
|
||||
type TenantRecord struct {
|
||||
type AccountRecord struct {
|
||||
ACLs []UserAclRecord `json:"acl"`
|
||||
CreatedTime uint64 `json:"creationTime"`
|
||||
DeletedTime uint64 `json:"deletionTime"`
|
||||
@@ -444,8 +422,8 @@ type TenantRecord struct {
|
||||
UpdatedTime uint64 `json:"updateTime"`
|
||||
}
|
||||
|
||||
const TenantsListAPI = "/restmachine/cloudapi/accounts/list"
|
||||
type TenantsListResp []TenantRecord
|
||||
const AccountsListAPI = "/restmachine/cloudapi/accounts/list"
|
||||
type AccountsListResp []AccountRecord
|
||||
|
||||
//
|
||||
// structures related to /cloudapi/portforwarding/list API
|
||||
@@ -516,7 +494,7 @@ const ComputeDiskDetachAPI = "/restmachine/cloudapi/compute/diskDetach"
|
||||
// structures related to /cloudapi/disks/create
|
||||
//
|
||||
type DiskCreateParam struct {
|
||||
TenantID int `json:"accountId`
|
||||
AccountID int `json:"accountId`
|
||||
GridID int `json:"gid"`
|
||||
Name string `json:"string"`
|
||||
Description string `json:"description"`
|
||||
|
||||
@@ -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>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -24,6 +24,8 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
|
||||
|
||||
package decort
|
||||
|
||||
/*
|
||||
|
||||
type DiskConfig struct {
|
||||
Label string
|
||||
Size int
|
||||
@@ -65,7 +67,7 @@ type ComputeConfig struct {
|
||||
Description string
|
||||
// The following two parameters are required to create data disks by
|
||||
// a separate disks/create API call
|
||||
TenantID int
|
||||
AccountID int
|
||||
GridID int
|
||||
// The following one paratmeter is required to create port forwards
|
||||
// it will be obsoleted when we implement true Resource Groups
|
||||
@@ -81,8 +83,8 @@ type ResgroupQuotaConfig struct {
|
||||
}
|
||||
|
||||
type ResgroupConfig struct {
|
||||
TenantID int
|
||||
TenantName string
|
||||
AccountID int
|
||||
AccountName string
|
||||
Location string
|
||||
Name string
|
||||
ID int
|
||||
@@ -90,4 +92,6 @@ type ResgroupConfig struct {
|
||||
ExtIP string // legacy field for VDC - this will eventually become obsoleted by true Resource Groups
|
||||
Quota ResgroupQuotaConfig
|
||||
Network NetworkConfig
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2019 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>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package decs
|
||||
package decort
|
||||
|
||||
import (
|
||||
|
||||
@@ -100,14 +100,18 @@ func Provider() *schema.Provider {
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource {
|
||||
"decs_resgroup": resourceResgroup(),
|
||||
"decs_vm": resourceVm(),
|
||||
"decort_resgroup": resourceResgroup(),
|
||||
"decort_kvmx86": resourceKvmX86(),
|
||||
"decort_disk": resourceDisk(),
|
||||
"decort_vins": resourceVins(),
|
||||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource {
|
||||
"decs_resgroup": dataSourceResgroup(),
|
||||
"decs_vm": dataSourceVm(),
|
||||
"decs_image": dataSourceImage(),
|
||||
"decort_resgroup": dataSourceResgroup(),
|
||||
"decs_kvmx86": dataSourceCompute(),
|
||||
"decort_image": dataSourceImage(),
|
||||
"decort_disk": dataSourceDisk(),
|
||||
"decort_vins": dataSourceVins(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
|
||||
@@ -35,12 +35,12 @@ import (
|
||||
)
|
||||
|
||||
func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
|
||||
log.Printf("resourceResgroupCreate: called for res group name %q, tenant name %q",
|
||||
d.Get("name").(string), d.Get("tenant").(string))
|
||||
log.Debugf("resourceResgroupCreate: called for res group name %q, account name %q",
|
||||
d.Get("name").(string), d.Get("account").(string))
|
||||
|
||||
rg := &ResgroupConfig{
|
||||
Name: d.Get("name").(string),
|
||||
TenantName: d.Get("tenant").(string),
|
||||
AccountName: d.Get("account").(string),
|
||||
}
|
||||
|
||||
// validate that we have all parameters required to create the new Resource Group
|
||||
@@ -49,35 +49,55 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
|
||||
if arg_set {
|
||||
rg.Location = arg_value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("Cannot create new resource group %q for tenant %q: missing location parameter.",
|
||||
rg.Name, rg.TenantName)
|
||||
return fmt.Errorf("Cannot create new RG %q for account %q: missing location parameter.",
|
||||
rg.Name, rg.AccountName)
|
||||
}
|
||||
// tenant ID is required to create new resource group
|
||||
// obtain Tenant ID by tenant name - it should not be zero on success
|
||||
tenant_id, err := utilityGetTenantIdByName(rg.TenantName, m)
|
||||
// account ID is required to create new resource group
|
||||
// obtain Account ID by account name - it should not be zero on success
|
||||
account_id, err := utilityGetAccountIdByName(rg.AccountName, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rg.TenantID = tenant_id
|
||||
rg.AccountID = account_id
|
||||
|
||||
set_quotas := false
|
||||
arg_value, arg_set = d.GetOk("quotas")
|
||||
if arg_set {
|
||||
log.Printf("resourceResgroupCreate: calling makeQuotaConfig")
|
||||
log.Debugf("resourceResgroupCreate: calling makeQuotaConfig")
|
||||
rg.Quota, _ = makeQuotaConfig(arg_value.([]interface{}))
|
||||
set_quotas = true
|
||||
}
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
log.Printf("resourceResgroupCreate: called by user %q for Resource group name %q, for tenant %q / ID %d, location %q",
|
||||
log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, for account %q / ID %d, location %q",
|
||||
controller.getdecortUsername(),
|
||||
rg.Name, d.Get("tenant"), rg.TenantID, rg.Location)
|
||||
rg.Name, d.Get("account").(string), rg.AccountID, rg.Location)
|
||||
/*
|
||||
type ResgroupCreateParam struct {
|
||||
AccountID int `json:"accountId"`
|
||||
GridId int `json:"gid"`
|
||||
Name string `json:"name"`
|
||||
Ram int `json:"maxMemoryCapacity"`
|
||||
Disk int `json:"maxVDiskCapacity"`
|
||||
Cpu int `json:"maxCPUCapacity"`
|
||||
NetTraffic int `json:"maxNetworkPeerTransfer"`
|
||||
ExtIPs int `json:"maxNumPublicIP"`
|
||||
Owner string `json:"owner"`
|
||||
DefNet string `json:"def_net"`
|
||||
IPCidr string `json:"ipcidr"`
|
||||
Desc string `json:"decs"`
|
||||
Reason string `json:"reason"`
|
||||
ExtNetID int `json:"extNetId"`
|
||||
ExtIP string `json:"extIp"`
|
||||
}
|
||||
*/
|
||||
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("accountId", fmt.Sprintf("%d", rg.TenantID))
|
||||
url_values.Add("accountId", fmt.Sprintf("%d", rg.AccountID))
|
||||
url_values.Add("name", rg.Name)
|
||||
url_values.Add("location", rg.Location)
|
||||
url_values.Add("access", controller.getdecortUsername())
|
||||
url_values.Add("gid", rg.Location)
|
||||
url_values.Add("owner", controller.getdecortUsername())
|
||||
url_values.Add("def_net", "NONE")
|
||||
// pass quota values as set
|
||||
if set_quotas {
|
||||
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", rg.Quota.Cpu))
|
||||
@@ -89,7 +109,7 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
|
||||
// pass externalnetworkid if set
|
||||
arg_value, arg_set = d.GetOk("extnet_id")
|
||||
if arg_set {
|
||||
url_values.Add("externalnetworkid", fmt.Sprintf("%d", arg_value))
|
||||
url_values.Add("extNetId", fmt.Sprintf("%d", arg_value))
|
||||
}
|
||||
|
||||
api_resp, err := controller.decortAPICall("POST", ResgroupCreateAPI, url_values)
|
||||
@@ -97,15 +117,15 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId(api_resp) // cloudspaces/create API plainly returns ID of the newly creted resource group on success
|
||||
d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success
|
||||
rg.ID, _ = strconv.Atoi(api_resp)
|
||||
|
||||
return resourceResgroupRead(d, m)
|
||||
}
|
||||
|
||||
func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
|
||||
log.Printf("resourceResgroupRead: called for res group name %q, tenant name %q",
|
||||
d.Get("name").(string), d.Get("tenant").(string))
|
||||
log.Debugf("resourceResgroupRead: called for RG name %q, account name %q",
|
||||
d.Get("name").(string), d.Get("account").(string))
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
if rg_facts == "" {
|
||||
// if empty string is returned from utilityResgroupCheckPresence then there is no
|
||||
@@ -119,13 +139,13 @@ func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
|
||||
|
||||
func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
|
||||
// this method will only update quotas, if any are set
|
||||
log.Printf("resourceResgroupUpdate: called for res group name %q, tenant name %q",
|
||||
d.Get("name").(string), d.Get("tenant").(string))
|
||||
log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q",
|
||||
d.Get("name").(string), d.Get("account").(string))
|
||||
|
||||
quota_value, arg_set := d.GetOk("quotas")
|
||||
if !arg_set {
|
||||
// if there are no quotas set explicitly in the resource configuration - no change will be done
|
||||
log.Printf("resourceResgroupUpdate: quotas are not set in the resource config - no update on this resource will be done")
|
||||
log.Debugf("resourceResgroupUpdate: quotas are not set in the resource config - no update on this resource will be done")
|
||||
return resourceResgroupRead(d, m)
|
||||
}
|
||||
quotaconfig_new, _ := makeQuotaConfig(quota_value.([]interface{}))
|
||||
@@ -142,66 +162,68 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
|
||||
|
||||
if quotaconfig_new.Cpu != quotaconfig_old.Cpu {
|
||||
do_update = true
|
||||
log.Printf("resourceResgroupUpdate: Cpu diff %d <- %d", quotaconfig_new.Cpu, quotaconfig_old.Cpu)
|
||||
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotaconfig_new.Cpu, quotaconfig_old.Cpu)
|
||||
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotaconfig_new.Cpu))
|
||||
}
|
||||
|
||||
if quotaconfig_new.Disk != quotaconfig_old.Disk {
|
||||
do_update = true
|
||||
log.Printf("resourceResgroupUpdate: Disk diff %d <- %d", quotaconfig_new.Disk, quotaconfig_old.Disk)
|
||||
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotaconfig_new.Disk, quotaconfig_old.Disk)
|
||||
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotaconfig_new.Disk))
|
||||
}
|
||||
|
||||
if quotaconfig_new.Ram != quotaconfig_old.Ram {
|
||||
do_update = true
|
||||
log.Printf("resourceResgroupUpdate: Ram diff %f <- %f", quotaconfig_new.Ram, quotaconfig_old.Ram)
|
||||
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotaconfig_new.Ram, quotaconfig_old.Ram)
|
||||
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotaconfig_new.Ram))
|
||||
}
|
||||
|
||||
if quotaconfig_new.NetTraffic != quotaconfig_old.NetTraffic {
|
||||
do_update = true
|
||||
log.Printf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotaconfig_new.NetTraffic, quotaconfig_old.NetTraffic)
|
||||
log.Debugf("resourceResgroupUpdate: NetTraffic diff %d <- %d", quotaconfig_new.NetTraffic, quotaconfig_old.NetTraffic)
|
||||
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotaconfig_new.NetTraffic))
|
||||
}
|
||||
|
||||
if quotaconfig_new.ExtIPs != quotaconfig_old.ExtIPs {
|
||||
do_update = true
|
||||
log.Printf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotaconfig_new.ExtIPs, quotaconfig_old.ExtIPs)
|
||||
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotaconfig_new.ExtIPs, quotaconfig_old.ExtIPs)
|
||||
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotaconfig_new.ExtIPs))
|
||||
}
|
||||
|
||||
if do_update {
|
||||
log.Printf("resourceResgroupUpdate: some new quotas are set - updating the resource")
|
||||
log.Debugf("resourceResgroupUpdate: some new quotas are set - updating the resource")
|
||||
_, err := controller.decortAPICall("POST", ResgroupUpdateAPI, url_values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("resourceResgroupUpdate: no difference in quotas between old and new state - no update on this resource will be done")
|
||||
log.Debugf("resourceResgroupUpdate: no difference in quotas between old and new state - no update on this resource will be done")
|
||||
}
|
||||
|
||||
return resourceResgroupRead(d, m)
|
||||
}
|
||||
|
||||
func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error {
|
||||
// NOTE: this method destroys target resource group with flag "permanently", so there is no way to
|
||||
// restore the destroyed resource group as well all VMs that existed in it
|
||||
log.Printf("resourceResgroupDelete: called for res group name %q, tenant name %q",
|
||||
d.Get("name").(string), d.Get("tenant").(string))
|
||||
// NOTE: this method forcibly destroys target resource group with flag "permanently", so there is no way to
|
||||
// restore the destroyed resource group as well all Computes & VINSes that existed in it
|
||||
log.Debugf("resourceResgroupDelete: called for RG name %q, account name %q",
|
||||
d.Get("name").(string), d.Get("account").(string))
|
||||
|
||||
vm_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
if vm_facts == "" {
|
||||
// the target VM does not exist - in this case according to Terraform best practice
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
if rg_facts == "" {
|
||||
// the target RG does not exist - in this case according to Terraform best practice
|
||||
// we exit from Destroy method without error
|
||||
return nil
|
||||
}
|
||||
|
||||
params := &url.Values{}
|
||||
params.Add("cloudspaceId", d.Id())
|
||||
params.Add("rgId", d.Id())
|
||||
params.Add("force", "true")
|
||||
params.Add("permanently", "true")
|
||||
params.Add("reason", "Destroyed by DECORT Terraform provider")
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
vm_facts, err = controller.decortAPICall("POST", CloudspacesDeleteAPI, params)
|
||||
vm_facts, err = controller.decortAPICall("POST", ResgroupDeleteAPI, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -243,13 +265,13 @@ func resourceResgroup() *schema.Resource {
|
||||
"name": &schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Name of this resource group. Names are case sensitive and unique within the context of a tenant.",
|
||||
Description: "Name of this resource group. Names are case sensitive and unique within the context of a account.",
|
||||
},
|
||||
|
||||
"tenant": &schema.Schema {
|
||||
"account": &schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Name of the tenant, which this resource group belongs to.",
|
||||
Description: "Name of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"extnet_id": &schema.Schema {
|
||||
@@ -258,31 +280,18 @@ func resourceResgroup() *schema.Resource {
|
||||
Description: "ID of the external network, which this resource group will be connected to by default.",
|
||||
},
|
||||
|
||||
"tenant_id": &schema.Schema {
|
||||
"account_id": &schema.Schema {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Unique ID of the tenant, which this resource group belongs to.",
|
||||
Description: "Unique ID of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"grid_id": &schema.Schema {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Required: true,
|
||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
||||
},
|
||||
|
||||
"location": &schema.Schema {
|
||||
Type: schema.TypeString,
|
||||
Optional: true, // note that it is a REQUIRED parameter when creating new resource group
|
||||
ForceNew: true,
|
||||
Description: "Name of the location where this resource group should exist.",
|
||||
},
|
||||
|
||||
"public_ip": { // this may be obsoleted as new network segments and true resource groups are implemented
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Public IP address of this resource group (if any).",
|
||||
},
|
||||
|
||||
"quotas": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
|
||||
@@ -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>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -36,23 +36,24 @@ import (
|
||||
// "github.com/hashicorp/terraform/helper/validation"
|
||||
)
|
||||
|
||||
func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupConfig, error) {
|
||||
func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp, error) {
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("cloudspaceId", fmt.Sprintf("%d", rgid))
|
||||
resgroup_facts, err := ctrl.decortAPICall("POST", CloudspacesGetAPI, url_values)
|
||||
url_values.Add("rgId", fmt.Sprintf("%d", rgid))
|
||||
resgroup_facts, err := ctrl.decortAPICall("POST", ResgroupGetAPI, url_values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("utilityResgroupConfigGet: ready to unmarshal string %q", resgroup_facts)
|
||||
model := CloudspacesGetResp{}
|
||||
err = json.Unmarshal([]byte(resgroup_facts), &model)
|
||||
log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %q", resgroup_facts)
|
||||
model := &ResgroupGetResp{}
|
||||
err = json.Unmarshal([]byte(resgroup_facts), model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
ret := &ResgroupConfig{}
|
||||
ret.TenantID = model.TenantID
|
||||
ret.AccountID = model.AccountID
|
||||
ret.Location = model.Location
|
||||
ret.Name = model.Name
|
||||
ret.ID = rgid
|
||||
@@ -60,53 +61,58 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupConfig,
|
||||
ret.ExtIP = model.ExtIP // legacy field for VDC - this will eventually become obsoleted by true Resource Groups
|
||||
// Quota ResgroupQuotaConfig
|
||||
// Network NetworkConfig
|
||||
log.Printf("utilityResgroupConfigGet: tenant ID %d, GridID %d, ExtIP %q",
|
||||
model.TenantID, model.GridID, model.ExtIP)
|
||||
*/
|
||||
log.Debugf("utilityResgroupConfigGet: account ID %d, GridID %d, Name %s",
|
||||
model.AccountID, model.GridID, model.Name)
|
||||
|
||||
return ret, nil
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
||||
// This function tries to locate resource group by its name and tenant name.
|
||||
// This function tries to locate resource group by its name and account name.
|
||||
// If succeeded, it returns non empty string that contains JSON formatted facts about the
|
||||
// resource group as returned by cloudspaces/get API call.
|
||||
// Otherwise it returns empty string and meaningful error.
|
||||
//
|
||||
// NOTE: As our provider always deletes RGs permanently, there is no "restore" method and
|
||||
// consequently we are not interested in matching RGs in DELETED state. Hence, we call
|
||||
// .../rg/list API with includedeleted=false
|
||||
//
|
||||
// This function does not modify its ResourceData argument, so it is safe to use it as core
|
||||
// method for the resource's Exists method.
|
||||
//
|
||||
name := d.Get("name").(string)
|
||||
tenant_name := d.Get("tenant").(string)
|
||||
account_name := d.Get("account").(string)
|
||||
|
||||
controller := m.(*ControllerCfg)
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("includedeleted", "false")
|
||||
body_string, err := controller.decortAPICall("POST", CloudspacesListAPI, url_values)
|
||||
body_string, err := controller.decortAPICall("POST", ResgroupListAPI, url_values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Printf("%s", body_string)
|
||||
log.Printf("utilityResgroupCheckPresence: ready to decode response body from %q", CloudspacesListAPI)
|
||||
log.Debugf("%s", body_string)
|
||||
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %q", ResgroupListAPI)
|
||||
model := CloudspacesListResp{}
|
||||
err = json.Unmarshal([]byte(body_string), &model)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Printf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
|
||||
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
|
||||
for index, item := range model {
|
||||
// need to match VDC by name & tenant name
|
||||
if item.Name == name && item.TenantName == tenant_name {
|
||||
log.Printf("utilityResgroupCheckPresence: match ResGroup name %q / ID %d, tenant %q at index %d",
|
||||
item.Name, item.ID, item.TenantName, index)
|
||||
// need to match RG by name & account name
|
||||
if item.Name == name && item.AccountName == account_name {
|
||||
log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account %q at index %d",
|
||||
item.Name, item.ID, item.AccountName, index)
|
||||
|
||||
// not all required information is returned by cloudspaces/list API, so we need to initiate one more
|
||||
// call to cloudspaces/get to obtain extra data to complete Resource population.
|
||||
// not all required information is returned by rg/list API, so we need to initiate one more
|
||||
// call to rg/get to obtain extra data to complete Resource population.
|
||||
// Namely, we need to extract resource quota settings
|
||||
req_values := &url.Values{}
|
||||
req_values.Add("cloudspaceId", fmt.Sprintf("%d", item.ID))
|
||||
body_string, err := controller.decortAPICall("POST", CloudspacesGetAPI, req_values)
|
||||
req_values.Add("rgId", fmt.Sprintf("%d", item.ID))
|
||||
body_string, err := controller.decortAPICall("POST", ResgroupGetAPI, req_values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -115,32 +121,32 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Cannot find resource group name %q owned by tenant %q", name, tenant_name)
|
||||
return "", fmt.Errorf("Cannot find RG name %q owned by account %q", name, account_name)
|
||||
}
|
||||
|
||||
func utilityGetTenantIdByName(tenant_name string, m interface{}) (int, error) {
|
||||
func utilityGetAccountIdByName(account_name string, m interface{}) (int, error) {
|
||||
controller := m.(*ControllerCfg)
|
||||
url_values := &url.Values{}
|
||||
body_string, err := controller.decortAPICall("POST", TenantsListAPI, url_values)
|
||||
body_string, err := controller.decortAPICall("POST", AccountsListAPI, url_values)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
model := TenantsListResp{}
|
||||
model := AccountsListResp{}
|
||||
err = json.Unmarshal([]byte(body_string), &model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
log.Printf("utilityGetTenantIdByName: traversing decoded Json of length %d", len(model))
|
||||
log.Debugf("utilityGetAccountIdByName: traversing decoded Json of length %d", len(model))
|
||||
for index, item := range model {
|
||||
// need to match Tenant by name
|
||||
if item.Name == tenant_name {
|
||||
log.Printf("utilityGetTenantIdByName: match Tenant name %q / ID %d at index %d",
|
||||
// need to match Account by name
|
||||
if item.Name == account_name {
|
||||
log.Debugf("utilityGetAccountIdByName: match Account name %q / ID %d at index %d",
|
||||
item.Name, item.ID, index)
|
||||
return item.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("Cannot find tenant %q for the current user. Check tenant value and your access rights", tenant_name)
|
||||
return 0, fmt.Errorf("Cannot find account %q for the current user. Check account value and your access rights", account_name)
|
||||
}
|
||||
Reference in New Issue
Block a user