This commit is contained in:
2023-12-18 18:36:55 +03:00
parent 294680282e
commit e2ee45ee14
155 changed files with 10125 additions and 17209 deletions

View File

@@ -1,8 +1,9 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Sergey Kisil, <svkisil@digitalenergy.online>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -33,66 +34,23 @@ package vins
import (
"context"
"fmt"
"reflect"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
// "net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
// "github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func flattenVins(d *schema.ResourceData, vinsRecord *vins.RecordVINS) diag.Diagnostics {
log.Debugf("flattenVins: decoded ViNS name:ID %s:%d, account ID %d, RG ID %d",
vinsRecord.Name, vinsRecord.ID, vinsRecord.AccountID, vinsRecord.RGID)
d.SetId(fmt.Sprintf("%d", vinsRecord.ID))
d.Set("name", vinsRecord.Name)
d.Set("account_id", vinsRecord.AccountID)
// d.Set("account_name", vinsRecord.AccountName)
d.Set("rg_id", vinsRecord.RGID)
d.Set("description", vinsRecord.Description)
d.Set("ipcidr", vinsRecord.Network)
noExtNetConnection := true
gw := vinsRecord.VNFs.GW
if !reflect.ValueOf(gw).IsZero() {
log.Debugf("flattenVins: discovered GW VNF ID %d in ViNS ID %d", gw.ID, vinsRecord.ID)
extNetID := gw.Config.ExtNetID
extNetIP := gw.Config.ExtNetIP
if extNetID != 0 && extNetIP != "" {
log.Debugf("flattenVins: ViNS ext_net_id=%d, ext_net_ip=%s", extNetID, extNetIP)
d.Set("ext_ip_addr", extNetIP)
d.Set("ext_net_id", extNetID)
} else {
return diag.Errorf("Failed to unmarshal VNF GW Config - structure is invalid.")
}
noExtNetConnection = false
}
if noExtNetConnection {
d.Set("ext_ip_addr", "")
d.Set("ext_net_id", 0)
}
log.Debugf("flattenVins: EXTRA CHECK - schema rg_id=%d, ext_net_id=%d", d.Get("rg_id").(int), d.Get("ext_net_id").(int))
return nil
}
func dataSourceVinsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
vinsFacts, err := utilityVinsCheckPresence(ctx, d, m)
if vinsFacts == nil {
vins, err := utilityVinsCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
return flattenVins(d, vinsFacts)
flattenVinsData(d, vins)
d.SetId(strconv.FormatUint(vins.ID, 10))
return nil
}
func DataSourceVins() *schema.Resource {
@@ -105,64 +63,6 @@ func DataSourceVins() *schema.Resource {
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the ViNS. Names are case sensitive and unique within the context of an account or resource group.",
},
/*
"vins_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the ViNS. If ViNS ID is specified, then ViNS name, rg_id and account_id are ignored.",
},
*/
"rg_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the resource group, where this ViNS is belongs to (for ViNS created at resource group level, 0 otherwise).",
},
"account_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the account, which this ViNS belongs to.",
},
// the rest of attributes are computed
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account, which this ViNS belongs to.",
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: "User-defined text description of this ViNS.",
},
"ext_ip_addr": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of the external connection (valid for ViNS connected to external network, empty string otherwise).",
},
"ext_net_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the external network this ViNS is connected to (-1 means no external connection).",
},
"ipcidr": {
Type: schema.TypeString,
Computed: true,
Description: "Network address used by this ViNS.",
},
},
Schema: dataSourceVinsSchemaMake(),
}
}

View File

