Files
cgrates/actions/dynamic.go

767 lines
24 KiB
Go

/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package actions
import (
"cmp"
"fmt"
"slices"
"strconv"
"strings"
"time"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// parseParamStringToMap parses a string containing key-value pairs separated by "&" and assigns
// these pairs to a given map. Each pair is expected to be in the format "key:value".
func parseParamStringToMap(paramStr string, targetMap map[string]any) error {
for tuple := range strings.SplitSeq(paramStr, utils.ANDSep) {
// Use strings.Cut to split 'tuple' into key-value pairs at the first occurrence of ':'.
// This ensures that additional ':' characters within the value do not affect parsing.
key, value, found := strings.Cut(tuple, utils.InInFieldSep)
if !found {
return fmt.Errorf("invalid key-value pair: %s", tuple)
}
targetMap[key] = value
}
return nil
}
// actDynamicThreshold processes the `ActionDiktatsOpts` field from the action to construct a Threshold profile
//
// The ActionDiktatsOpts field format is expected as follows:
//
// 0 Tenant: string
// 1 ID: string
// 2 FilterIDs: strings separated by "&".
// 3 Weight: strings separated by "&". Should be higher than the threshold weight that
// triggers this action
// 4 MaxHits: integer
// 5 MinHits: integer
// 6 MinSleep: duration
// 7 Blocker: bool, should always be true
// 8 ActionProfileIDs: strings separated by "&".
// 9 Async: bool
// 10 APIOpts: set of key-value pairs (separated by "&").
//
// Parameters are separated by ";" and must be provided in the specified order.
type actDynamicThreshold struct {
config *config.CGRConfig
connMgr *engine.ConnManager
fltrS *engine.FilterS
aCfg *utils.APAction
tnt string
cgrEv *utils.CGREvent
}
func (aL *actDynamicThreshold) id() string {
return aL.aCfg.ID
}
func (aL *actDynamicThreshold) cfg() *utils.APAction {
return aL.aCfg
}
// execute implements actioner interface
func (aL *actDynamicThreshold) execute(ctx *context.Context, data utils.MapStorage, trgID string) (err error) {
if len(aL.config.ActionSCfg().AdminSConns) == 0 {
return fmt.Errorf("no connection with AdminS")
}
data[utils.MetaNow] = time.Now()
data[utils.MetaTenant] = utils.FirstNonEmpty(aL.cgrEv.Tenant, aL.tnt,
config.CgrConfig().GeneralCfg().DefaultTenant)
// Parse action parameters based on the predefined format.
if len(aL.aCfg.Diktats) == 0 {
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
}
weights := make(map[string]float64) // stores sorting weights by Diktat ID
diktats := make([]*utils.APDiktat, 0) // list of diktats which have *template in opts, will be weight sorted later
for _, diktat := range aL.aCfg.Diktats {
if pass, err := aL.fltrS.Pass(ctx, aL.tnt, diktat.FilterIDs, data); err != nil {
return err
} else if !pass {
continue
}
weight, err := engine.WeightFromDynamics(ctx, diktat.Weights, aL.fltrS, aL.tnt, data)
if err != nil {
return err
}
weights[diktat.ID] = weight
diktats = append(diktats, diktat)
}
// Sort by weight (higher values first).
slices.SortFunc(diktats, func(a, b *utils.APDiktat) int {
return cmp.Compare(weights[b.ID], weights[a.ID])
})
for _, diktat := range diktats {
params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]),
utils.InfieldSep)
if len(params) != 11 {
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
}
// parse dynamic parameters
for i := range params {
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
return err
}
}
// Prepare request arguments based on provided parameters.
args := &engine.ThresholdProfileWithAPIOpts{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: params[0],
ID: params[1],
},
APIOpts: make(map[string]any),
}
// populate Threshold's FilterIDs
if params[2] != utils.EmptyString {
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
}
// populate Threshold's Weight
if params[3] != utils.EmptyString {
args.Weights = utils.DynamicWeights{&utils.DynamicWeight{}}
wghtSplit := strings.Split(params[3], utils.ANDSep)
if len(wghtSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if wghtSplit[0] != utils.EmptyString {
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
}
if wghtSplit[1] != utils.EmptyString {
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
if err != nil {
return err
}
}
}
// populate Threshold's MaxHits
if params[4] != utils.EmptyString {
args.MaxHits, err = strconv.Atoi(params[4])
if err != nil {
return err
}
}
// populate Threshold's MinHits
if params[5] != utils.EmptyString {
args.MinHits, err = strconv.Atoi(params[5])
if err != nil {
return err
}
}
// populate Threshold's MinSleep
if params[6] != utils.EmptyString {
args.MinSleep, err = utils.ParseDurationWithNanosecs(params[6])
if err != nil {
return err
}
}
// populate Threshold's Blocker
if params[7] != utils.EmptyString {
args.Blocker, err = strconv.ParseBool(params[7])
if err != nil {
return err
}
}
// populate Threshold's ActionProfileIDs
if params[8] != utils.EmptyString {
args.ActionProfileIDs = strings.Split(params[8], utils.ANDSep)
}
// populate Threshold's Async bool
if params[9] != utils.EmptyString {
args.Async, err = strconv.ParseBool(params[9])
if err != nil {
return err
}
}
// populate Threshold's APIOpts
if params[10] != utils.EmptyString {
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
return err
}
}
// create the ThresholdProfile based on the populated parameters
var rply string
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
utils.AdminSv1SetThresholdProfile, args, &rply); err != nil {
return err
}
if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil {
return err
} else if blocker {
break
}
}
return
}
// actDynamicStats processes the `ActionDiktatsOpts` field from the action to construct a StatQueueProfile
//
// The ActionDiktatsOpts field format is expected as follows:
//
// 0 Tenant: string
// 1 ID: string
// 2 FilterIDs: strings separated by "&".
// 3 Weights: strings separated by "&".
// 4 Blockers: strings separated by "&".
// 5 QueueLength: integer
// 6 TTL: duration
// 7 MinItems: integer
// 8 Stored: bool
// 9 ThresholdIDs: strings separated by "&".
// 10 MetricIDs: strings separated by "&".
// 11 MetricFilterIDs: strings separated by "&".
// 12 MetricBlockers: strings separated by "&".
// 13 APIOpts: set of key-value pairs (separated by "&").
//
// Parameters are separated by ";" and must be provided in the specified order.
type actDynamicStats struct {
config *config.CGRConfig
connMgr *engine.ConnManager
fltrS *engine.FilterS
aCfg *utils.APAction
tnt string
cgrEv *utils.CGREvent
}
func (aL *actDynamicStats) id() string {
return aL.aCfg.ID
}
func (aL *actDynamicStats) cfg() *utils.APAction {
return aL.aCfg
}
// execute implements actioner interface
func (aL *actDynamicStats) execute(ctx *context.Context, data utils.MapStorage, trgID string) (err error) {
if len(aL.config.ActionSCfg().AdminSConns) == 0 {
return fmt.Errorf("no connection with AdminS")
}
data[utils.MetaNow] = time.Now()
data[utils.MetaTenant] = utils.FirstNonEmpty(aL.cgrEv.Tenant, aL.tnt,
config.CgrConfig().GeneralCfg().DefaultTenant)
// Parse action parameters based on the predefined format.
if len(aL.aCfg.Diktats) == 0 {
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
}
weights := make(map[string]float64) // stores sorting weights by Diktat ID
diktats := make([]*utils.APDiktat, 0) // list of diktats which have *template in opts, will be weight sorted later
for _, diktat := range aL.aCfg.Diktats {
if pass, err := aL.fltrS.Pass(ctx, aL.tnt, diktat.FilterIDs, data); err != nil {
return err
} else if !pass {
continue
}
weight, err := engine.WeightFromDynamics(ctx, diktat.Weights, aL.fltrS, aL.tnt, data)
if err != nil {
return err
}
weights[diktat.ID] = weight
diktats = append(diktats, diktat)
}
// Sort by weight (higher values first).
slices.SortFunc(diktats, func(a, b *utils.APDiktat) int {
return cmp.Compare(weights[b.ID], weights[a.ID])
})
for _, diktat := range diktats {
params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]),
utils.InfieldSep)
if len(params) != 14 {
return fmt.Errorf("invalid number of parameters <%d> expected 14", len(params))
}
// parse dynamic parameters
for i := range params {
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
return err
}
}
// Prepare request arguments based on provided parameters.
args := &engine.StatQueueProfileWithAPIOpts{
StatQueueProfile: &engine.StatQueueProfile{
Tenant: params[0],
ID: params[1],
},
APIOpts: make(map[string]any),
}
// populate Stat's FilterIDs
if params[2] != utils.EmptyString {
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
}
// populate Stat's Weights
if params[3] != utils.EmptyString {
args.Weights = utils.DynamicWeights{&utils.DynamicWeight{}}
wghtSplit := strings.Split(params[3], utils.ANDSep)
if len(wghtSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if wghtSplit[0] != utils.EmptyString {
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
}
if wghtSplit[1] != utils.EmptyString {
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
if err != nil {
return err
}
}
}
// populate Stat's Blockers
if params[4] != utils.EmptyString {
args.Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}}
blckrSplit := strings.Split(params[4], utils.ANDSep)
if len(blckrSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if blckrSplit[0] != utils.EmptyString {
args.Blockers[0].FilterIDs = []string{blckrSplit[0]}
}
if blckrSplit[1] != utils.EmptyString {
args.Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
if err != nil {
return err
}
}
}
// populate Stat's QueueLengh
if params[5] != utils.EmptyString {
args.QueueLength, err = strconv.Atoi(params[5])
if err != nil {
return err
}
}
// populate Stat's TTL
if params[6] != utils.EmptyString {
args.TTL, err = utils.ParseDurationWithNanosecs(params[6])
if err != nil {
return err
}
}
// populate Stat's MinItems
if params[7] != utils.EmptyString {
args.MinItems, err = strconv.Atoi(params[7])
if err != nil {
return err
}
}
// populate Stat's Stored
if params[8] != utils.EmptyString {
args.Stored, err = strconv.ParseBool(params[8])
if err != nil {
return err
}
}
// populate Stat's ThresholdIDs
if params[9] != utils.EmptyString {
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
}
// populate Stat's MetricID
if params[10] != utils.EmptyString {
metrics := strings.Split(params[10], utils.ANDSep)
args.Metrics = make([]*engine.MetricWithFilters, len(metrics))
for i, strM := range metrics {
args.Metrics[i] = &engine.MetricWithFilters{MetricID: strM}
}
}
// populate Stat's metricFliterIDs
if params[11] != utils.EmptyString {
metricFliters := strings.Split(params[11], utils.ANDSep)
for i := range args.Metrics {
args.Metrics[i].FilterIDs = metricFliters
}
}
// populate Stat's metricBlockers
if params[12] != utils.EmptyString {
blckrSplit := strings.Split(params[12], utils.ANDSep)
if len(blckrSplit) > 2 {
return utils.ErrUnsupportedFormat
}
for i := range args.Metrics {
args.Metrics[i].Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}}
if blckrSplit[0] != utils.EmptyString {
args.Metrics[i].Blockers[0].FilterIDs = []string{blckrSplit[0]}
}
if blckrSplit[1] != utils.EmptyString {
args.Metrics[i].Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
if err != nil {
return err
}
}
}
}
// populate Stat's APIOpts
if params[13] != utils.EmptyString {
if err := parseParamStringToMap(params[13], args.APIOpts); err != nil {
return err
}
}
// create the StatQueueProfile based on the populated parameters
var rply string
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
utils.AdminSv1SetStatQueueProfile, args, &rply); err != nil {
return err
}
if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil {
return err
} else if blocker {
break
}
}
return
}
// actDynamicAttribute processes the `ActionDiktatsOpts` field from the action to construct a AttributeProfile
//
// The ActionDiktatsOpts field format is expected as follows:
//
// 0 Tenant: string
// 1 ID: string
// 2 FilterIDs: strings separated by "&".
// 3 Weights: strings separated by "&".
// 4 Blocker: strings separated by "&".
// 5 AttributeFilterIDs: strings separated by "&".
// 6 AttributeBlockers: strings separated by "&".
// 7 Path: string
// 8 Type: string
// 9 Value: strings separated by "&".
// 10 APIOpts: set of key-value pairs (separated by "&").
//
// Parameters are separated by ";" and must be provided in the specified order.
type actDynamicAttribute struct {
config *config.CGRConfig
connMgr *engine.ConnManager
fltrS *engine.FilterS
aCfg *utils.APAction
tnt string
cgrEv *utils.CGREvent
}
func (aL *actDynamicAttribute) id() string {
return aL.aCfg.ID
}
func (aL *actDynamicAttribute) cfg() *utils.APAction {
return aL.aCfg
}
// execute implements actioner interface
func (aL *actDynamicAttribute) execute(ctx *context.Context, data utils.MapStorage, trgID string) (err error) {
if len(aL.config.ActionSCfg().AdminSConns) == 0 {
return fmt.Errorf("no connection with AdminS")
}
data[utils.MetaNow] = time.Now()
data[utils.MetaTenant] = utils.FirstNonEmpty(aL.cgrEv.Tenant, aL.tnt,
config.CgrConfig().GeneralCfg().DefaultTenant)
// Parse action parameters based on the predefined format.
if len(aL.aCfg.Diktats) == 0 {
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
}
weights := make(map[string]float64) // stores sorting weights by Diktat ID
diktats := make([]*utils.APDiktat, 0) // list of diktats which have *template in opts, will be weight sorted later
for _, diktat := range aL.aCfg.Diktats {
if pass, err := aL.fltrS.Pass(ctx, aL.tnt, diktat.FilterIDs, data); err != nil {
return err
} else if !pass {
continue
}
weight, err := engine.WeightFromDynamics(ctx, diktat.Weights, aL.fltrS, aL.tnt, data)
if err != nil {
return err
}
weights[diktat.ID] = weight
diktats = append(diktats, diktat)
}
// Sort by weight (higher values first).
slices.SortFunc(diktats, func(a, b *utils.APDiktat) int {
return cmp.Compare(weights[b.ID], weights[a.ID])
})
for _, diktat := range diktats {
params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]),
utils.InfieldSep)
if len(params) != 11 {
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
}
// parse dynamic parameters
for i := range params {
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
return err
}
}
// Prepare request arguments based on provided parameters.
args := &utils.APIAttributeProfileWithAPIOpts{
APIAttributeProfile: &utils.APIAttributeProfile{
Tenant: params[0],
ID: params[1],
},
APIOpts: make(map[string]any),
}
// populate Attribute's FilterIDs
if params[2] != utils.EmptyString {
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
}
// populate Attribute's Weights
if params[3] != utils.EmptyString {
args.Weights = utils.DynamicWeights{&utils.DynamicWeight{}}
wghtSplit := strings.Split(params[3], utils.ANDSep)
if len(wghtSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if wghtSplit[0] != utils.EmptyString {
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
}
if wghtSplit[1] != utils.EmptyString {
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
if err != nil {
return err
}
}
}
// populate Attribute's Blockers
if params[4] != utils.EmptyString {
args.Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}}
blckrSplit := strings.Split(params[4], utils.ANDSep)
if len(blckrSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if blckrSplit[0] != utils.EmptyString {
args.Blockers[0].FilterIDs = []string{blckrSplit[0]}
}
if blckrSplit[1] != utils.EmptyString {
args.Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
if err != nil {
return err
}
}
}
// populate Attribute's Attributes
if params[7] != utils.EmptyString {
var attrFltrIDs []string
if params[5] != utils.EmptyString {
attrFltrIDs = strings.Split(params[5], utils.ANDSep)
}
var attrFltrBlckrs utils.DynamicBlockers
if params[6] != utils.EmptyString {
attrFltrBlckrs = utils.DynamicBlockers{&utils.DynamicBlocker{}}
blckrSplit := strings.Split(params[6], utils.ANDSep)
if len(blckrSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if blckrSplit[0] != utils.EmptyString {
attrFltrBlckrs[0].FilterIDs = []string{blckrSplit[0]}
}
if blckrSplit[1] != utils.EmptyString {
attrFltrBlckrs[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
if err != nil {
return err
}
}
}
args.Attributes = append(args.Attributes, &utils.ExternalAttribute{
FilterIDs: attrFltrIDs,
Blockers: attrFltrBlckrs,
Path: params[7],
Type: params[8],
Value: params[9],
})
}
// populate Attribute's APIOpts
if params[10] != utils.EmptyString {
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
return err
}
}
// create the AttributeProfile based on the populated parameters
var rply string
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
utils.AdminSv1SetAttributeProfile, args, &rply); err != nil {
return err
}
if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil {
return err
} else if blocker {
break
}
}
return
}
// actDynamicResource processes the `ActionDiktatsOpts` field from the action to construct a ResourceProfile
//
// The ActionDiktatsOpts field format is expected as follows:
//
// 0 Tenant: string
// 1 ID: string
// 2 FilterIDs: strings separated by "&".
// 3 Weights: strings separated by "&".
// 4 TTL: duration
// 5 Limit: float
// 6 AllocationMessage: string
// 7 Blocker: bool
// 8 Stored: bool
// 9 ThresholdIDs: strings separated by "&".
// 10 APIOpts: set of key-value pairs (separated by "&").
//
// Parameters are separated by ";" and must be provided in the specified order.
type actDynamicResource struct {
config *config.CGRConfig
connMgr *engine.ConnManager
fltrS *engine.FilterS
aCfg *utils.APAction
tnt string
cgrEv *utils.CGREvent
}
func (aL *actDynamicResource) id() string {
return aL.aCfg.ID
}
func (aL *actDynamicResource) cfg() *utils.APAction {
return aL.aCfg
}
// execute implements actioner interface
func (aL *actDynamicResource) execute(ctx *context.Context, data utils.MapStorage, trgID string) (err error) {
if len(aL.config.ActionSCfg().AdminSConns) == 0 {
return fmt.Errorf("no connection with AdminS")
}
data[utils.MetaNow] = time.Now()
data[utils.MetaTenant] = utils.FirstNonEmpty(aL.cgrEv.Tenant, aL.tnt,
config.CgrConfig().GeneralCfg().DefaultTenant)
// Parse action parameters based on the predefined format.
if len(aL.aCfg.Diktats) == 0 {
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
}
weights := make(map[string]float64) // stores sorting weights by Diktat ID
diktats := make([]*utils.APDiktat, 0) // list of diktats which have *template in opts, will be weight sorted later
for _, diktat := range aL.aCfg.Diktats {
if pass, err := aL.fltrS.Pass(ctx, aL.tnt, diktat.FilterIDs, data); err != nil {
return err
} else if !pass {
continue
}
weight, err := engine.WeightFromDynamics(ctx, diktat.Weights, aL.fltrS, aL.tnt, data)
if err != nil {
return err
}
weights[diktat.ID] = weight
diktats = append(diktats, diktat)
}
// Sort by weight (higher values first).
slices.SortFunc(diktats, func(a, b *utils.APDiktat) int {
return cmp.Compare(weights[b.ID], weights[a.ID])
})
for _, diktat := range diktats {
params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]),
utils.InfieldSep)
if len(params) != 11 {
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
}
// parse dynamic parameters
for i := range params {
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
return err
}
}
// Prepare request arguments based on provided parameters.
args := &utils.ResourceProfileWithAPIOpts{
ResourceProfile: &utils.ResourceProfile{
Tenant: params[0],
ID: params[1],
AllocationMessage: params[6],
},
APIOpts: make(map[string]any),
}
// populate Resource's FilterIDs
if params[2] != utils.EmptyString {
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
}
// populate Resource's Weights
if params[3] != utils.EmptyString {
args.Weights = utils.DynamicWeights{&utils.DynamicWeight{}}
wghtSplit := strings.Split(params[3], utils.ANDSep)
if len(wghtSplit) > 2 {
return utils.ErrUnsupportedFormat
}
if wghtSplit[0] != utils.EmptyString {
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
}
if wghtSplit[1] != utils.EmptyString {
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
if err != nil {
return err
}
}
}
// populate Resource's UsageTTL
if params[4] != utils.EmptyString {
args.UsageTTL, err = utils.ParseDurationWithNanosecs(params[4])
if err != nil {
return err
}
}
// populate Resource's Limit
if params[5] != utils.EmptyString {
args.Limit, err = strconv.ParseFloat(params[5], 64)
if err != nil {
return err
}
}
// populate Resource's Blocker
if params[7] != utils.EmptyString {
args.Blocker, err = strconv.ParseBool(params[7])
if err != nil {
return err
}
}
// populate Resource's Stored
if params[8] != utils.EmptyString {
args.Stored, err = strconv.ParseBool(params[8])
if err != nil {
return err
}
}
// populate Resource's ThresholdIDs
if params[9] != utils.EmptyString {
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
}
// populate Resource's APIOpts
if params[10] != utils.EmptyString {
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
return err
}
}
// create the ResourceProfile based on the populated parameters
var rply string
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
utils.AdminSv1SetResourceProfile, args, &rply); err != nil {
return err
}
if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil {
return err
} else if blocker {
break
}
}
return
}