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"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/vins"
"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 * decort . DecortClient ) ( * 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 * decort . DecortClient ) ( 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 * decort . DecortClient ) ( 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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 * decort . DecortClient ) 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
}