@@ -1,190 +1,71 @@
/*
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://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package vins
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func flattenVinsList(vl *vins.ListVINS) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
for _, v := range vl.Data {
temp := map[string]interface{}{
"account_id": v.AccountID,
"account_name": v.AccountName,
"created_by": v.CreatedBy,
"created_time": v.CreatedTime,
"deleted_by": v.DeletedBy,
"deleted_time": v.DeletedTime,
"external_ip": v.ExternalIP,
"vins_id": v.ID,
"vins_name": v.Name,
"network": v.Network,
"rg_id": v.RGID,
"rg_name": v.RGName,
"status": v.Status,
"updated_by": v.UpdatedBy,
"updated_time": v.UpdatedTime,
"vxlan_id": v.VXLANID,
}
res = append(res, temp)
}
return res
}
func dataSourceVinsListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
vinsList, err := utilityVinsListCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenVinsList(vinsList))
return nil
}
func dataSourceVinsListSchemaMake() map[string]*schema.Schema {
res := map[string]*schema.Schema{
"include_deleted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "include deleted computes",
},
"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,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"external_ip": {
Type: schema.TypeString,
Computed: true,
},
"vins_id": {
Type: schema.TypeInt,
Computed: true,
},
"vins_name": {
Type: schema.TypeString,
Computed: true,
},
"network": {
Type: schema.TypeString,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
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,
},
"vxlan_id": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
}
return res
}
func DataSourceVinsList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceVinsListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceVinsListSchemaMake(),
}
}
/*
Copyright (c) 2019-2023 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://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package vins
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceVinsListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
vinsList, err := utilityVinsListCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("items", flattenVinsList(vinsList))
d.Set("entry_count", vinsList.EntryCount)
return nil
}
func DataSourceVinsList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceVinsListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceVinsListSchemaMake(),
}
}

View File

@@ -1,8 +1,9 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Sergey Kisil, <svkisil@digitalenergy.online>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -39,83 +40,53 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
"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 ipcidrDiffSupperss(key, oldVal, newVal string, d *schema.ResourceData) bool {
if oldVal == "" && newVal != "" {
// if old value for "ipcidr" resource is empty string, it means that we are creating new ViNS
// and there is a chance that the user will want specific IP address range for this ViNS -
// check if "ipcidr" is explicitly set in TF file to a non-empty string.
log.Debugf("ipcidrDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=FALSE", key, oldVal, newVal)
return false // there is a difference between stored and new value
}
log.Debugf("ipcidrDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=TRUE", key, oldVal, newVal)
return true // suppress difference
}
func resourceVinsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceVinsCreate: called for ViNS name %s, Account ID %d, RG ID %d",
d.Get("name").(string), d.Get("account_id").(int), d.Get("rg_id").(int))
createInAcc := true
c := m.(*controller.ControllerCfg)
inAccReq := vins.CreateInAccountRequest{
Name: d.Get("name").(string),
}
inRGReq := vins.CreateInRGRequest{
Name: d.Get("name").(string),
RGID, rgOk := d.GetOk("rg_id")
AccountID, accountIdOk := d.GetOk("account_id")
if !rgOk && !accountIdOk {
return diag.Errorf("resourceVinsCreate: no valid accountId or resource group ID specified")
}
argVal, argSet := d.GetOk("rg_id")
if argSet && argVal.(int) > 0 {
createInAcc = false
inRGReq.RGID = uint64(argVal.(int))
} else {
argVal, argSet = d.GetOk("account_id")
if !argSet || argVal.(int) <= 0 {
return diag.Errorf("resourceVinsCreate: ViNS name %s - no valid account and/or resource group ID specified", d.Id())
}
inAccReq.AccountID = uint64(argVal.(int))
}
argVal, argSet = d.GetOk("ext_net_id") // NB: even if ext_net_id value is explicitly set to 0, argSet = false anyway
if argSet {
if argVal.(int) > 0 {
inRGReq.ExtNetID = uint64(argVal.(int))
} else {
inRGReq.ExtNetID = 0
}
}
argVal, argSet = d.GetOk("ipcidr")
if argSet && argVal.(string) != "" {
log.Debugf("resourceVinsCreate: ipcidr is set to %s", argVal.(string))
inAccReq.IPCIDR = argVal.(string)
inRGReq.IPCIDR = argVal.(string)
}
argVal, argSet = d.GetOk("description")
if argSet {
inAccReq.Description = argVal.(string)
inRGReq.Description = argVal.(string)
if rgOk && accountIdOk {
return diag.Errorf("resourceVinsCreate: either accountId or resource group ID should be specified")
}
var vinsID uint64
if createInAcc {
apiResp, err := c.CloudBroker().VINS().CreateInAccount(ctx, inAccReq)
if accountIdOk {
req, diags := createVinsInAcc(ctx, d, m, uint64(AccountID.(int)))
if diags != nil {
return diags
}
apiResp, err := c.CloudBroker().VINS().CreateInAccount(ctx, req)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
vinsID = apiResp
} else {
apiResp, err := c.CloudBroker().VINS().CreateInRG(ctx, inRGReq)
} else if rgOk {
req, diags := createVinsInRG(ctx, d, m, uint64(RGID.(int)))
if diags != nil {
return diags
}
apiResp, err := c.CloudBroker().VINS().CreateInRG(ctx, req)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
@@ -123,20 +94,103 @@ func resourceVinsCreate(ctx context.Context, d *schema.ResourceData, m interface
}
d.SetId(strconv.FormatUint(vinsID, 10))
d.Set("vins_id", vinsID)
log.Debugf("resourceVinsCreate: new ViNS ID / name %d / %s creation sequence complete", vinsID, d.Get("name").(string))
return dataSourceVinsRead(ctx, d, m)
warnings := dc.Warnings{}
if _, ok := d.GetOk("ip"); ok {
if errs := resourceVinsIpReserve(ctx, d, m, vinsID); len(errs) != 0 {
for _, err := range errs {
warnings.Add(err)
}
}
}
if _, ok := d.GetOk("nat_rule"); ok {
if errs := resourceVinsNatRuleAdd(ctx, d, m, vinsID); len(errs) != 0 {
for _, err := range errs {
warnings.Add(err)
}
}
}
return append(warnings.Get(), resourceVinsRead(ctx, d, m)...)
}
func resourceVinsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
vinsFacts, err := utilityVinsCheckPresence(ctx, d, m)
if vinsFacts == nil {
log.Debugf("resourceVinsRead: called for vins id %s, name %s",
d.Id(), d.Get("name").(string))
warnings := dc.Warnings{}
vinsData, err := utilityVinsCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
return flattenVins(d, vinsFacts)
isEnabled := d.Get("enable").(bool)
hasChangeState := false
switch vinsData.Status {
case status.Destroyed:
d.Set("vins_id", 0)
d.SetId("")
return diag.Errorf("The resource cannot be read because it has been destroyed")
// return resourceVinsCreate(ctx, d, m)
case status.Deleted:
// hasChangeState = true
// req := vins.RestoreRequest{
// VINSID: vinsData.ID,
// }
// if reason, ok := d.GetOk("reason"); ok {
// req.Reason = reason.(string)
// }
// _, err := c.CloudBroker().VINS().Restore(ctx, req)
// if err != nil {
// warnings.Add(err)
// }
case status.Modeled:
return diag.Errorf("ViNS are in status: %s, please, contact support for more information", vinsData.Status)
case status.Created:
case status.Enabled:
if !isEnabled {
hasChangeState = true
if err := resourceVinsDisable(ctx, d, m, vinsData.ID); err != nil {
warnings.Add(err)
}
}
case status.Enabling:
case status.Disabled:
if isEnabled {
hasChangeState = true
if err := resourceVinsEnable(ctx, d, m, vinsData.ID); err != nil {
warnings.Add(err)
}
}
case status.Disabling:
case status.Deleting:
return diag.Errorf("ViNS are in progress with status: %s", vinsData.Status)
}
if hasChangeState {
vinsData, err = utilityVinsCheckPresence(ctx, d, m)
if vinsData == nil {
d.SetId("")
return diag.FromErr(err)
}
}
flattenVins(d, vinsData)
log.Debugf("resourceVinsRead: after flattenVins: vins_id %s, name %s",
d.Id(), d.Get("name").(string))
return warnings.Get()
}
func resourceVinsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
@@ -144,137 +198,588 @@ func resourceVinsUpdate(ctx context.Context, d *schema.ResourceData, m interface
d.Id(), d.Get("name").(string), d.Get("account_id").(int), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
vinsID, _ := strconv.ParseUint(d.Id(), 10, 64)
oldExtNetId, newExtNedId := d.GetChange("ext_net_id")
if oldExtNetId.(int) != newExtNedId.(int) {
log.Debugf("resourceVinsUpdate: changing ViNS ID %s - ext_net_id %d -> %d", d.Id(), oldExtNetId.(int), newExtNedId.(int))
if diags := checkParamsExistenceUpdate(ctx, d, c); diags != nil {
return diags
}
if oldExtNetId.(int) > 0 {
// there was preexisting external net connection - disconnect ViNS
req := vins.ExtNetDisconnectRequest{VINSID: vinsID}
vinsData, err := utilityVinsCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
_, err := c.CloudBroker().VINS().ExtNetDisconnect(ctx, req)
if err != nil {
return diag.FromErr(err)
isEnabled := d.Get("enable").(bool)
hasChangeState := false
warnings := dc.Warnings{}
switch vinsData.Status {
case status.Destroyed:
d.Set("vins_id", 0)
d.SetId("")
return diag.Errorf("The resource cannot be updated because it has been destroyed")
// return resourceVinsCreate(ctx, d, m)
case status.Deleted:
hasChangeState = true
if err := resourceVinsRestore(ctx, d, m, vinsData.ID); err != nil {
warnings.Add(err)
}
case status.Modeled:
return diag.Errorf("ViNS are in status: %s, please, contact support for more information", vinsData.Status)
case status.Created:
case status.Enabled:
if !isEnabled {
hasChangeState = true
if err := resourceVinsDisable(ctx, d, m, vinsData.ID); err != nil {
warnings.Add(err)
}
}
if newExtNedId.(int) > 0 {
req := vins.ExtNetConnectRequest{
VINSID: vinsID,
NetID: uint64(newExtNedId.(int)),
case status.Enabling:
case status.Disabled:
if isEnabled {
hasChangeState = true
if err := resourceVinsEnable(ctx, d, m, vinsData.ID); err != nil {
warnings.Add(err)
}
}
case status.Disabling:
case status.Deleting:
return diag.Errorf("ViNS are in progress with status: %s", vinsData.Status)
}
_, err := c.CloudBroker().VINS().ExtNetConnect(ctx, req)
if err != nil {
return diag.FromErr(err)
if hasChangeState {
vinsData, err = utilityVinsCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
}
if d.HasChange("enable") {
if err := resourceVinsChangeEnabled(ctx, d, m); err != nil {
warnings.Add(err)
}
}
if d.HasChange("ext_net_id") {
if err := resourceVinsChangeExtNetId(ctx, d, m); err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("ip") {
if errs := resourceVinsChangeIp(ctx, d, m); len(errs) != 0 {
for _, err := range errs {
warnings.Add(err)
}
}
}
if d.HasChange("nat_rule") {
if errs := resourceVinsChangeNatRule(ctx, d, m); len(errs) != 0 {
for _, err := range errs {
warnings.Add(err)
}
}
}
return dataSourceVinsRead(ctx, d, m)
if d.HasChange("default_qos") {
if err := resourceVinsChangeDefaultQos(ctx, d, m); err != nil {
warnings.Add(err)
}
}
if d.HasChange("vnfdev_redeploy") {
if err := resourceVinsChangeVnfRedeploy(ctx, d, m); err != nil {
warnings.Add(err)
}
}
if d.HasChange("vnfdev_restart") {
if err := resourceVinsChangeVnfRestart(ctx, d, m); err != nil {
warnings.Add(err)
}
}
if d.HasChange("vnfdev_reset") {
if err := resourceVinsChangeVnfReset(ctx, d, m); err != nil {
warnings.Add(err)
}
}
if d.HasChange("vnfdev_start") {
if err := resourceVinsChangeVnfStartStop(ctx, d, m); err != nil {
warnings.Add(err)
}
}
return append(warnings.Get(), dataSourceVinsRead(ctx, d, m)...)
}
func resourceVinsDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceVinsDelete: called for ViNS ID / name %s / %s, Account ID %d, RG ID %d",
d.Id(), d.Get("name").(string), d.Get("account_id").(int), d.Get("rg_id").(int))
vinsFacts, err := utilityVinsCheckPresence(ctx, d, m)
if vinsFacts == nil {
if err != nil {
return diag.FromErr(err)
}
// the specified ViNS does not exist - in this case according to Terraform best practice
// we exit from Destroy method without error
return nil
}
c := m.(*controller.ControllerCfg)
req := vins.DeleteRequest{
VINSID: vinsFacts.ID,
Force: true,
Permanently: true,
vinsItem, err := utilityVinsCheckPresence(ctx, d, m)
if vinsItem == nil {
d.SetId("")
return diag.FromErr(err)
}
_, err = c.CloudBroker().VINS().Delete(ctx, req)
if err != nil {
req := vins.DeleteRequest{VINSID: vinsItem.ID}
if force, ok := d.GetOk("force"); ok {
req.Force = force.(bool)
}
if permanently, ok := d.GetOk("permanently"); ok {
req.Permanently = permanently.(bool)
}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
if _, err := c.CloudBroker().VINS().Delete(ctx, req); err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
func resourceVinsEnable(ctx context.Context, d *schema.ResourceData, m interface{}, vinsId uint64) error {
c := m.(*controller.ControllerCfg)
req := vins.EnableRequest{
VINSID: vinsId,
}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().Enable(ctx, req)
return err
}
func resourceVinsDisable(ctx context.Context, d *schema.ResourceData, m interface{}, vinsId uint64) error {
c := m.(*controller.ControllerCfg)
req := vins.DisableRequest{
VINSID: vinsId,
}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().Disable(ctx, req)
return err
}
func resourceVinsRestore(ctx context.Context, d *schema.ResourceData, m interface{}, vinsId uint64) error {
c := m.(*controller.ControllerCfg)
req := vins.RestoreRequest{
VINSID: vinsId,
}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().Restore(ctx, req)
return err
}
func resourceVinsIpReserve(ctx context.Context, d *schema.ResourceData, m interface{}, vinsId uint64) []error {
var errs []error
c := m.(*controller.ControllerCfg)
ipRes := d.Get("ip")
ipsSlice := ipRes.([]interface{})
for _, ipInterfase := range ipsSlice {
ip := ipInterfase.(map[string]interface{})
req := vins.IPReserveRequest{
VINSID: vinsId,
Type: ip["type"].(string),
}
if ipAddr, ok := ip["ip_addr"]; ok {
req.IPAddr = ipAddr.(string)
}
if macAddr, ok := ip["mac"]; ok {
req.MAC = macAddr.(string)
}
if computeId, ok := ip["compute_id"]; ok {
req.ComputeID = uint64(computeId.(int))
}
if reason, ok := ip["reason"]; ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().IPReserve(ctx, req)
if err != nil {
errs = append(errs, err)
}
}
return errs
}
func resourceVinsNatRuleAdd(ctx context.Context, d *schema.ResourceData, m interface{}, vinsId uint64) []error {
var errs []error
c := m.(*controller.ControllerCfg)
natRule := d.Get("nat_rule")
addedNatRules := natRule.([]interface{})
if len(addedNatRules) > 0 {
for _, natRuleInterface := range addedNatRules {
natRule := natRuleInterface.(map[string]interface{})
req := vins.NATRuleAddRequest{
VINSID: vinsId,
IntIP: natRule["int_ip"].(string),
IntPort: uint64(natRule["int_port"].(int)),
ExtPortStart: uint64(natRule["ext_port_start"].(int)),
}
if extPortEnd, ok := natRule["ext_port_end"]; ok {
req.ExtPortEnd = uint64(extPortEnd.(int))
}
if proto, ok := natRule["proto"]; ok {
req.Proto = proto.(string)
}
if reason, ok := natRule["reason"]; ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().NATRuleAdd(ctx, req)
if err != nil {
errs = append(errs, err)
}
}
}
return errs
}
func resourceVinsChangeEnabled(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
_, enableNew := d.GetChange("enable")
if enableNew.(bool) {
req := vins.EnableRequest{
VINSID: vinsId,
}
_, err := c.CloudBroker().VINS().Enable(ctx, req)
return err
}
req := vins.DisableRequest{
VINSID: vinsId,
}
_, err := c.CloudBroker().VINS().Disable(ctx, req)
return err
}
func resourceVinsChangeExtNetId(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
oldExtNetId, newExtNedId := d.GetChange("ext_net_id")
log.Debugf("resourceVinsUpdate - resourceVinsChangeExtNetId: changing ViNS ID %s - ext_net_id %d -> %d", d.Id(), oldExtNetId.(int), newExtNedId.(int))
if oldExtNetId.(int) > 0 {
// there was preexisting external net connection - disconnect ViNS
req := vins.ExtNetDisconnectRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().ExtNetDisconnect(ctx, req)
return err
}
if newExtNedId.(int) > 0 {
req := vins.ExtNetConnectRequest{
VINSID: vinsId,
NetID: uint64(newExtNedId.(int)),
}
if ip, ok := d.GetOk("ext_ip"); ok && ip != "" {
req.IP = ip.(string)
}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().ExtNetConnect(ctx, req)
return err
}
return nil
}
func resourceVinsSchemaMake() map[string]*schema.Schema {
rets := map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
Description: "Name of the ViNS. Names are case sensitive and unique within the context of an account or resource group.",
},
func resourceVinsChangeIp(ctx context.Context, d *schema.ResourceData, m interface{}) []error {
c := m.(*controller.ControllerCfg)
/* we do not need ViNS ID as an argument because if we already know this ID, it is not practical to call resource provider.
Resource Import will work anyway, as it obtains the ID of ViNS to be imported through another mechanism.
"vins_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Unique ID of the ViNS. If ViNS ID is specified, then ViNS name, rg_id and account_id are ignored.",
},
*/
var errs []error
"rg_id": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 0,
Description: "ID of the resource group, where this ViNS belongs to. Non-zero for ViNS created at resource group level, 0 otherwise.",
},
vinsId := uint64(d.Get("vins_id").(int))
deletedIps := make([]interface{}, 0)
addedIps := make([]interface{}, 0)
"account_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntAtLeast(1),
Description: "ID of the account, which this ViNS belongs to. For ViNS created at account level, resource group ID is 0.",
},
oldIpInterface, newIpInterface := d.GetChange("ip")
oldIpSlice := oldIpInterface.([]interface{})
newIpSlice := newIpInterface.([]interface{})
"ext_net_id": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntAtLeast(0),
Description: "ID of the external network this ViNS is connected to. Pass 0 if no external connection required.",
},
"ipcidr": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: ipcidrDiffSupperss,
Description: "Network address to use by this ViNS. This parameter is only valid when creating new ViNS.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "Optional user-defined text description of this ViNS.",
},
// the rest of attributes are computed
"account_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the account, which this ViNS belongs to.",
},
"ext_ip_addr": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of the external connection (valid for ViNS connected to external network, ignored otherwise).",
},
for _, el := range oldIpSlice {
if !isContainsIp(newIpSlice, el) {
deletedIps = append(deletedIps, el)
}
}
return rets
for _, el := range newIpSlice {
if !isContainsIp(oldIpSlice, el) {
addedIps = append(addedIps, el)
}
}
if len(deletedIps) > 0 {
for _, ipInterface := range deletedIps {
ip := ipInterface.(map[string]interface{})
req := vins.IPReleaseRequest{VINSID: vinsId}
if ip["ip_addr"].(string) != "" {
req.IPAddr = ip["ip_addr"].(string)
}
if ip["mac"].(string) != "" {
req.MAC = ip["mac"].(string)
}
_, err := c.CloudBroker().VINS().IPRelease(ctx, req)
if err != nil {
errs = append(errs, err)
}
}
}
if len(addedIps) > 0 {
for _, ipInterface := range addedIps {
ip := ipInterface.(map[string]interface{})
req := vins.IPReserveRequest{
VINSID: vinsId,
Type: ip["type"].(string),
}
if ip["ip_addr"].(string) != "" {
req.IPAddr = ip["ip_addr"].(string)
}
if ip["mac"].(string) != "" {
req.MAC = ip["mac"].(string)
}
if ip["compute_id"].(int) != 0 {
req.ComputeID = uint64(ip["compute_id"].(int))
}
if ip["reason"].(string) != "" {
req.Reason = ip["reason"].(string)
}
_, err := c.CloudBroker().VINS().IPReserve(ctx, req)
if err != nil {
errs = append(errs, err)
}
}
}
return errs
}
func resourceVinsChangeNatRule(ctx context.Context, d *schema.ResourceData, m interface{}) []error {
c := m.(*controller.ControllerCfg)
var errs []error
vinsId := uint64(d.Get("vins_id").(int))
deletedNatRules := make([]interface{}, 0)
addedNatRules := make([]interface{}, 0)
oldNatRulesInterface, newNatRulesInterface := d.GetChange("nat_rule")
oldNatRulesSlice := oldNatRulesInterface.([]interface{})
newNatRulesSlice := newNatRulesInterface.([]interface{})
for _, el := range oldNatRulesSlice {
if !isContainsNatRule(newNatRulesSlice, el) {
deletedNatRules = append(deletedNatRules, el)
}
}
for _, el := range newNatRulesSlice {
if !isContainsNatRule(oldNatRulesSlice, el) {
addedNatRules = append(addedNatRules, el)
}
}
if len(deletedNatRules) > 0 {
for _, natRuleInterface := range deletedNatRules {
natRule := natRuleInterface.(map[string]interface{})
req := vins.NATRuleDelRequest{
VINSID: vinsId,
RuleID: int64(natRule["rule_id"].(int)),
}
if natRule["reason"].(string) != "" {
req.Reason = natRule["reason"].(string)
}
_, err := c.CloudBroker().VINS().NATRuleDel(ctx, req)
errs = append(errs, err)
}
}
if len(addedNatRules) > 0 {
for _, natRuleInterface := range addedNatRules {
natRule := natRuleInterface.(map[string]interface{})
req := vins.NATRuleAddRequest{
VINSID: vinsId,
IntIP: natRule["int_ip"].(string),
IntPort: uint64(natRule["int_port"].(int)),
ExtPortStart: uint64(natRule["ext_port_start"].(int)),
}
if natRule["ext_port_end"].(int) != 0 {
req.ExtPortEnd = uint64(natRule["ext_port_end"].(int))
}
if natRule["proto"].(string) != "" {
req.Proto = natRule["proto"].(string)
}
_, err := c.CloudBroker().VINS().NATRuleAdd(ctx, req)
if err != nil {
errs = append(errs, err)
}
}
}
return errs
}
func resourceVinsChangeDefaultQos(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
defaultQosInterface := d.Get("default_qos").([]interface{})
if len(defaultQosInterface) > 0 {
defaultQos := defaultQosInterface[0].(map[string]interface{})
req := vins.DefaultQOSUpdateRequest{VINSID: vinsId}
if inRate, ok := defaultQos["in_rate"]; ok {
req.IngressRate = uint64(inRate.(int))
}
if inBurst, ok := defaultQos["in_burst"]; ok {
req.IngressBirst = uint64(inBurst.(int))
}
if eRate, ok := defaultQos["e_rate"]; ok {
req.EgressRate = uint64(eRate.(int))
}
_, err := c.CloudBroker().VINS().DefaultQOSUpdate(ctx, req)
return err
}
return nil
}
func resourceVinsChangeVnfRedeploy(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
_, newRedeploy := d.GetChange("vnfdev_redeploy")
if newRedeploy.(bool) {
req := vins.VNFDevRedeployRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().VNFDevRedeploy(ctx, req)
return err
}
return nil
}
func resourceVinsChangeVnfRestart(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
_, newRestart := d.GetChange("vnfdev_restart")
if newRestart.(bool) {
req := vins.VNFDevRestartRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().VNFDevRestart(ctx, req)
if err != nil {
return err
}
}
return nil
}
func resourceVinsChangeVnfReset(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
_, newRestart := d.GetChange("vnfdev_reset")
if newRestart.(bool) {
req := vins.VNFDevResetRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().VNFDevReset(ctx, req)
if err != nil {
return err
}
}
return nil
}
func resourceVinsChangeVnfStartStop(ctx context.Context, d *schema.ResourceData, m interface{}) error {
c := m.(*controller.ControllerCfg)
vinsId := uint64(d.Get("vins_id").(int))
_, newStart := d.GetChange("vnfdev_start")
if newStart.(bool) {
req := vins.VNFDevStartRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().VNFDevStart(ctx, req)
if err != nil {
return err
}
}
req := vins.VNFDevStopRequest{VINSID: vinsId}
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
_, err := c.CloudBroker().VINS().VNFDevStop(ctx, req)
return err
}
func ResourceVins() *schema.Resource {

View File

@@ -1,8 +1,9 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Sergey Kisil, <svkisil@digitalenergy.online>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -33,7 +34,6 @@ package vins
import (
"context"
"fmt"
"strconv"
log "github.com/sirupsen/logrus"
@@ -46,78 +46,24 @@ import (
func utilityVinsCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*vins.RecordVINS, error) {
c := m.(*controller.ControllerCfg)
idSet := false
vinsID, err := strconv.ParseUint(d.Id(), 10, 64)
if err != nil || vinsID <= 0 {
vinsId, argSet := d.GetOk("vins_id") // NB: vins_id is NOT present in vinsResource schema!
if argSet {
vinsID = uint64(vinsId.(int))
idSet = true
}
log.Debug("utilityVinsCheckPresence: locating ViNS by its ID")
req := vins.GetRequest{}
if d.Id() != "" {
id, _ := strconv.ParseUint(d.Id(), 10, 64)
req.VINSID = id
} else {
idSet = true
req.VINSID = uint64(d.Get("vins_id").(int))
}
if idSet {
log.Debugf("utilityVinsCheckPresence: locating ViNS by its ID %d", vinsID)
req := vins.GetRequest{VINSID: vinsID}
vinsFacts, err := c.CloudBroker().VINS().Get(ctx, req)
if err != nil {
return nil, err
}
return vinsFacts, nil
if reason, ok := d.GetOk("reason"); ok {
req.Reason = reason.(string)
}
vinsName, argSet := d.GetOk("name")
if !argSet {
return nil, fmt.Errorf("Cannot check ViNS presence if ViNS name is empty")
}
req := vins.SearchRequest{
Name: vinsName.(string),
ShowAll: false,
}
log.Debugf("utilityVinsCheckPresence: preparing to locate ViNS name %s", vinsName.(string))
rgId, rgSet := d.GetOk("rg_id")
if rgSet {
log.Debugf("utilityVinsCheckPresence: limiting ViNS search to RG ID %d", rgId.(int))
req.RGID = uint64(rgId.(int))
}
accountId, accountSet := d.GetOk("account_id")
if accountSet {
log.Debugf("utilityVinsCheckPresence: limiting ViNS search to Account ID %d", accountId.(int))
req.AccountID = uint64(accountId.(int))
}
vinsList, err := c.CloudBroker().VINS().Search(ctx, req)
vins, err := c.CloudBroker().VINS().Get(ctx, req)
if err != nil {
return nil, err
}
log.Debugf("utilityVinsCheckPresence: traversing decoded Json of length %d", len(vinsList))
for index, item := range vinsList {
if item.Name == vinsName.(string) {
if (accountSet && item.AccountID != uint64(accountId.(int))) ||
(rgSet && item.RGID != uint64(rgId.(int))) {
continue
}
log.Debugf("utilityVinsCheckPresence: match ViNS name %s / ID %d, account ID %d, RG ID %d at index %d",
item.Name, item.ID, item.AccountID, item.RGID, index)
req := vins.GetRequest{VINSID: item.ID}
vinsGetResp, err := c.CloudBroker().VINS().Get(ctx, req)
if err != nil {
return nil, err
}
return vinsGetResp, nil
}
}
return nil, fmt.Errorf("Cannot find ViNS name %s. Check name and/or RG ID & Account ID and your access rights", vinsName.(string))
return vins, nil
}

View File

@@ -1,62 +1,80 @@
/*
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://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package vins
import (
"context"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityVinsListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*vins.ListVINS, error) {
c := m.(*controller.ControllerCfg)
req := vins.ListRequest{}
if page, ok := d.GetOk("page"); ok {
req.Page = uint64(page.(int))
}
if size, ok := d.GetOk("size"); ok {
req.Size = uint64(size.(int))
}
log.Debugf("utilityVinsListCheckPresence")
vinsList, err := c.CloudBroker().VINS().List(ctx, req)
if err != nil {
return nil, err
}
return vinsList, nil
}
/*
Copyright (c) 2019-2023 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://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package vins
import (
"context"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func utilityVinsListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*vins.ListVINS, error) {
c := m.(*controller.ControllerCfg)
req := vins.ListRequest{}
if byId, ok := d.GetOk("by_id"); ok {
req.ByID = uint64(byId.(int))
}
if name, ok := d.GetOk("name"); ok {
req.Name = name.(string)
}
if accountId, ok := d.GetOk("account_id"); ok {
req.AccountID = uint64(accountId.(int))
}
if rgId, ok := d.GetOk("rg_id"); ok {
req.RGID = uint64(rgId.(int))
}
if extIp, ok := d.GetOk("ext_ip"); ok {
req.ExtIP = extIp.(string)
}
if page, ok := d.GetOk("page"); ok {
req.Page = uint64(page.(int))
}
if size, ok := d.GetOk("size"); ok {
req.Size = uint64(size.(int))
}
if includeDeleted, ok := d.GetOk("include_deleted"); ok {
req.IncludeDeleted = includeDeleted.(bool)
}
log.Debugf("utilityVinsListCheckPresence")
vinsList, err := c.CloudBroker().VINS().List(ctx, req)
if err != nil {
return nil, err
}
return vinsList, nil
}