package utilities import ( "context" "fmt" "strconv" "strings" "time" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/vins/models" "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/status" ) func VINSResourceCheckPresence(ctx context.Context, vinsId uint64, c *client.Client) (*vins.RecordVINS, diag.Diagnostics) { tflog.Info(ctx, fmt.Sprintf("VINSResourceCheckPresence: Get info about vins with ID - %v", vinsId)) diags := diag.Diagnostics{} recordVINS, err := c.CloudBroker().VINS().Get(ctx, vins.GetRequest{VINSID: vinsId}) if err != nil { diags.AddError(fmt.Sprintf("Cannot get info about vins with ID %v", vinsId), err.Error()) return nil, diags } tflog.Info(ctx, "VINSResourceCheckPresence: response from CloudBroker().VINS().Get", map[string]any{"vins_id": vinsId, "response": recordVINS}) return recordVINS, nil } // CreateInRGResourceVINS creates vins in resource group based on plan. // Returns vins_id for created vins and errors in case of failures. func CreateInRGResourceVINS(ctx context.Context, plan *models.ResourceVINSModel, c *client.Client) (uint64, diag.Diagnostics) { tflog.Info(ctx, fmt.Sprintf("Start CreateInRGResourceVINS: vins_name %s", plan.Name.ValueString())) diags := diag.Diagnostics{} createReq := vins.CreateInRGRequest{ Name: plan.Name.ValueString(), RGID: uint64(plan.RGID.ValueInt64()), } if !plan.IPCIDR.IsNull() { // IPCIDR is optional createReq.IPCIDR = plan.IPCIDR.ValueString() } if !plan.ExtNet.IsNull() { // ExtNet is optional var extnetPlan models.ExtNetModel tflog.Info(ctx, "CreateInRGResourceVINS: extnet specified", map[string]any{"name": plan.Name.ValueString()}) diags.Append(plan.ExtNet.As(ctx, &extnetPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true})...) if diags.HasError() { tflog.Error(ctx, "CreateInRGResourceVINS: cannot populate extnet with plan.ExtNet object element") return 0, diags } if extnetPlan.ExtNetID.IsNull() { createReq.ExtNetID = -1 // default value } else { createReq.ExtNetID = extnetPlan.ExtNetID.ValueInt64() } if !extnetPlan.ExtNetIP.IsNull() { createReq.ExtIP = extnetPlan.ExtNetIP.ValueString() } } else { createReq.ExtNetID = -1 // default value } if !plan.Description.IsNull() { // Description is optional createReq.Description = plan.Description.ValueString() } if plan.PreReservationsNum.IsUnknown() { // PreReservationsNum is optional & computed createReq.PreReservationsNum = uint64(32) // default value } else { createReq.PreReservationsNum = uint64(plan.PreReservationsNum.ValueInt64()) } if !plan.DNS.IsNull() { result := make([]string, 0, len(plan.DNS.Elements())) for _, val := range plan.DNS.Elements() { result = append(result, strings.Trim(val.String(), "\"")) } createReq.DNSList = result } tflog.Info(ctx, "CreateInRGResourceVINS: before call CloudBroker().VINS().CreateInRG", map[string]any{"req": createReq}) vinsId, err := c.CloudBroker().VINS().CreateInRG(ctx, createReq) if err != nil { diags.AddError( "Create resourceVINS: unable to Create VINS in RG", err.Error(), ) return 0, diags } tflog.Info(ctx, "CreateInRGResourceVINS: vins created", map[string]any{"vins_id": vinsId, "vins_name": plan.Name.ValueString()}) return vinsId, nil } // CreateInAccountResourceVINS creates vins in account based on plan. // Returns vins_id for created vins and errors in case of failures. func CreateInAccountResourceVINS(ctx context.Context, plan *models.ResourceVINSModel, c *client.Client) (uint64, diag.Diagnostics) { tflog.Info(ctx, fmt.Sprintf("Start CreateInAccountResourceVINS: vins_name %s", plan.Name.ValueString())) diags := diag.Diagnostics{} createReq := vins.CreateInAccountRequest{ Name: plan.Name.ValueString(), AccountID: uint64(plan.AccountID.ValueInt64()), } if !plan.GID.IsUnknown() { // IPCIDR is optional & computed createReq.GID = uint64(plan.GID.ValueInt64()) } if !plan.IPCIDR.IsNull() { // IPCIDR is optional createReq.IPCIDR = plan.IPCIDR.ValueString() } if !plan.Description.IsNull() { // Description is optional createReq.Description = plan.Description.ValueString() } if plan.PreReservationsNum.IsNull() { // PreReservationsNum is optional createReq.PreReservationsNum = uint64(32) // default value } else { createReq.PreReservationsNum = uint64(plan.PreReservationsNum.ValueInt64()) } if !plan.DNS.IsNull() { result := make([]string, 0, len(plan.DNS.Elements())) for _, val := range plan.DNS.Elements() { result = append(result, strings.Trim(val.String(), "\"")) } createReq.DNSList = result } tflog.Info(ctx, "CreateInAccountResourceVINS: before call CloudBroker().VINS().CreateInAccount", map[string]any{"req": createReq}) vinsId, err := c.CloudBroker().VINS().CreateInAccount(ctx, createReq) if err != nil { diags.AddError( "Create resourceVINS: unable to Create VINS in Account", err.Error(), ) return 0, diags } tflog.Info(ctx, "CreateInAccountResourceVINS: vins created", map[string]any{"vins_id": vinsId, "vins_name": plan.Name.ValueString()}) return vinsId, nil } // IPCreateVINS reserves ips that user specified in ip field for created resource. // In case of failure returns warnings. func IPCreateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} // plan.IP is not null as it was checked before call ipPlan := make([]models.IPModel, 0, len(plan.IP.Elements())) tflog.Info(ctx, "IPCreateVINS: new ip specified", map[string]any{"vins_id": vinsId}) diagsItem := plan.IP.ElementsAs(ctx, &ipPlan, true) if diagsItem.HasError() { tflog.Error(ctx, fmt.Sprintf("IPCreateVINS: cannot populate ipPlan with plan.IP list elements: %v", diagsItem)) diags.AddWarning("IPCreateVINS: Unable to read ip for vins", fmt.Sprintf("%v", diagsItem)) return diags } for _, ip := range ipPlan { ipReserveReq := vins.IPReserveRequest{ VINSID: vinsId, Type: ip.Type.ValueString(), } if ip.IPAddr.ValueString() != "" { ipReserveReq.IPAddr = ip.IPAddr.ValueString() } if ip.MacAddr.ValueString() != "" { ipReserveReq.MAC = ip.MacAddr.ValueString() } if ip.ComputeID.ValueInt64() != 0 { ipReserveReq.ComputeID = uint64(ip.ComputeID.ValueInt64()) } tflog.Info(ctx, "IPCreateVINS: before calling CloudBroker().VINS().IPReserve", map[string]any{ "vins_id": vinsId, "ipReserveReq": ipReserveReq}) res, err := c.CloudBroker().VINS().IPReserve(ctx, ipReserveReq) if err != nil { diags.AddWarning("IPCreateVINS: Unable to reserve ip for vins", err.Error()) } tflog.Info(ctx, "IPCreateVINS: response from CloudBroker().VINS().IPReserve", map[string]any{ "vins_id": vinsId, "response": res}) } return diags } // IPUpdateVINS reserves/releases ips that user specified in ip field for updated resource. // In case of failure returns errors. func IPUpdateVINS(ctx context.Context, vinsId uint64, plan, state *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { tflog.Info(ctx, "Start IPUpdateVINS: new ip specified", map[string]any{"vins_id": vinsId}) diags := diag.Diagnostics{} ipPlan := make([]models.IPModel, 0, len(plan.IP.Elements())) tflog.Info(ctx, "IPUpdateVINS: new ip specified", map[string]any{"vins_id": vinsId}) diags.Append(plan.IP.ElementsAs(ctx, &ipPlan, true)...) if diags.HasError() { tflog.Error(ctx, "IPUpdateVINS: cannot populate ipPlan with plan.IP list elements") return diags } ipState := make([]models.IPModel, 0, len(state.IP.Elements())) tflog.Info(ctx, "IPUpdateVINS: new ip specified", map[string]any{"vins_id": vinsId}) diags.Append(state.IP.ElementsAs(ctx, &ipState, true)...) if diags.HasError() { tflog.Error(ctx, "IPUpdateVINS: cannot populate ipState with state.IP list elements") return diags } // define ip to be released and release them var deletedIP []models.IPModel for _, ipStateElem := range ipState { if !ipStateElem.Contains(ipPlan) { deletedIP = append(deletedIP, ipStateElem) } } if len(deletedIP) == 0 { tflog.Info(ctx, "IPUpdateVINS: no ip needs to be release", map[string]any{"vins_id": plan.Id.ValueString()}) } if len(deletedIP) > 0 { tflog.Info(ctx, "IPUpdateVINS: ip needs to be released", map[string]any{ "vins_id": plan.Id.ValueString(), "deletedIP": deletedIP}) for _, deletedIPItem := range deletedIP { releaseIPReq := vins.IPReleaseRequest{ VINSID: vinsId, IPAddr: deletedIPItem.IPAddr.ValueString(), MAC: deletedIPItem.MacAddr.ValueString(), } tflog.Info(ctx, "IPUpdateVINS: before calling CloudBroker().VINS().IPRelese", map[string]any{"vins_id": plan.Id.ValueString(), "req": releaseIPReq}) res, err := c.CloudBroker().VINS().IPRelease(ctx, releaseIPReq) tflog.Info(ctx, "IPUpdateVINS: response from CloudBroker().VINS().IPRelese", map[string]any{"vins_id": plan.Id.ValueString(), "response": res}) if err != nil { diags.AddError( "IPUpdateVINS: can not release ip for VINS", err.Error()) } } } // define ips to be reserved and reserve them var addedIP []models.IPModel for _, ipPlanElem := range ipPlan { if !ipPlanElem.Contains(ipState) { addedIP = append(addedIP, ipPlanElem) } } if len(addedIP) == 0 { tflog.Info(ctx, "IPUpdateVINS: no ip needs to be reserved", map[string]any{"vins_id": plan.Id.ValueString()}) } if len(addedIP) > 0 { tflog.Info(ctx, "IPUpdateVINS: ip needs to be reserved", map[string]any{ "vins_id": plan.Id.ValueString(), "addedIP": addedIP}) for _, addedIPItem := range addedIP { ipReserveReq := vins.IPReserveRequest{ VINSID: vinsId, Type: addedIPItem.Type.ValueString(), } if addedIPItem.IPAddr.ValueString() != "" { ipReserveReq.IPAddr = addedIPItem.IPAddr.ValueString() } if addedIPItem.MacAddr.ValueString() != "" { ipReserveReq.MAC = addedIPItem.MacAddr.ValueString() } if addedIPItem.ComputeID.ValueInt64() != 0 { ipReserveReq.ComputeID = uint64(addedIPItem.ComputeID.ValueInt64()) } tflog.Info(ctx, "IPUpdateVINS: before calling CloudBroker().VINS().IPReserve", map[string]any{ "vins_id": vinsId, "ipReserveReq": ipReserveReq}) res, err := c.CloudBroker().VINS().IPReserve(ctx, ipReserveReq) if err != nil { diags.AddError("IPUpdateVINS: Unable to reserve ip for vins", err.Error()) } tflog.Info(ctx, "IPUpdateVINS: response from CloudBroker().VINS().IPReserve", map[string]any{ "vins_id": vinsId, "response": res}) } } return diags } // ExtNetUpdateVINS updates ext_net_id and/or ext_net_ip that user specified in ext_net block for updated resource. // In case of failure returns errors. func ExtNetUpdateVINS(ctx context.Context, vinsId uint64, plan, state *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { tflog.Info(ctx, "Start ExtNetUpdateVINS: new ext_net specified", map[string]any{ "vins_id": vinsId, }) diags := diag.Diagnostics{} if !state.ExtNet.IsNull() { disconReq := vins.ExtNetDisconnectRequest{ VINSID: vinsId, } tflog.Info(ctx, "ExtNetUpdateVINS: before calling CloudBroker().VINS().ExtNetDisconnect", map[string]any{"vins_id": plan.Id.ValueString(), "req": disconReq}) res, err := c.CloudBroker().VINS().ExtNetDisconnect(ctx, disconReq) tflog.Info(ctx, "ExtNetUpdateVINS: response from CloudBroker().VINS().ExtNetDisconnect", map[string]any{"vins_id": plan.Id.ValueString(), "response": res}) if err != nil { diags.AddError( "ExtNetUpdateVINS: can not disconnect extnet for VINS", err.Error()) } } if !plan.ExtNet.IsNull() { var extnetPlan models.ExtNetModel tflog.Info(ctx, "ExtNetUpdateVINS: new extnet specified", map[string]any{"name": plan.Name.ValueString()}) diags.Append(plan.ExtNet.As(ctx, &extnetPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true})...) if diags.HasError() { tflog.Error(ctx, "ExtNetUpdateVINS: cannot populate extnet with plan.ExtNet object element") return diags } conReq := vins.ExtNetConnectRequest{ VINSID: vinsId, } if !extnetPlan.ExtNetID.IsNull() { conReq.NetID = uint64(extnetPlan.ExtNetID.ValueInt64()) } if !extnetPlan.ExtNetIP.IsNull() { conReq.IP = extnetPlan.ExtNetIP.ValueString() } tflog.Info(ctx, "ExtNetUpdateVINS: before calling CloudBroker().VINS().ExtNetConnect", map[string]any{"vins_id": plan.Id.ValueString(), "req": conReq}) res, err := c.CloudBroker().VINS().ExtNetConnect(ctx, conReq) tflog.Info(ctx, "ExtNetUpdateVINS: response from CloudBroker().VINS().ExtNetConnect", map[string]any{"vins_id": plan.Id.ValueString(), "response": res}) if err != nil { diags.AddError( "ExtNetUpdateVINS: can not connect extnet to VINS", err.Error()) } } return diags } // NATRuleCreateVINS adds nat rules that user specified in nat_rule field for created resource. // In case of failure returns warnings. func NATRuleCreateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} // plan.NatRule is not null as it was checked before call natRulePlan := make([]models.NatRuleResourceModel, 0, len(plan.NatRule.Elements())) tflog.Info(ctx, "NATRuleCreateVINS: new natRule specified", map[string]any{"vins_id": vinsId}) diagsItem := plan.NatRule.ElementsAs(ctx, &natRulePlan, false) if diagsItem.HasError() { tflog.Error(ctx, fmt.Sprintf("NATRuleCreateVINS: cannot populate natRulePlan with plan.NatRule list elements: %v", diagsItem)) diags.AddWarning("NATRuleCreateVINS: Unable to add nat rule for vins", fmt.Sprintf("%v", diagsItem)) return diags } for _, nat := range natRulePlan { natAddReq := vins.NATRuleAddRequest{ VINSID: vinsId, IntIP: nat.IntIP.ValueString(), IntPort: uint64(nat.IntPort.ValueInt64()), ExtPortStart: uint64(nat.ExtPortStart.ValueInt64()), } if !nat.ExtPortEnd.IsUnknown() { natAddReq.ExtPortEnd = uint64(nat.ExtPortEnd.ValueInt64()) } if !nat.Proto.IsUnknown() { natAddReq.Proto = nat.Proto.ValueString() } tflog.Info(ctx, "NATRuleCreateVINS: before calling CloudBroker().VINS().NATRuleAdd", map[string]any{ "vins_id": vinsId, "natAddReq": natAddReq}) res, err := c.CloudBroker().VINS().NATRuleAdd(ctx, natAddReq) if err != nil { diags.AddWarning("NATRuleCreateVINS: Unable to add nat rule for vins", err.Error()) } tflog.Info(ctx, "NATRuleCreateVINS: response from CloudBroker().VINS().NATRuleAdd", map[string]any{ "vins_id": vinsId, "response": res}) } return diags } // DefaultQosCreateVINS update qos that user specified in defaultQos field for created resource. // In case of failure returns warnings. func DefaultQosCreateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} // plan.DefaultQOS is not null as it was checked before call var defaultQosPlan models.QOSModel tflog.Info(ctx, "DefaultQosCreateVINS: defaultQos specified", map[string]any{"name": plan.Name.ValueString()}) diags.Append(plan.DefaultQOS.As(ctx, &defaultQosPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true})...) if diags.HasError() { tflog.Error(ctx, "DefaultQosCreateVINS: cannot populate defaultQosPlan with plan.DefaultQOS object") diags.AddWarning("DefaultQosCreateVINS: Unable to update defaultQos for vins", fmt.Sprintf("cannot populate defaultQosPlan with plan.DefaultQOS object")) return diags } qosReq := vins.DefaultQOSUpdateRequest{ VINSID: vinsId, } if !defaultQosPlan.InRate.IsUnknown() { qosReq.IngressRate = uint64(defaultQosPlan.InRate.ValueInt64()) } if !defaultQosPlan.InBurst.IsUnknown() { qosReq.IngressBirst = uint64(defaultQosPlan.InBurst.ValueInt64()) } if !defaultQosPlan.ERate.IsUnknown() { qosReq.EgressRate = uint64(defaultQosPlan.ERate.ValueInt64()) } tflog.Info(ctx, "DefaultQosCreateVINS: before calling CloudBroker().VINS().DefaultQOSUpdate", map[string]any{ "vins_id": vinsId, "natAddReq": qosReq}) res, err := c.CloudBroker().VINS().DefaultQOSUpdate(ctx, qosReq) if err != nil { diags.AddWarning("DefaultQosCreateVINS: Unable to update defaultQos for vins", err.Error()) } tflog.Info(ctx, "DefaultQosCreateVINS: response from CloudBroker().VINS().DefaultQOSUpdate", map[string]any{ "vins_id": vinsId, "response": res}) return diags } // NATRuleUpdateVINS adds/deleted nat rules that user specified in nat_rule field for updated resource. // In case of failure returns errors. func NATRuleUpdateVINS(ctx context.Context, vinsId uint64, plan, state *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { tflog.Info(ctx, "Start NATRuleUpdateVINS: new natRule specified", map[string]any{"vins_id": vinsId}) diags := diag.Diagnostics{} itemsNatRulePlan := make([]models.NatRuleResourceModel, 0, len(plan.NatRule.Elements())) diags.Append(plan.NatRule.ElementsAs(ctx, &itemsNatRulePlan, false)...) if diags.HasError() { tflog.Error(ctx, "NATRuleUpdateVINS: cannot populate natRulePlan with plan.NatRule list elements") return diags } itemsNatRuleState := make([]models.NatRuleResourceModel, 0, len(state.NatRule.Elements())) diags.Append(state.NatRule.ElementsAs(ctx, &itemsNatRuleState, false)...) if diags.HasError() { tflog.Error(ctx, "NATRuleUpdateVINS: cannot populate natRuleState with state.NatRule list elements") return diags } // define nat rules to be deleted and delete them var deletedNatRule []models.NatRuleResourceModel for _, natRuleStateElem := range itemsNatRuleState { if !natRuleStateElem.Contains(itemsNatRulePlan) { deletedNatRule = append(deletedNatRule, natRuleStateElem) } } if len(deletedNatRule) == 0 { tflog.Info(ctx, "NATRuleUpdateVINS: no natRule needs to be deleted", map[string]any{"vins_id": plan.Id.ValueString()}) } if len(deletedNatRule) > 0 { tflog.Info(ctx, "NATRuleUpdateVINS: natRule needs to be deleted", map[string]any{ "vins_id": plan.Id.ValueString(), "deletedNatRule": deletedNatRule}) for _, deletedNatRuleItem := range deletedNatRule { deleteNATReq := vins.NATRuleDelRequest{ VINSID: vinsId, RuleID: deletedNatRuleItem.RuleID.ValueInt64(), } tflog.Info(ctx, "NATRuleUpdateVINS: before calling CloudBroker().VINS().NATRuleDel", map[string]any{"vins_id": plan.Id.ValueString(), "req": deleteNATReq}) res, err := c.CloudBroker().VINS().NATRuleDel(ctx, deleteNATReq) tflog.Info(ctx, "NATRuleUpdateVINS: response from CloudBroker().VINS().NATRuleDel", map[string]any{"vins_id": plan.Id.ValueString(), "response": res}) if err != nil { diags.AddError( "NATRuleUpdateVINS: can not delete nat rule for VINS", err.Error()) } } } // define nat rules to be added and add them var addedNatRules []models.NatRuleResourceModel for _, natRulePlanElem := range itemsNatRulePlan { if !natRulePlanElem.Contains(itemsNatRuleState) { addedNatRules = append(addedNatRules, natRulePlanElem) } } if len(addedNatRules) == 0 { tflog.Info(ctx, "NATRuleUpdateVINS: no nat rule needs to be added", map[string]any{"vins_id": plan.Id.ValueString()}) } if len(addedNatRules) > 0 { tflog.Info(ctx, "NATRuleUpdateVINS: nat rule needs to be added", map[string]any{ "vins_id": plan.Id.ValueString(), "addedNatRules": addedNatRules}) for _, addedNatRuleItem := range addedNatRules { natAddReq := vins.NATRuleAddRequest{ VINSID: vinsId, IntIP: addedNatRuleItem.IntIP.ValueString(), IntPort: uint64(addedNatRuleItem.IntPort.ValueInt64()), ExtPortStart: uint64(addedNatRuleItem.ExtPortStart.ValueInt64()), } if !addedNatRuleItem.ExtPortEnd.IsUnknown() { natAddReq.ExtPortEnd = uint64(addedNatRuleItem.ExtPortEnd.ValueInt64()) } if !addedNatRuleItem.Proto.IsUnknown() { natAddReq.Proto = addedNatRuleItem.Proto.ValueString() } tflog.Info(ctx, "NATRuleUpdateVINS: before calling CloudBroker().VINS().NATRuleAdd", map[string]any{ "vins_id": vinsId, "natAddReq": natAddReq}) res, err := c.CloudBroker().VINS().NATRuleAdd(ctx, natAddReq) if err != nil { diags.AddError("NATRuleUpdateVINS: Unable to add nat rule for vins", err.Error()) } tflog.Info(ctx, "NATRuleUpdateVINS: response from CloudBroker().VINS().NATRuleAdd", map[string]any{ "vins_id": vinsId, "response": res}) } } return diags } // VINSReadStatus loads vins resource by ids id, gets it current status. Performs restore and enable if needed for // Deleted status. // In case of failure returns errors. func VINSReadStatus(ctx context.Context, state *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { tflog.Info(ctx, "VINSReadStatus: Read status vins with ID", map[string]any{"vins_id": state.Id.ValueString()}) diags := diag.Diagnostics{} vinsId, err := strconv.ParseUint(state.Id.ValueString(), 10, 64) if err != nil { diags.AddError("VINSReadStatus: Cannot parse vins ID from state", err.Error()) return diags } recordVINS, diags := VINSResourceCheckPresence(ctx, vinsId, c) if err != nil { diags.AddError("VINSReadStatus: Unable to Read/Update VINS before status check", err.Error()) return diags } // check resource status switch recordVINS.Status { case status.Modeled: diags.AddError( "VINS is in status Modeled", "please, contact support for more information", ) return diags case status.Deleted: // attempt to restore vins tflog.Info(ctx, "VINSReadStatus: vins with status.Deleted is being read or updated, attempt to restore it", map[string]any{ "vins_id": recordVINS.ID, "status": recordVINS.Status}) if state.Restore.IsNull() || state.Restore.ValueBool() { // default true or user set-up true diags.Append(RestoreVINS(ctx, vinsId, c)...) if diags.HasError() { tflog.Error(ctx, "VINSReadStatus: cannot restore vins") return diags } tflog.Info(ctx, "VINSReadStatus: vins restored successfully", map[string]any{"vins_id": vinsId}) if state.Enable.IsNull() || state.Enable.ValueBool() { diags.Append(EnableVINS(ctx, vinsId, c)...) if diags.HasError() { tflog.Error(ctx, "VINSReadStatus: cannot enable vins") return diags } tflog.Info(ctx, "VINSReadStatus: vins enabled successfully", map[string]any{"vins_id": vinsId}) } state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) } case status.Enabled: if !state.Enable.ValueBool() && !state.Enable.IsNull() { tflog.Info(ctx, "VINSReadStatus: vins with status.Enabled is being read or updated but should not be according to configuration (enable=false), attempt to disable it", map[string]any{ "vins_id": recordVINS.ID, "status": recordVINS.Status}) diags.Append(DisableVINS(ctx, vinsId, c)...) if diags.HasError() { tflog.Error(ctx, "VINSReadStatus: cannot disable vins") return diags } tflog.Info(ctx, "VINSReadStatus: vins disabled successfully", map[string]any{"vins_id": vinsId}) state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) } case status.Disabled: if state.Enable.ValueBool() { tflog.Info(ctx, "VINSReadStatus: vins with status.Disabled is being read or updated but should not be according to configuration (enable=true), attempt to enable it", map[string]any{ "vins_id": recordVINS.ID, "status": recordVINS.Status}) diags.Append(EnableVINS(ctx, vinsId, c)...) if diags.HasError() { tflog.Error(ctx, "VINSReadStatus: cannot enable vins") return diags } tflog.Info(ctx, "VINSReadStatus: vins enabled successfully", map[string]any{"vins_id": vinsId}) state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) } case status.Destroyed: diags.AddError( "VINSReadStatus: vins is in status Destroyed", fmt.Sprintf("the resource with vins_id %d cannot be read or updated because it has been destroyed", vinsId), ) return diags } return nil } // RestoreVINS performs vins Restore request. // Returns error in case of failures. func RestoreVINS(ctx context.Context, vinsId uint64, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "RestoreVINS: before calling CloudBroker().VINS().Restore", map[string]any{"vinsId": vinsId, "req": vins.RestoreRequest{VINSID: vinsId}}) res, err := c.CloudBroker().VINS().Restore(ctx, vins.RestoreRequest{VINSID: vinsId}) if err != nil { diags.AddError( "RestoreVINS: cannot restore vins", err.Error(), ) return diags } tflog.Info(ctx, "RestoreVINS: response from CloudBroker().VINS().Restore", map[string]any{"vinsId": vinsId, "response": res}) return nil } // DisableVINS performs vins Disable request. // Returns error in case of failures. func DisableVINS(ctx context.Context, vinsId uint64, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "DisableVINS: before calling CloudBroker().VINS().Disable", map[string]any{"vinsId": vinsId}) res, err := c.CloudBroker().VINS().Disable(ctx, vins.DisableRequest{VINSID: vinsId}) if err != nil { diags.AddError( "DisableVINS: cannot disable vins", err.Error(), ) return diags } tflog.Info(ctx, "DisableVINS: response from CloudBroker().VINS().Disable", map[string]any{"vinsId": vinsId, "response": res}) return nil } // EnableVINS performs vins Enable request. // Returns error in case of failures. func EnableVINS(ctx context.Context, vinsId uint64, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "EnableVINS: before calling CloudBroker().VINS().Enable", map[string]any{"vinsId": vinsId}) res, err := c.CloudBroker().VINS().Enable(ctx, vins.EnableRequest{VINSID: vinsId}) if err != nil { diags.AddError( "EnableVINS: cannot enable vins", err.Error(), ) return diags } tflog.Info(ctx, "EnableVINS: response from CloudBroker().VINS().Enable", map[string]any{"vinsId": vinsId, "response": res}) return nil } // EnableDisableUpdateVINS performs vins Enable/disable request. // Returns errors in case of failures. func EnableDisableUpdateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} enable := plan.Enable.ValueBool() tflog.Info(ctx, "Start EnableDisableUpdateVINS", map[string]any{"vinsId": vinsId, "enable": enable}) if enable { diags.Append(EnableVINS(ctx, vinsId, c)...) return diags } if !enable { diags.Append(DisableVINS(ctx, vinsId, c)...) return diags } return nil } // VnfdevRestartUpdateVINS restarts vnf_dev for vins. // Returns error in case of failures. func VnfdevRestartUpdateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "VnfdevRestartUpdateVINS: before calling CloudBroker().VINS().VNFDevRestart", map[string]any{"vinsId": vinsId}) req := vins.VNFDevRestartRequest{ VINSID: vinsId, } res, err := c.CloudBroker().VINS().VNFDevRestart(ctx, req) if err != nil { diags.AddError( "VnfdevRestartUpdateVINS: cannot restart vnf_dev for vins", err.Error(), ) return diags } tflog.Info(ctx, "VnfdevRestartUpdateVINS: response from CloudBroker().VINS().VNFDevRestart", map[string]any{"vinsId": vinsId, "response": res}) return nil } // VnfdevRedeployUpdateVINS redeploys vnf_dev for vins. // Returns error in case of failures. func VnfdevRedeployUpdateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "VnfdevRedeployUpdateVINS: before calling CloudBroker().VINS().VNFDevRedeploy", map[string]any{"vinsId": vinsId}) req := vins.VNFDevRedeployRequest{ VINSID: vinsId, } res, err := c.CloudBroker().VINS().VNFDevRedeploy(ctx, req) if err != nil { diags.AddError( "VnfdevRedeployUpdateVINS: cannot redeploy vnf_dev for vins", err.Error(), ) return diags } tflog.Info(ctx, "VnfdevRedeployUpdateVINS: response from CloudBroker().VINS().VNFDevRedeploy", map[string]any{"vinsId": vinsId, "response": res}) return nil } // VnfdevResetUpdateVINS reset vnf_dev for vins. // Returns error in case of failures. func VnfdevResetUpdateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} tflog.Info(ctx, "VnfdevResetUpdateVINS: before calling CloudBroker().VINS().VNFDevReset", map[string]any{"vinsId": vinsId}) req := vins.VNFDevResetRequest{ VINSID: vinsId, } res, err := c.CloudBroker().VINS().VNFDevReset(ctx, req) if err != nil { diags.AddError( "VnfdevResetUpdateVINS: cannot reset vnf_dev for vins", err.Error(), ) return diags } tflog.Info(ctx, "VnfdevResetUpdateVINS: response from CloudBroker().VINS().VNFDevReset", map[string]any{"vinsId": vinsId, "response": res}) return nil } // VnfdevStartStopUpdateVINS start/stop vnf_dev for vins. // Returns error in case of failures. func VnfdevStartStopUpdateVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} if plan.VnfdevStart.ValueBool() { req := vins.VNFDevStartRequest{ VINSID: vinsId, } tflog.Info(ctx, "VnfdevResetUpdateVINS: before calling CloudBroker().VINS().VNFDevStart", map[string]any{"vinsId": vinsId}) res, err := c.CloudBroker().VINS().VNFDevStart(ctx, req) if err != nil { diags.AddError( "VnfdevResetUpdateVINS: cannot start vnf_dev for vins", err.Error(), ) return diags } tflog.Info(ctx, "VnfdevResetUpdateVINS: response from CloudBroker().VINS().VNFDevStart", map[string]any{"vinsId": vinsId, "response": res}) return nil } req := vins.VNFDevStopRequest{ VINSID: vinsId, } tflog.Info(ctx, "VnfdevResetUpdateVINS: before calling CloudBroker().VINS().VNFDevStop", map[string]any{"vinsId": vinsId}) res, err := c.CloudBroker().VINS().VNFDevStop(ctx, req) if err != nil { diags.AddError( "VnfdevResetUpdateVINS: cannot start vnf_dev for vins", err.Error(), ) return diags } tflog.Info(ctx, "VnfdevResetUpdateVINS: response from CloudBroker().VINS().VNFDevStop", map[string]any{"vinsId": vinsId, "response": res}) return nil } // UpdateDNSlistVINS apply new DNS list in VINS // Returns error in case of failures. func UpdateDNSlistVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} req := vins.DNSApplyRequest{ VINSID: vinsId, } dnsList := make([]string, 0, len(plan.DNS.Elements())) for _, val := range plan.DNS.Elements() { dnsList = append(dnsList, strings.Trim(val.String(), "\"")) } req.DNSList = dnsList tflog.Info(ctx, "UpdateDNSListVINS: before calling CloudBroker().VINS().DNSApply", map[string]any{"vinsId": vinsId}) res, err := c.CloudBroker().VINS().DNSApply(ctx, req) if err != nil { diags.AddError( "UpdateDNSListVINS: cannot apply DNSList for vins", err.Error(), ) return diags } tflog.Info(ctx, "UpdateDNSListVINS: after calling CloudBroker().VINS().DNSApply", map[string]any{"vinsId": vinsId, "response": res}) return nil } // UpdateDefaultQosVINS update qos that user specified in defaultQos field for update resource. // In case of failure returns error. func UpdateDefaultQosVINS(ctx context.Context, vinsId uint64, plan *models.ResourceVINSModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} // plan.DefaultQOS is not null as it was checked before call var defaultQosPlan models.QOSModel tflog.Info(ctx, "DefaultQosUpdateVINS: defaultQos specified", map[string]any{"name": plan.Name.ValueString()}) diags.Append(plan.DefaultQOS.As(ctx, &defaultQosPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true})...) if diags.HasError() { tflog.Error(ctx, "DefaultQosUpdateVINS: cannot populate defaultQosPlan with plan.DefaultQOS object") return diags } qosReq := vins.DefaultQOSUpdateRequest{ VINSID: vinsId, } if !defaultQosPlan.InRate.IsUnknown() { qosReq.IngressRate = uint64(defaultQosPlan.InRate.ValueInt64()) } if !defaultQosPlan.InBurst.IsUnknown() { qosReq.IngressBirst = uint64(defaultQosPlan.InBurst.ValueInt64()) } if !defaultQosPlan.ERate.IsUnknown() { qosReq.EgressRate = uint64(defaultQosPlan.ERate.ValueInt64()) } tflog.Info(ctx, "DefaultQosUpdateVINS: before calling CloudBroker().VINS().DefaultQOSUpdate", map[string]any{ "vins_id": vinsId, "natAddReq": qosReq}) res, err := c.CloudBroker().VINS().DefaultQOSUpdate(ctx, qosReq) if err != nil { diags.AddError("DefaultQosUpdateVINS: Unable to update defaultQos for vins", err.Error()) } tflog.Info(ctx, "DefaultQosUpdateVINS: response from CloudBroker().VINS().DefaultQOSUpdate", map[string]any{ "vins_id": vinsId, "response": res}) return diags }