Migrate to sdkv2 and project structure refactoring
This commit is contained in:
39
internal/service/cloudapi/rg/api.go
Normal file
39
internal/service/cloudapi/rg/api.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
const ResgroupCreateAPI = "/restmachine/cloudapi/rg/create"
|
||||
const ResgroupUpdateAPI = "/restmachine/cloudapi/rg/update"
|
||||
const ResgroupListAPI = "/restmachine/cloudapi/rg/list"
|
||||
const ResgroupGetAPI = "/restmachine/cloudapi/rg/get"
|
||||
const ResgroupDeleteAPI = "/restmachine/cloudapi/rg/delete"
|
||||
const RgListComputesAPI = "/restmachine/cloudapi/rg/listComputes"
|
||||
197
internal/service/cloudapi/rg/data_source_rg.go
Normal file
197
internal/service/cloudapi/rg/data_source_rg.go
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/rudecs/terraform-provider-decort/internal/constants"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
// "net/url"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
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.Debugf("%s", rg_facts)
|
||||
log.Debugf("flattenResgroup: ready to decode response body from API")
|
||||
details := ResgroupGetResp{}
|
||||
err := json.Unmarshal([]byte(rg_facts), &details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("flattenResgroup: decoded RG name %q / ID %d, account ID %d",
|
||||
details.Name, details.ID, details.AccountID)
|
||||
|
||||
d.SetId(fmt.Sprintf("%d", details.ID))
|
||||
d.Set("rg_id", details.ID)
|
||||
d.Set("name", details.Name)
|
||||
d.Set("account_name", details.AccountName)
|
||||
d.Set("account_id", details.AccountID)
|
||||
// d.Set("grid_id", details.GridID)
|
||||
d.Set("description", details.Desc)
|
||||
d.Set("status", details.Status)
|
||||
d.Set("def_net_type", details.DefaultNetType)
|
||||
d.Set("def_net_id", details.DefaultNetID)
|
||||
/*
|
||||
d.Set("vins", details.Vins)
|
||||
d.Set("computes", details.Computes)
|
||||
*/
|
||||
|
||||
log.Debugf("flattenResgroup: calling flattenQuota()")
|
||||
if err = d.Set("quota", parseQuota(details.Quota)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dataSourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
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 in this case
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return diag.FromErr(flattenResgroup(d, rg_facts))
|
||||
}
|
||||
|
||||
func DataSourceResgroup() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
|
||||
ReadContext: dataSourceResgroupRead,
|
||||
|
||||
Timeouts: &schema.ResourceTimeout{
|
||||
Read: &constants.Timeout30s,
|
||||
Default: &constants.Timeout60s,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Name of the resource group. Names are case sensitive and unique within the context of an account.",
|
||||
},
|
||||
|
||||
"rg_id": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "Unique ID of the resource group. If this ID is specified, then resource group name is ignored.",
|
||||
},
|
||||
|
||||
"account_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Name of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"account_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
Description: "Unique ID of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "User-defined text description of this resource group.",
|
||||
},
|
||||
|
||||
/* commented out, as in this version of provider we use default Grid ID
|
||||
"grid_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
||||
},
|
||||
*/
|
||||
|
||||
"quota": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: quotaRgSubresourceSchemaMake(), // this is a dictionary
|
||||
},
|
||||
Description: "Quota settings for this resource group.",
|
||||
},
|
||||
|
||||
"def_net_type": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Type of the default network for this resource group.",
|
||||
},
|
||||
|
||||
"def_net_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "ID of the default network for this resource group (if any).",
|
||||
},
|
||||
|
||||
/*
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Current status of this resource group.",
|
||||
},
|
||||
|
||||
"vins": {
|
||||
Type: schema.TypeList, // this is a list of ints
|
||||
Computed: true,
|
||||
MaxItems: LimitMaxVinsPerResgroup,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
Description: "List of VINs deployed in this resource group.",
|
||||
},
|
||||
|
||||
"computes": {
|
||||
Type: schema.TypeList, //t his is a list of ints
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
Description: "List of computes deployed in this resource group.",
|
||||
},
|
||||
*/
|
||||
},
|
||||
}
|
||||
}
|
||||
325
internal/service/cloudapi/rg/data_source_rg_list.go
Normal file
325
internal/service/cloudapi/rg/data_source_rg_list.go
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/rudecs/terraform-provider-decort/internal/constants"
|
||||
)
|
||||
|
||||
func flattenRgList(rgl ResgroupListResp) []map[string]interface{} {
|
||||
res := make([]map[string]interface{}, 0)
|
||||
for _, rg := range rgl {
|
||||
temp := map[string]interface{}{
|
||||
"account_id": rg.AccountID,
|
||||
"account_name": rg.AccountName,
|
||||
"acl": flattenRgAcl(rg.ACLs),
|
||||
"created_by": rg.CreatedBy,
|
||||
"created_time": rg.CreatedTime,
|
||||
"def_net_id": rg.DefaultNetID,
|
||||
"def_net_type": rg.DefaultNetType,
|
||||
"deleted_by": rg.DeletedBy,
|
||||
"deleted_time": rg.DeletedTime,
|
||||
"desc": rg.Decsription,
|
||||
"gid": rg.GridID,
|
||||
"guid": rg.GUID,
|
||||
"rg_id": rg.ID,
|
||||
"lock_status": rg.LockStatus,
|
||||
"milestones": rg.Milestones,
|
||||
"name": rg.Name,
|
||||
"register_computes": rg.RegisterComputes,
|
||||
"resource_limits": flattenRgResourceLimits(rg.ResourceLimits),
|
||||
"secret": rg.Secret,
|
||||
"status": rg.Status,
|
||||
"updated_by": rg.UpdatedBy,
|
||||
"updated_time": rg.UpdatedTime,
|
||||
"vins": rg.Vins,
|
||||
"vms": rg.Computes,
|
||||
}
|
||||
res = append(res, temp)
|
||||
}
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
func flattenRgAcl(rgAcls []AccountAclRecord) []map[string]interface{} {
|
||||
res := make([]map[string]interface{}, 0)
|
||||
for _, rgAcl := range rgAcls {
|
||||
temp := map[string]interface{}{
|
||||
"explicit": rgAcl.IsExplicit,
|
||||
"guid": rgAcl.Guid,
|
||||
"right": rgAcl.Rights,
|
||||
"status": rgAcl.Status,
|
||||
"type": rgAcl.Type,
|
||||
"user_group_id": rgAcl.UgroupID,
|
||||
}
|
||||
res = append(res, temp)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func flattenRgResourceLimits(rl ResourceLimits) []map[string]interface{} {
|
||||
res := make([]map[string]interface{}, 0)
|
||||
temp := map[string]interface{}{
|
||||
"cu_c": rl.CUC,
|
||||
"cu_d": rl.CUD,
|
||||
"cu_i": rl.CUI,
|
||||
"cu_m": rl.CUM,
|
||||
"cu_np": rl.CUNP,
|
||||
"gpu_units": rl.GpuUnits,
|
||||
}
|
||||
res = append(res, temp)
|
||||
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
func dataSourceRgListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
rgList, err := utilityRgListCheckPresence(d, m)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
d.SetId(id.String())
|
||||
d.Set("items", flattenRgList(rgList))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dataSourceRgListSchemaMake() map[string]*schema.Schema {
|
||||
res := map[string]*schema.Schema{
|
||||
"includedeleted": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
Description: "included deleted resource groups",
|
||||
},
|
||||
"page": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "Page number",
|
||||
},
|
||||
"size": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "Page size",
|
||||
},
|
||||
"items": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"account_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"account_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"acl": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"explicit": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
},
|
||||
"guid": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"right": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"type": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"user_group_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"created_by": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"created_time": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"def_net_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"def_net_type": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"deleted_by": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"deleted_time": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"desc": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"gid": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"guid": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"rg_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"lock_status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"milestones": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"register_computes": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
},
|
||||
"resource_limits": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cu_c": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
"cu_d": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
"cu_i": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
"cu_m": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
"cu_np": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
"gpu_units": {
|
||||
Type: schema.TypeFloat,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"secret": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"updated_by": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"updated_time": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"vins": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
},
|
||||
"vms": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func DataSourceRgList() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
|
||||
ReadContext: dataSourceRgListRead,
|
||||
|
||||
Timeouts: &schema.ResourceTimeout{
|
||||
Read: &constants.Timeout30s,
|
||||
Default: &constants.Timeout60s,
|
||||
},
|
||||
|
||||
Schema: dataSourceRgListSchemaMake(),
|
||||
}
|
||||
}
|
||||
149
internal/service/cloudapi/rg/models.go
Normal file
149
internal/service/cloudapi/rg/models.go
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
type ResourceLimits struct {
|
||||
CUC float64 `json:"CU_C"`
|
||||
CUD float64 `json:"CU_D"`
|
||||
CUI float64 `json:"CU_I"`
|
||||
CUM float64 `json:"CU_M"`
|
||||
CUNP float64 `json:"CU_NP"`
|
||||
GpuUnits float64 `json:"gpu_units"`
|
||||
}
|
||||
|
||||
type ResgroupRecord struct {
|
||||
ACLs []AccountAclRecord `json:"acl"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreatedTime uint64 `json:"createdTime"`
|
||||
DefaultNetID int `json:"def_net_id"`
|
||||
DefaultNetType string `json:"def_net_type"`
|
||||
DeletedBy string `json:"deletedBy"`
|
||||
DeletedTime int `json:"deletedTime"`
|
||||
Decsription string `json:"desc"`
|
||||
GridID int `json:"gid"`
|
||||
GUID int `json:"guid"`
|
||||
ID uint `json:"id"`
|
||||
LockStatus string `json:"lockStatus"`
|
||||
Milestones int `json:"milestones"`
|
||||
Name string `json:"name"`
|
||||
RegisterComputes bool `json:"registerComputes"`
|
||||
ResourceLimits ResourceLimits `json:"resourceLimits"`
|
||||
Secret string `json:"secret"`
|
||||
Status string `json:"status"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
UpdatedTime uint64 `json:"updatedTime"`
|
||||
Vins []int `json:"vins"`
|
||||
Computes []int `json:"vms"`
|
||||
}
|
||||
|
||||
type ResgroupListResp []ResgroupRecord
|
||||
|
||||
type ResgroupUpdateParam struct {
|
||||
RgId int `json:"rgId"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"decs"`
|
||||
Ram int `json:"maxMemoryCapacity"`
|
||||
Disk int `json:"maxVDiskCapacity"`
|
||||
Cpu int `json:"maxCPUCapacity"`
|
||||
NetTraffic int `json:"maxNetworkPeerTransfer"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type AccountAclRecord struct {
|
||||
IsExplicit bool `json:"explicit"`
|
||||
Guid string `json:"guid"`
|
||||
Rights string `json:"right"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
UgroupID string `json:"userGroupId"`
|
||||
CanBeDeleted bool `json:"canBeDeleted"`
|
||||
}
|
||||
|
||||
type ResgroupGetResp struct {
|
||||
ACLs []UserAclRecord `json:"ACLs"`
|
||||
Usage UsageRecord `json:"Resources"`
|
||||
AccountID int `json:"accountId"`
|
||||
AccountName string `json:"accountName"`
|
||||
GridID int `json:"gid"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreatedTime uint64 `json:"createdTime"`
|
||||
DefaultNetID int `json:"def_net_id"`
|
||||
DefaultNetType string `json:"def_net_type"`
|
||||
DeletedBy string `json:"deletedBy"`
|
||||
DeletedTime uint64 `json:"deletedTime"`
|
||||
Desc string `json:"desc"`
|
||||
ID uint `json:"id"`
|
||||
LockStatus string `json:"lockStatus"`
|
||||
Name string `json:"name"`
|
||||
Quota QuotaRecord `json:"resourceLimits"`
|
||||
Status string `json:"status"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
UpdatedTime uint64 `json:"updatedTime"`
|
||||
Vins []int `json:"vins"`
|
||||
Computes []int `json:"vms"`
|
||||
|
||||
Ignored map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
type UserAclRecord struct {
|
||||
IsExplicit bool `json:"explicit"`
|
||||
Rights string `json:"right"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
UgroupID string `json:"userGroupId"`
|
||||
// CanBeDeleted bool `json:"canBeDeleted"`
|
||||
}
|
||||
|
||||
type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get
|
||||
Cpu int `json:"CU_C"` // CPU count in pcs
|
||||
Ram float64 `json:"CU_M"` // RAM volume in MB, it is STILL reported as FLOAT
|
||||
Disk int `json:"CU_D"` // Disk capacity in GB
|
||||
ExtIPs int `json:"CU_I"` // Ext IPs count
|
||||
ExtTraffic int `json:"CU_NP"` // Ext network traffic
|
||||
GpuUnits int `json:"gpu_units"` // GPU count
|
||||
}
|
||||
|
||||
type ResourceRecord struct { // this is how actual usage is reported by /api/.../rg/get
|
||||
Cpu int `json:"cpu"`
|
||||
Disk int `json:"disksize"`
|
||||
ExtIPs int `json:"extips"`
|
||||
ExtTraffic int `json:"exttraffic"`
|
||||
Gpu int `json:"gpu"`
|
||||
Ram int `json:"ram"`
|
||||
}
|
||||
|
||||
type UsageRecord struct {
|
||||
Current ResourceRecord `json:"Current"`
|
||||
Reserved ResourceRecord `json:"Reserved"`
|
||||
}
|
||||
137
internal/service/cloudapi/rg/quota_subresource.go
Normal file
137
internal/service/cloudapi/rg/quota_subresource.go
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
func makeQuotaRecord(arg_list []interface{}) QuotaRecord {
|
||||
quota := QuotaRecord{
|
||||
Cpu: -1,
|
||||
Ram: -1., // this is float64, but may change in the future
|
||||
Disk: -1,
|
||||
ExtTraffic: -1,
|
||||
ExtIPs: -1,
|
||||
GpuUnits: -1,
|
||||
}
|
||||
subres_data := arg_list[0].(map[string]interface{})
|
||||
|
||||
if subres_data["cpu"].(int) > 0 {
|
||||
quota.Cpu = subres_data["cpu"].(int)
|
||||
}
|
||||
|
||||
if subres_data["disk"].(int) > 0 {
|
||||
quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB
|
||||
}
|
||||
|
||||
if subres_data["ram"].(float64) > 0 {
|
||||
quota.Ram = subres_data["ram"].(float64) // RAM volume in MB, as float64!
|
||||
}
|
||||
|
||||
if subres_data["ext_traffic"].(int) > 0 {
|
||||
quota.ExtTraffic = subres_data["ext_traffic"].(int)
|
||||
}
|
||||
|
||||
if subres_data["ext_ips"].(int) > 0 {
|
||||
quota.ExtIPs = subres_data["ext_ips"].(int)
|
||||
}
|
||||
|
||||
if subres_data["gpu_units"].(int) > 0 {
|
||||
quota.GpuUnits = subres_data["gpu_units"].(int)
|
||||
}
|
||||
|
||||
return quota
|
||||
}
|
||||
|
||||
func parseQuota(quota QuotaRecord) []interface{} {
|
||||
quota_map := make(map[string]interface{})
|
||||
|
||||
quota_map["cpu"] = quota.Cpu
|
||||
quota_map["ram"] = quota.Ram // NB: this is float64, unlike the rest of values
|
||||
quota_map["disk"] = quota.Disk
|
||||
quota_map["ext_traffic"] = quota.ExtTraffic
|
||||
quota_map["ext_ips"] = quota.ExtIPs
|
||||
quota_map["gpu_units"] = quota.GpuUnits
|
||||
|
||||
result := make([]interface{}, 1)
|
||||
result[0] = quota_map
|
||||
|
||||
return result // this result will be used to d.Set("quota,") of dataSourceResgroup schema
|
||||
}
|
||||
|
||||
func quotaRgSubresourceSchemaMake() map[string]*schema.Schema {
|
||||
rets := map[string]*schema.Schema{
|
||||
"cpu": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: -1,
|
||||
Description: "Limit on the total number of CPUs in this resource group.",
|
||||
},
|
||||
|
||||
"ram": {
|
||||
Type: schema.TypeFloat, // NB: API expects and returns this as float in units of MB! This may be changed in the future.
|
||||
Optional: true,
|
||||
Default: -1.,
|
||||
Description: "Limit on the total amount of RAM in this resource group, specified in MB.",
|
||||
},
|
||||
|
||||
"disk": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: -1,
|
||||
Description: "Limit on the total volume of storage resources in this resource group, specified in GB.",
|
||||
},
|
||||
|
||||
"ext_traffic": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: -1,
|
||||
Description: "Limit on the total ingress network traffic for this resource group, specified in GB.",
|
||||
},
|
||||
|
||||
"ext_ips": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: -1,
|
||||
Description: "Limit on the total number of external IP addresses this resource group can use.",
|
||||
},
|
||||
|
||||
"gpu_units": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: -1,
|
||||
Description: "Limit on the total number of virtual GPUs this resource group can use.",
|
||||
},
|
||||
}
|
||||
return rets
|
||||
}
|
||||
443
internal/service/cloudapi/rg/resource_rg.go
Normal file
443
internal/service/cloudapi/rg/resource_rg.go
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/rudecs/terraform-provider-decort/internal/constants"
|
||||
"github.com/rudecs/terraform-provider-decort/internal/controller"
|
||||
"github.com/rudecs/terraform-provider-decort/internal/location"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||
)
|
||||
|
||||
func resourceResgroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
// First validate that we have all parameters required to create the new Resource Group
|
||||
|
||||
// Valid account ID is required to create new resource group
|
||||
// obtain Account ID by account name - it should not be zero on success
|
||||
|
||||
rg_name, arg_set := d.GetOk("name")
|
||||
if !arg_set {
|
||||
return diag.FromErr(fmt.Errorf("Cannot create new RG: missing name."))
|
||||
}
|
||||
|
||||
/* Current version of provider works with default grid id (same is true for disk resources)
|
||||
grid_id, arg_set := d.GetOk("grid_id")
|
||||
if !arg_set {
|
||||
return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.",
|
||||
rg_name.(string), validated_account_id)
|
||||
}
|
||||
if grid_id.(int) < 1 {
|
||||
grid_id = DefaultGridID
|
||||
}
|
||||
*/
|
||||
|
||||
// all required parameters are set in the schema - we can continue with RG creation
|
||||
log.Debugf("resourceResgroupCreate: called for RG name %s, account ID %d",
|
||||
rg_name.(string), d.Get("account_id").(int))
|
||||
|
||||
// quota settings are optional
|
||||
set_quota := false
|
||||
var quota_record QuotaRecord
|
||||
arg_value, arg_set := d.GetOk("quota")
|
||||
if arg_set {
|
||||
log.Debugf("resourceResgroupCreate: setting Quota on RG requested")
|
||||
quota_record = makeQuotaRecord(arg_value.([]interface{}))
|
||||
set_quota = true
|
||||
}
|
||||
|
||||
c := m.(*controller.ControllerCfg)
|
||||
log.Debugf("resourceResgroupCreate: called by user %q for RG name %s, account ID %d",
|
||||
c.GetDecortUsername(),
|
||||
rg_name.(string), d.Get("account_id").(int))
|
||||
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("accountId", fmt.Sprintf("%d", d.Get("account_id").(int)))
|
||||
url_values.Add("name", rg_name.(string))
|
||||
url_values.Add("gid", fmt.Sprintf("%d", location.DefaultGridID)) // use default Grid ID, similar to disk resource mgmt convention
|
||||
url_values.Add("owner", c.GetDecortUsername())
|
||||
|
||||
// pass quota values as set
|
||||
if set_quota {
|
||||
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quota_record.Cpu))
|
||||
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quota_record.Disk))
|
||||
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quota_record.Ram)) // RAM quota is float; this may change in the future
|
||||
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quota_record.ExtTraffic))
|
||||
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quota_record.ExtIPs))
|
||||
// url_values.Add("???", fmt.Sprintf("%d", quota_record.GpuUnits))
|
||||
}
|
||||
|
||||
// parse and handle network settings
|
||||
def_net_type, arg_set := d.GetOk("def_net_type")
|
||||
if arg_set {
|
||||
url_values.Add("def_net", def_net_type.(string)) // NOTE: in API default network type is set by "def_net" parameter
|
||||
}
|
||||
|
||||
ipcidr, arg_set := d.GetOk("ipcidr")
|
||||
if arg_set {
|
||||
url_values.Add("ipcidr", ipcidr.(string))
|
||||
}
|
||||
|
||||
ext_net_id, arg_set := d.GetOk("ext_net_id")
|
||||
if arg_set {
|
||||
url_values.Add("extNetId", fmt.Sprintf("%d", ext_net_id.(int)))
|
||||
}
|
||||
|
||||
ext_ip, arg_set := d.GetOk("ext_ip")
|
||||
if arg_set {
|
||||
url_values.Add("extIp", ext_ip.(string))
|
||||
}
|
||||
|
||||
api_resp, err := c.DecortAPICall("POST", ResgroupCreateAPI, url_values)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
d.SetId(api_resp) // rg/create API returns ID of the newly creted resource group on success
|
||||
// rg.ID, _ = strconv.Atoi(api_resp)
|
||||
if !set_quota {
|
||||
resp, err := utilityResgroupCheckPresence(d, m)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
rg := ResgroupGetResp{}
|
||||
if err := json.Unmarshal([]byte(resp), &rg); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
d.Set("quota", parseQuota(rg.Quota))
|
||||
}
|
||||
|
||||
// re-read newly created RG to make sure schema contains complete and up to date set of specifications
|
||||
return resourceResgroupRead(ctx, d, m)
|
||||
}
|
||||
|
||||
func resourceResgroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
log.Debugf("resourceResgroupRead: called for RG name %s, account ID %d",
|
||||
d.Get("name").(string), d.Get("account_id").(int))
|
||||
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
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
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return diag.FromErr(flattenResgroup(d, rg_facts))
|
||||
}
|
||||
|
||||
func resourceResgroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
log.Debugf("resourceResgroupUpdate: called for RG name %s, account ID %d",
|
||||
d.Get("name").(string), d.Get("account_id").(int))
|
||||
|
||||
/* NOTE: we do not allow changing the following attributes of an existing RG via terraform:
|
||||
- def_net_type
|
||||
- ipcidr
|
||||
- ext_net_id
|
||||
- ext_ip
|
||||
|
||||
The following code fragment checks if any of these have been changed and generates error.
|
||||
*/
|
||||
for _, attr := range []string{"def_net_type", "ipcidr", "ext_ip"} {
|
||||
attr_new, attr_old := d.GetChange("def_net_type")
|
||||
if attr_new.(string) != attr_old.(string) {
|
||||
return diag.FromErr(fmt.Errorf("resourceResgroupUpdate: RG ID %s: changing %s for existing RG is not allowed", d.Id(), attr))
|
||||
}
|
||||
}
|
||||
|
||||
attr_new, attr_old := d.GetChange("ext_net_id")
|
||||
if attr_new.(int) != attr_old.(int) {
|
||||
return diag.FromErr(fmt.Errorf("resourceResgroupUpdate: RG ID %s: changing ext_net_id for existing RG is not allowed", d.Id()))
|
||||
}
|
||||
|
||||
do_general_update := false // will be true if general RG update is necessary (API rg/update)
|
||||
|
||||
c := m.(*controller.ControllerCfg)
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("rgId", d.Id())
|
||||
|
||||
name_new, name_set := d.GetOk("name")
|
||||
if name_set {
|
||||
log.Debugf("resourceResgroupUpdate: name specified - looking for deltas from the old settings.")
|
||||
name_old, _ := d.GetChange("name")
|
||||
if name_old.(string) != name_new.(string) {
|
||||
do_general_update = true
|
||||
url_values.Add("name", name_new.(string))
|
||||
}
|
||||
}
|
||||
|
||||
quota_value, quota_set := d.GetOk("quota")
|
||||
if quota_set {
|
||||
log.Debugf("resourceResgroupUpdate: quota specified - looking for deltas from the old quota.")
|
||||
quotarecord_new := makeQuotaRecord(quota_value.([]interface{}))
|
||||
quota_value_old, _ := d.GetChange("quota") // returns old as 1st, new as 2nd return value
|
||||
quotarecord_old := makeQuotaRecord(quota_value_old.([]interface{}))
|
||||
|
||||
if quotarecord_new.Cpu != quotarecord_old.Cpu {
|
||||
do_general_update = true
|
||||
log.Debugf("resourceResgroupUpdate: Cpu diff %d <- %d", quotarecord_new.Cpu, quotarecord_old.Cpu)
|
||||
url_values.Add("maxCPUCapacity", fmt.Sprintf("%d", quotarecord_new.Cpu))
|
||||
}
|
||||
|
||||
if quotarecord_new.Disk != quotarecord_old.Disk {
|
||||
do_general_update = true
|
||||
log.Debugf("resourceResgroupUpdate: Disk diff %d <- %d", quotarecord_new.Disk, quotarecord_old.Disk)
|
||||
url_values.Add("maxVDiskCapacity", fmt.Sprintf("%d", quotarecord_new.Disk))
|
||||
}
|
||||
|
||||
if quotarecord_new.Ram != quotarecord_old.Ram { // NB: quota on RAM is stored as float32, in units of MB
|
||||
do_general_update = true
|
||||
log.Debugf("resourceResgroupUpdate: Ram diff %f <- %f", quotarecord_new.Ram, quotarecord_old.Ram)
|
||||
url_values.Add("maxMemoryCapacity", fmt.Sprintf("%f", quotarecord_new.Ram))
|
||||
}
|
||||
|
||||
if quotarecord_new.ExtTraffic != quotarecord_old.ExtTraffic {
|
||||
do_general_update = true
|
||||
log.Debugf("resourceResgroupUpdate: ExtTraffic diff %d <- %d", quotarecord_new.ExtTraffic, quotarecord_old.ExtTraffic)
|
||||
url_values.Add("maxNetworkPeerTransfer", fmt.Sprintf("%d", quotarecord_new.ExtTraffic))
|
||||
}
|
||||
|
||||
if quotarecord_new.ExtIPs != quotarecord_old.ExtIPs {
|
||||
do_general_update = true
|
||||
log.Debugf("resourceResgroupUpdate: ExtIPs diff %d <- %d", quotarecord_new.ExtIPs, quotarecord_old.ExtIPs)
|
||||
url_values.Add("maxNumPublicIP", fmt.Sprintf("%d", quotarecord_new.ExtIPs))
|
||||
}
|
||||
}
|
||||
|
||||
desc_new, desc_set := d.GetOk("description")
|
||||
if desc_set {
|
||||
log.Debugf("resourceResgroupUpdate: description specified - looking for deltas from the old settings.")
|
||||
desc_old, _ := d.GetChange("description")
|
||||
if desc_old.(string) != desc_new.(string) {
|
||||
do_general_update = true
|
||||
url_values.Add("desc", desc_new.(string))
|
||||
}
|
||||
}
|
||||
|
||||
if do_general_update {
|
||||
log.Debugf("resourceResgroupUpdate: detected delta between new and old RG specs - updating the RG")
|
||||
_, err := c.DecortAPICall("POST", ResgroupUpdateAPI, url_values)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("resourceResgroupUpdate: no difference between old and new state - no update on the RG will be done")
|
||||
}
|
||||
|
||||
return resourceResgroupRead(ctx, d, m)
|
||||
}
|
||||
|
||||
func resourceResgroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||||
// 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 %s, account ID %d",
|
||||
d.Get("name").(string), d.Get("account_id").(int))
|
||||
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
if rg_facts == "" {
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
// the target RG does not exist - in this case according to Terraform best practice
|
||||
// we exit from Destroy method without error
|
||||
return nil
|
||||
}
|
||||
|
||||
url_values := &url.Values{}
|
||||
url_values.Add("rgId", d.Id())
|
||||
url_values.Add("force", "1")
|
||||
url_values.Add("permanently", "1")
|
||||
url_values.Add("reason", "Destroyed by DECORT Terraform provider")
|
||||
|
||||
c := m.(*controller.ControllerCfg)
|
||||
_, err = c.DecortAPICall("POST", ResgroupDeleteAPI, url_values)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceResgroupExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
||||
// Reminder: according to Terraform rules, this function should NOT modify ResourceData argument
|
||||
rg_facts, err := utilityResgroupCheckPresence(d, m)
|
||||
if rg_facts == "" {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func ResourceResgroup() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
|
||||
CreateContext: resourceResgroupCreate,
|
||||
ReadContext: resourceResgroupRead,
|
||||
UpdateContext: resourceResgroupUpdate,
|
||||
DeleteContext: resourceResgroupDelete,
|
||||
Exists: resourceResgroupExists,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Timeouts: &schema.ResourceTimeout{
|
||||
Create: &constants.Timeout180s,
|
||||
Read: &constants.Timeout30s,
|
||||
Update: &constants.Timeout180s,
|
||||
Delete: &constants.Timeout60s,
|
||||
Default: &constants.Timeout60s,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "Name of this resource group. Names are case sensitive and unique within the context of a account.",
|
||||
},
|
||||
|
||||
"account_id": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ValidateFunc: validation.IntAtLeast(1),
|
||||
Description: "Unique ID of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
"def_net_type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "PRIVATE",
|
||||
ValidateFunc: validation.StringInSlice([]string{"PRIVATE", "PUBLIC", "NONE"}, false),
|
||||
Description: "Type of the network, which this resource group will use as default for its computes - PRIVATE or PUBLIC or NONE.",
|
||||
},
|
||||
|
||||
"def_net_id": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "ID of the default network for this resource group (if any).",
|
||||
},
|
||||
|
||||
"ipcidr": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Address of the netowrk inside the private network segment (aka ViNS) if def_net_type=PRIVATE",
|
||||
},
|
||||
|
||||
"ext_net_id": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 0,
|
||||
Description: "ID of the external network for default ViNS. Pass 0 if def_net_type=PUBLIC or no external connection required for the defult ViNS when def_net_type=PRIVATE",
|
||||
},
|
||||
|
||||
"ext_ip": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "IP address on the external netowrk to request when def_net_type=PRIVATE and ext_net_id is not 0",
|
||||
},
|
||||
|
||||
/* commented out, as in this version of provider we use default Grid ID
|
||||
"grid_id": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 0, // if 0 is passed, default Grid ID will be used
|
||||
// DefaultFunc: utilityResgroupGetDefaultGridID,
|
||||
ForceNew: true, // change of Grid ID will require new RG
|
||||
Description: "Unique ID of the grid, where this resource group is deployed.",
|
||||
},
|
||||
*/
|
||||
|
||||
"quota": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: quotaRgSubresourceSchemaMake(),
|
||||
},
|
||||
Description: "Quota settings for this resource group.",
|
||||
},
|
||||
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "User-defined text description of this resource group.",
|
||||
},
|
||||
|
||||
"account_name": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Name of the account, which this resource group belongs to.",
|
||||
},
|
||||
|
||||
/*
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "Current status of this resource group.",
|
||||
},
|
||||
|
||||
"vins": {
|
||||
Type: schema.TypeList, // this is a list of ints
|
||||
Computed: true,
|
||||
MaxItems: LimitMaxVinsPerResgroup,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
Description: "List of VINs deployed in this resource group.",
|
||||
},
|
||||
|
||||
"computes": {
|
||||
Type: schema.TypeList, // this is a list of ints
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
},
|
||||
Description: "List of computes deployed in this resource group.",
|
||||
},
|
||||
*/
|
||||
},
|
||||
}
|
||||
}
|
||||
138
internal/service/cloudapi/rg/utility_rg.go
Normal file
138
internal/service/cloudapi/rg/utility_rg.go
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/rudecs/terraform-provider-decort/internal/controller"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
// On success this function returns a string, as returned by API rg/get, which could be unmarshalled
|
||||
// into ResgroupGetResp structure
|
||||
func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
||||
// This function tries to locate resource group by one of the following algorithms depending
|
||||
// on the parameters passed:
|
||||
// - if resource group ID is specified -> by RG ID
|
||||
// - if resource group name is specifeid -> by RG name and either account ID or account name
|
||||
//
|
||||
// If succeeded, it returns non empty string that contains JSON formatted facts about the
|
||||
// resource group as returned by rg/get API call.
|
||||
// Otherwise it returns empty string and a 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 Terraform resource Exists method.
|
||||
//
|
||||
|
||||
c := m.(*controller.ControllerCfg)
|
||||
urlValues := &url.Values{}
|
||||
|
||||
// make it possible to use "read" & "check presence" functions with RG ID set so
|
||||
// that Import of RG resource is possible
|
||||
idSet := false
|
||||
theId, err := strconv.Atoi(d.Id())
|
||||
if err != nil || theId <= 0 {
|
||||
rgId, argSet := d.GetOk("rg_id")
|
||||
if argSet {
|
||||
theId = rgId.(int)
|
||||
idSet = true
|
||||
}
|
||||
} else {
|
||||
idSet = true
|
||||
}
|
||||
|
||||
if idSet {
|
||||
// go straight for the RG by its ID
|
||||
log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", theId)
|
||||
urlValues.Add("rgId", fmt.Sprintf("%d", theId))
|
||||
rgFacts, err := c.DecortAPICall("POST", ResgroupGetAPI, urlValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rgFacts, nil
|
||||
}
|
||||
|
||||
rgName, argSet := d.GetOk("name")
|
||||
if !argSet {
|
||||
// no RG ID and no RG name - we cannot locate resource group in this case
|
||||
return "", fmt.Errorf("Cannot check resource group presence if name is empty and no resource group ID specified")
|
||||
}
|
||||
|
||||
// Valid account ID is required to locate a resource group
|
||||
// obtain Account ID by account name - it should not be zero on success
|
||||
|
||||
urlValues.Add("includedeleted", "false")
|
||||
apiResp, err := c.DecortAPICall("POST", ResgroupListAPI, urlValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// log.Debugf("%s", apiResp)
|
||||
log.Debugf("utilityResgroupCheckPresence: ready to decode response body from %s", ResgroupListAPI)
|
||||
model := ResgroupListResp{}
|
||||
err = json.Unmarshal([]byte(apiResp), &model)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debugf("utilityResgroupCheckPresence: traversing decoded Json of length %d", len(model))
|
||||
for index, item := range model {
|
||||
// match by RG name & account ID
|
||||
if item.Name == rgName.(string) && item.AccountID == d.Get("account_id").(int) {
|
||||
log.Debugf("utilityResgroupCheckPresence: match RG name %s / ID %d, account ID %d at index %d",
|
||||
item.Name, item.ID, item.AccountID, index)
|
||||
|
||||
// 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 resource quota settings
|
||||
reqValues := &url.Values{}
|
||||
reqValues.Add("rgId", fmt.Sprintf("%d", item.ID))
|
||||
apiResp, err := c.DecortAPICall("POST", ResgroupGetAPI, reqValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return apiResp, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Cannot find RG name %s owned by account ID %d", rgName, d.Get("account_id").(int))
|
||||
}
|
||||
73
internal/service/cloudapi/rg/utility_rg_list.go
Normal file
73
internal/service/cloudapi/rg/utility_rg_list.go
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||
Authors:
|
||||
Petr Krutov, <petr.krutov@digitalenergy.online>
|
||||
Stanislav Solovev, <spsolovev@digitalenergy.online>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
|
||||
Orchestration Technology) with Terraform by Hashicorp.
|
||||
|
||||
Source code: https://github.com/rudecs/terraform-provider-decort
|
||||
|
||||
Please see README.md to learn where to place source code so that it
|
||||
builds seamlessly.
|
||||
|
||||
Documentation: https://github.com/rudecs/terraform-provider-decort/wiki
|
||||
*/
|
||||
|
||||
package rg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/rudecs/terraform-provider-decort/internal/controller"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
func utilityRgListCheckPresence(d *schema.ResourceData, m interface{}) (ResgroupListResp, error) {
|
||||
c := m.(*controller.ControllerCfg)
|
||||
urlValues := &url.Values{}
|
||||
|
||||
rgList := ResgroupListResp{}
|
||||
|
||||
if size, ok := d.GetOk("size"); ok {
|
||||
urlValues.Add("size", strconv.Itoa(size.(int)))
|
||||
}
|
||||
if page, ok := d.GetOk("page"); ok {
|
||||
urlValues.Add("page", strconv.Itoa(page.(int)))
|
||||
}
|
||||
if includedeleted, ok := d.GetOk("includedeleted"); ok {
|
||||
urlValues.Add("includedeleted", strconv.FormatBool(includedeleted.(bool)))
|
||||
}
|
||||
|
||||
log.Debugf("utilityRgListCheckPresence: load rg list")
|
||||
rgListRaw, err := c.DecortAPICall("POST", ResgroupListAPI, urlValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(rgListRaw), &rgList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rgList, nil
|
||||
}
|
||||
Reference in New Issue
Block a user