mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
benchmarks for ips functionality && rewriting actionTypes constants
This commit is contained in:
committed by
Dan Christian Bogos
parent
eba021be54
commit
71e4563ecd
@@ -199,14 +199,14 @@ cgrates.org,1002,,;30,;false,,VoiceBalance,,;10,,*voice,14,fltr3&fltr4;150;fltr5
|
||||
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,,,,,,,,,,,,
|
||||
cgrates.org,ONE_TIME_ACT,,;10,;true,*asap,*accounts,1001;1002,,,,,,,,,,,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP,,0s,*add_balance,,,,ADDBALVALUE,,,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP,,0s,*addBalance,,,,ADDBALVALUE,,,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,*asap,*accounts,1001;1002,,,,,,,,,,,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP,,,,,,,ADDBALVALUE,,*balancePath:*balance.TestBalance.Value;*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*add_balance,,,,ADDBALVALUE,,*balancePath:*balance.TestDataBalance.Value;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALVALUE1,,*balancePath:*balance.TestVoiceBalance.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALVALUE2,,*balancePath:*balance.TestVoiceBalance2.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALVALUE,,*balancePath:*balance.TestDataBalance.Value;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE1,,*balancePath:*balance.TestVoiceBalance.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE2,,*balancePath:*balance.TestVoiceBalance2.Value;*balanceValue:15m15s,,
|
||||
`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ cgrates.org,1001,,,,,VoiceBalance,,;10,*string:~*req.Destination:1002;true;;fals
|
||||
// writing in files the csv containing the profile for ActionProfile
|
||||
if err := os.WriteFile(path.Join("/tmp/ActionsIn", utils.ActionsCsv), []byte(`
|
||||
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*add_balance,,,,ADDBALVALUE,,*balancePath:*balance.TestBalance.Value;*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*add_balance,,,,ADDBALVALUE,,*balancePath:*balance.TestDataBalance.Value;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALVALUE1,,*balancePath:*balance.TestVoiceBalance.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALVALUE2,,*balancePath:*balance.TestVoiceBalance2.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*addBalance,,,,ADDBALVALUE,,*balancePath:*balance.TestBalance.Value;*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALVALUE,,*balancePath:*balance.TestDataBalance.Value;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE1,,*balancePath:*balance.TestVoiceBalance.Value;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALVALUE2,,*balancePath:*balance.TestVoiceBalance2.Value;*balanceValue:15m15s,,
|
||||
`), 0644); err != nil {
|
||||
t.Fatalf("Err %v when writing in file %s", err, utils.ActionsCsv)
|
||||
}
|
||||
|
||||
@@ -1077,7 +1077,7 @@ func testTPeSExportTariffPlanHalfTariffPlan(t *testing.T) {
|
||||
utils.ActionType, utils.ActionOpts, utils.ActionWeights, utils.ActionBlockers,
|
||||
utils.ActionDiktatsID, utils.ActionDiktatsFilterIDs, utils.ActionDiktatsOpts,
|
||||
utils.ActionDiktatsWeights, utils.ActionDiktatsBlockers},
|
||||
{"cgrates.org", "Execute_thd", "", ";20", "", "", "*thresholds", "THD_1", "actID", "", "0s", "*reset_threshold", "", "", "", "", "", "", "", ""},
|
||||
{"cgrates.org", "Execute_thd", "", ";20", "", "", "*thresholds", "THD_1", "actID", "", "0s", "*resetThreshold", "", "", "", "", "", "", "", ""},
|
||||
},
|
||||
utils.ThresholdsCsv: {
|
||||
{"#Tenant", "ID", "FilterIDs", "Weights", "MaxHits", "MinHits", "MinSleep", "Blocker", "ActionProfileIDs", "Async", "EeIDs"},
|
||||
@@ -1210,8 +1210,8 @@ func testTPeSExportTariffPlanAllTariffPlan(t *testing.T) {
|
||||
utils.ActionType, utils.ActionOpts, utils.ActionWeights, utils.ActionBlockers,
|
||||
utils.ActionDiktatsID, utils.ActionDiktatsFilterIDs, utils.ActionDiktatsOpts,
|
||||
utils.ActionDiktatsWeights, utils.ActionDiktatsBlockers},
|
||||
{"cgrates.org", "Execute_thd", "", ";20", "", "", "*thresholds", "THD_1", "actID", "", "0s", "*reset_threshold", "", "", "", "", "", "", "", ""},
|
||||
{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*set_balance", "", "", "", "SetBalMonetary10", "", "*balancePath:MONETARY;*balanceValue:10", "", ""},
|
||||
{"cgrates.org", "Execute_thd", "", ";20", "", "", "*thresholds", "THD_1", "actID", "", "0s", "*resetThreshold", "", "", "", "", "", "", "", ""},
|
||||
{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*setBalance", "", "", "", "SetBalMonetary10", "", "*balancePath:MONETARY;*balanceValue:10", "", ""},
|
||||
},
|
||||
utils.ThresholdsCsv: {
|
||||
{"#Tenant", "ID", "FilterIDs", "Weights", "MaxHits", "MinHits", "MinSleep", "Blocker", "ActionProfileIDs", "Async", "EeIDs"},
|
||||
@@ -1225,7 +1225,7 @@ func testTPeSExportTariffPlanAllTariffPlan(t *testing.T) {
|
||||
expected[utils.AccountsCsv] = csvRply[utils.AccountsCsv]
|
||||
|
||||
if !reflect.DeepEqual(expected, csvRply) {
|
||||
if !reflect.DeepEqual(csvRply[utils.ActionsCsv][2], []string{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*set_balance", "", "", "", "SetBalMonetary10", "", "*balanceValue:10;*balancePath:MONETARY", "", ""}) {
|
||||
if !reflect.DeepEqual(csvRply[utils.ActionsCsv][2], []string{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*setBalance", "", "", "", "SetBalMonetary10", "", "*balanceValue:10;*balancePath:MONETARY", "", ""}) {
|
||||
t.Errorf("Expected %+v \n received %+v", utils.ToJSON(expected), utils.ToJSON(csvRply))
|
||||
} else {
|
||||
newCsvReply := csvRply[utils.ActionsCsv][:1]
|
||||
@@ -1266,7 +1266,7 @@ func testTPeSExportTariffPlanAllTariffPlan(t *testing.T) {
|
||||
}
|
||||
// expected will remain the same
|
||||
if !reflect.DeepEqual(expected, csvRply) {
|
||||
if !reflect.DeepEqual(csvRply[utils.ActionsCsv][2], []string{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*set_balance", "", "", "", "SetBalMonetary10", "", "*balanceValue:10;*balancePath:MONETARY", "", ""}) {
|
||||
if !reflect.DeepEqual(csvRply[utils.ActionsCsv][2], []string{"cgrates.org", "SET_BAL", "*string:~*req.Account:1001", ";10", "", "*asap", "*accounts", "1001", "SET_BAL", "", "0s", "*setBalance", "", "", "", "SetBalMonetary10", "", "*balanceValue:10;*balancePath:MONETARY", "", ""}) {
|
||||
t.Errorf("Expected %+v \n received %+v", utils.ToJSON(expected), utils.ToJSON(csvRply))
|
||||
} else {
|
||||
csvRply[utils.ActionsCsv][2] = nil
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units:*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*set_balance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*rem_balance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units:*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*setBalance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*remBalance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units;*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*set_balance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*rem_balance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units;*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*setBalance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*remBalance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Tenant,ID,FilterIDs,Weights,Blockers,Schedule,TargetType,TargetIDs,ActionID,ActionFilterIDs,ActionTTL,ActionType,ActionOpts,ActionWeights,ActionBlockers,ActionDiktatsID,ActionDiktatsFilterIDs,ActionDiktatsOpts,ActionDiktatsWeights,ActionDiktatsBlockers
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units:*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*set_balance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*set_balance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*rem_balance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
cgrates.org,ONE_TIME_ACT,,;10,,*asap,*accounts,1001;1002,TOPUP,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestBalance.Units:*balanceValue:10,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_DATA,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestDataBalance.Type;*balanceValue:*data,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_DATA,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestDataBalance.Units;*balanceValue:1024,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_VOICE,,0s,*setBalance,,,,SETBALTYPE,,*balancePath:*balance.TestVoiceBalance.Type;*balanceValue:*voice,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_TEST_VOICE,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.TestVoiceBalance.Units;*balanceValue:15m15s,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,SET_BALANCE_TEST_FILTERS,,0s,*setBalance,,,,SETBALFILTER,,*balancePath:*balance.TestVoiceBalance.Filters;*balanceValue:*string:~*req.CustomField:500,,
|
||||
cgrates.org,ONE_TIME_ACT,,,,,,,TOPUP_REM_VOICE,,0s,*remBalance,,,,REMBAL,,*balancePath:TestVoiceBalance2,,
|
||||
|
||||
|
@@ -2,6 +2,6 @@
|
||||
|
||||
# TOPUP_RST_MONETARY_10 resets the <*default> <*monetary> balance to 10 units
|
||||
|
||||
#cgrates.org,TOPUP_RST_MONETARY_10,,;10,,*asap,*accounts,1001,TOPUP,,0s,*add_balance,,;10,;false,*asap,,,,,,10,10,,,,ADDBAL,,*balanceValue:10,,
|
||||
#cgrates.org,TOPUP_RST_MONETARY_10,,;10,,*asap,*accounts,1001,TOPUP,,0s,*addBalance,,;10,;false,*asap,,,,,,10,10,,,,ADDBAL,,*balanceValue:10,,
|
||||
|
||||
cgrates.org,TOPUP_RST_MONETARY_10,,;10,,*asap,*accounts,1001,TOPUP,,0s,*add_balance,,,,ADDBALUNITS,,*balancePath:*balance.Concrete1.Units;*balanceValue:10,,
|
||||
cgrates.org,TOPUP_RST_MONETARY_10,,;10,,*asap,*accounts,1001,TOPUP,,0s,*addBalance,,,,ADDBALUNITS,,*balancePath:*balance.Concrete1.Units;*balanceValue:10,,
|
||||
|
||||
|
324
general_tests/ips_load_test.go
Normal file
324
general_tests/ips_load_test.go
Normal file
@@ -0,0 +1,324 @@
|
||||
//go:build performance
|
||||
// +build performance
|
||||
|
||||
/*
|
||||
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 general_tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/birpc/context"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
var count = flag.Int("count", 1000, "Number of events to process in the load test")
|
||||
|
||||
func TestStressIPsAllocateIP(t *testing.T) {
|
||||
var dbConfig engine.DBCfg
|
||||
switch *utils.DBType {
|
||||
case utils.MetaInternal:
|
||||
dbConfig = engine.DBCfg{
|
||||
DataDB: &engine.DBParams{
|
||||
Type: utils.StringPointer(utils.MetaInternal),
|
||||
},
|
||||
StorDB: &engine.DBParams{
|
||||
Type: utils.StringPointer(utils.MetaInternal),
|
||||
},
|
||||
}
|
||||
case utils.MetaMySQL:
|
||||
case utils.MetaMongo, utils.MetaPostgres:
|
||||
t.SkipNow()
|
||||
default:
|
||||
t.Fatal("unsupported dbtype value")
|
||||
}
|
||||
|
||||
content := `{
|
||||
"general": {
|
||||
"log_level": 7
|
||||
},
|
||||
"stor_db": {
|
||||
"db_password": "CGRateS.org"
|
||||
},
|
||||
|
||||
"admins": {
|
||||
"enabled": true,
|
||||
},
|
||||
"ips": {
|
||||
"enabled": true,
|
||||
"indexed_selects":false,
|
||||
},
|
||||
}`
|
||||
|
||||
ng := engine.TestEngine{
|
||||
ConfigJSON: content,
|
||||
DBCfg: dbConfig,
|
||||
LogBuffer: bytes.NewBuffer(nil),
|
||||
Encoding: utils.MetaJSON,
|
||||
}
|
||||
client, _ := ng.Run(t)
|
||||
|
||||
t.Run("SetIPProfile", func(t *testing.T) {
|
||||
var reply string
|
||||
for i := 1; i <= 10; i++ {
|
||||
ipProfile := &utils.IPProfileWithAPIOpts{
|
||||
IPProfile: &utils.IPProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: fmt.Sprintf("IP_PROF_%d", i),
|
||||
FilterIDs: []string{fmt.Sprintf("*string:~*req.Account:%d", i)},
|
||||
TTL: 10 * time.Minute,
|
||||
Pools: []*utils.IPPool{
|
||||
{
|
||||
ID: "POOL_A",
|
||||
Range: fmt.Sprintf("10.%d.0.0/16", i),
|
||||
Message: "Allocated by test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := client.Call(context.Background(), utils.AdminSv1SetIPProfile, ipProfile, &reply); err != nil {
|
||||
t.Fatalf("Failed to set IP profile: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IPsAllocateEvent", func(t *testing.T) {
|
||||
ticker := time.NewTicker(time.Second / time.Duration(*count))
|
||||
defer ticker.Stop()
|
||||
jobs := make(chan int, *count)
|
||||
for i := 1; i <= *count; i++ {
|
||||
jobs <- i
|
||||
}
|
||||
close(jobs)
|
||||
|
||||
numWrk := 50
|
||||
var wg sync.WaitGroup
|
||||
latencies := make(chan time.Duration, *count)
|
||||
|
||||
totalCall := time.Now()
|
||||
for range numWrk {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range jobs {
|
||||
<-ticker.C
|
||||
|
||||
callStart := time.Now()
|
||||
|
||||
args := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: utils.GenUUID(),
|
||||
Event: map[string]any{
|
||||
utils.AccountField: fmt.Sprintf("%d", ((i-1)%10)+1),
|
||||
utils.AnswerTime: utils.TimePointer(time.Now()),
|
||||
utils.Usage: 10,
|
||||
utils.Tenant: "cgrates.org",
|
||||
},
|
||||
APIOpts: map[string]any{
|
||||
utils.OptsIPsAllocationID: utils.GenUUID(),
|
||||
},
|
||||
}
|
||||
var reply utils.AllocatedIP
|
||||
if err := client.Call(context.Background(), utils.IPsV1AllocateIP, args, &reply); err != nil {
|
||||
t.Errorf("Error processing event %d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
latencies <- time.Since(callStart)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
totalDuration := time.Since(totalCall)
|
||||
close(latencies)
|
||||
latencySlice := make([]time.Duration, 0, *count)
|
||||
for latency := range latencies {
|
||||
latencySlice = append(latencySlice, latency)
|
||||
}
|
||||
|
||||
successfulCalls := len(latencySlice)
|
||||
if successfulCalls == 0 {
|
||||
t.Fatal("No calls succeeded, cannot calculate performance.")
|
||||
}
|
||||
|
||||
actualThroughput := float64(successfulCalls) / totalDuration.Seconds()
|
||||
slices.Sort(latencySlice)
|
||||
|
||||
t.Logf("--- IP Allocation Performance Load Test Results ---")
|
||||
t.Logf("Target Rate: %d events/sec", *count)
|
||||
t.Logf("Successful Calls: %d", successfulCalls)
|
||||
t.Logf("Actual Throughput: %.2f events/sec", actualThroughput)
|
||||
t.Logf("Total Duration: %v", totalDuration)
|
||||
|
||||
// Calculate percentiles
|
||||
p50Index := int(float64(successfulCalls) * 0.50)
|
||||
p90Index := int(float64(successfulCalls) * 0.90)
|
||||
p99Index := int(float64(successfulCalls) * 0.99)
|
||||
|
||||
t.Logf("p50 Latency: %v", latencySlice[p50Index])
|
||||
t.Logf("p90 Latency: %v", latencySlice[p90Index])
|
||||
t.Logf("p99 Latency: %v", latencySlice[p99Index])
|
||||
})
|
||||
}
|
||||
|
||||
func TestStressIPsAuthorize(t *testing.T) {
|
||||
var dbConfig engine.DBCfg
|
||||
switch *utils.DBType {
|
||||
case utils.MetaInternal:
|
||||
dbConfig = engine.DBCfg{
|
||||
DataDB: &engine.DBParams{
|
||||
Type: utils.StringPointer(utils.MetaInternal),
|
||||
},
|
||||
StorDB: &engine.DBParams{
|
||||
Type: utils.StringPointer(utils.MetaInternal),
|
||||
},
|
||||
}
|
||||
case utils.MetaMySQL:
|
||||
case utils.MetaMongo, utils.MetaPostgres:
|
||||
t.SkipNow()
|
||||
default:
|
||||
t.Fatal("unsupported dbtype value")
|
||||
}
|
||||
|
||||
content := `{
|
||||
"general": {
|
||||
"log_level": 7
|
||||
},
|
||||
"stor_db": {
|
||||
"db_password": "CGRateS.org"
|
||||
},
|
||||
|
||||
"admins": {
|
||||
"enabled": true,
|
||||
},
|
||||
"ips": {
|
||||
"enabled": true,
|
||||
"indexed_selects":false,
|
||||
},
|
||||
}`
|
||||
|
||||
ng := engine.TestEngine{
|
||||
ConfigJSON: content,
|
||||
DBCfg: dbConfig,
|
||||
LogBuffer: bytes.NewBuffer(nil),
|
||||
Encoding: utils.MetaGOB,
|
||||
}
|
||||
client, _ := ng.Run(t)
|
||||
|
||||
t.Run("SetIPProfile", func(t *testing.T) {
|
||||
var reply string
|
||||
for i := 1; i <= 10; i++ {
|
||||
ipProfile := &utils.IPProfileWithAPIOpts{
|
||||
IPProfile: &utils.IPProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: fmt.Sprintf("IP_PROF_%d", i),
|
||||
FilterIDs: []string{fmt.Sprintf("*string:~*req.Account:%d", i)},
|
||||
TTL: 10 * time.Minute,
|
||||
Pools: []*utils.IPPool{
|
||||
{
|
||||
ID: "POOL_A",
|
||||
Range: fmt.Sprintf("10.0.0.%d/32", i),
|
||||
Message: "Allocated by test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := client.Call(context.Background(), utils.AdminSv1SetIPProfile, ipProfile, &reply); err != nil {
|
||||
t.Fatalf("Failed to set IP profile: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IPsAuthorizeEvent", func(t *testing.T) {
|
||||
ticker := time.NewTicker(time.Second / time.Duration(*count))
|
||||
defer ticker.Stop()
|
||||
jobs := make(chan int, *count)
|
||||
for i := 1; i <= *count; i++ {
|
||||
jobs <- i
|
||||
}
|
||||
close(jobs)
|
||||
numWrk := 50
|
||||
var wg sync.WaitGroup
|
||||
latencies := make(chan time.Duration, *count)
|
||||
totalCall := time.Now()
|
||||
for range numWrk {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range jobs {
|
||||
<-ticker.C
|
||||
callStart := time.Now()
|
||||
args := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: utils.GenUUID(),
|
||||
Event: map[string]any{
|
||||
utils.AccountField: fmt.Sprintf("%d", ((i-1)%10)+1),
|
||||
},
|
||||
APIOpts: map[string]any{
|
||||
utils.OptsIPsAllocationID: utils.GenUUID(),
|
||||
},
|
||||
}
|
||||
var reply utils.AllocatedIP
|
||||
if err := client.Call(context.Background(), utils.IPsV1AuthorizeIP, args, &reply); err != nil {
|
||||
t.Errorf("Error processing event %d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
latencies <- time.Since(callStart)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
totalDuration := time.Since(totalCall)
|
||||
close(latencies)
|
||||
latencySlice := make([]time.Duration, 0, *count)
|
||||
for latency := range latencies {
|
||||
latencySlice = append(latencySlice, latency)
|
||||
}
|
||||
|
||||
successfulCalls := len(latencySlice)
|
||||
if successfulCalls == 0 {
|
||||
t.Fatal("No calls succeeded, cannot calculate performance.")
|
||||
}
|
||||
|
||||
actualThroughput := float64(successfulCalls) / totalDuration.Seconds()
|
||||
slices.Sort(latencySlice)
|
||||
|
||||
t.Logf("--- IP Allocation Performance Load Test Results ---")
|
||||
t.Logf("Target Rate: %d events/sec", *count)
|
||||
t.Logf("Successful Calls: %d", successfulCalls)
|
||||
t.Logf("Actual Throughput: %.2f events/sec", actualThroughput)
|
||||
t.Logf("Total Duration: %v", totalDuration)
|
||||
|
||||
// Calculate percentiles
|
||||
p50Index := int(float64(successfulCalls) * 0.50)
|
||||
p90Index := int(float64(successfulCalls) * 0.90)
|
||||
p99Index := int(float64(successfulCalls) * 0.99)
|
||||
|
||||
t.Logf("p50 Latency: %v", latencySlice[p50Index])
|
||||
t.Logf("p90 Latency: %v", latencySlice[p90Index])
|
||||
t.Logf("p99 Latency: %v", latencySlice[p99Index])
|
||||
})
|
||||
|
||||
}
|
||||
@@ -24,8 +24,10 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/birpc/context"
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
@@ -319,3 +321,152 @@ cgrates.org,IPs2,*string:~*req.Account:1002,;20,2s,false,POOL1,*string:~*req.Des
|
||||
}
|
||||
})
|
||||
}
|
||||
func BenchmarkIPsAuthorize(b *testing.B) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
dataDB, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
|
||||
dm := engine.NewDataManager(dataDB, cfg, nil)
|
||||
fltrs := engine.NewFilterS(cfg, nil, dm)
|
||||
cm := engine.NewConnManager(cfg)
|
||||
ipService := NewIPService(dm, cfg, fltrs, cm)
|
||||
|
||||
ctx := context.Background()
|
||||
profile := &utils.IPProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "IP1",
|
||||
FilterIDs: []string{"*string:~*req.Account:1001"},
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
TTL: time.Second,
|
||||
Stored: false,
|
||||
Pools: []*utils.IPPool{{
|
||||
ID: "POOL1",
|
||||
FilterIDs: []string{},
|
||||
Type: "*ipv4",
|
||||
Range: "192.168.122.1/32",
|
||||
Strategy: "*ascending",
|
||||
Message: "bench pool",
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
||||
}},
|
||||
}
|
||||
if err := dm.SetIPProfile(ctx, profile, true); err != nil {
|
||||
b.Fatalf("Failed to set IPProfile: %v", err)
|
||||
}
|
||||
|
||||
for b.Loop() {
|
||||
b.StopTimer()
|
||||
args := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: utils.UUIDSha1Prefix(),
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
APIOpts: map[string]any{
|
||||
utils.OptsIPsAllocationID: "alloc1",
|
||||
},
|
||||
}
|
||||
var allocIP utils.AllocatedIP
|
||||
b.StartTimer()
|
||||
if err := ipService.V1AuthorizeIP(ctx, args, &allocIP); err != nil {
|
||||
b.Error("AuthorizeIP failed:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIPsAllocate(b *testing.B) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
dataDB, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
|
||||
dm := engine.NewDataManager(dataDB, cfg, nil)
|
||||
fltrs := engine.NewFilterS(cfg, nil, dm)
|
||||
cm := engine.NewConnManager(cfg)
|
||||
ipService := NewIPService(dm, cfg, fltrs, cm)
|
||||
|
||||
ctx := context.Background()
|
||||
profile := &utils.IPProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "IP2",
|
||||
FilterIDs: []string{"*string:~*req.Account:1001"},
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
TTL: time.Second,
|
||||
Stored: false,
|
||||
Pools: []*utils.IPPool{{
|
||||
ID: "POOL1",
|
||||
FilterIDs: []string{},
|
||||
Type: "*ipv4",
|
||||
Range: "192.168.122.1/32",
|
||||
Strategy: "*ascending",
|
||||
Message: "bench pool",
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
||||
}},
|
||||
}
|
||||
if err := dm.SetIPProfile(ctx, profile, true); err != nil {
|
||||
b.Fatalf("Failed to set IPProfile: %v", err)
|
||||
}
|
||||
for b.Loop() {
|
||||
b.StopTimer()
|
||||
args := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: utils.UUIDSha1Prefix(),
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
APIOpts: map[string]any{
|
||||
utils.OptsIPsAllocationID: "alloc1",
|
||||
},
|
||||
}
|
||||
var allocIP utils.AllocatedIP
|
||||
b.StartTimer()
|
||||
if err := ipService.V1AllocateIP(ctx, args, &allocIP); err != nil {
|
||||
b.Error("AuthorizeIP failed:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
func BenchmarkIPsRelease(b *testing.B) {
|
||||
cfg := config.NewDefaultCGRConfig()
|
||||
data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items)
|
||||
dm := engine.NewDataManager(data, cfg, nil)
|
||||
fltrs := engine.NewFilterS(cfg, nil, dm)
|
||||
cm := engine.NewConnManager(cfg)
|
||||
ipService := NewIPService(dm, cfg, fltrs, cm)
|
||||
ctx := context.Background()
|
||||
profile := &utils.IPProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "IP3",
|
||||
FilterIDs: []string{"*string:~*req.Account:1001"},
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
TTL: time.Second,
|
||||
Stored: false,
|
||||
Pools: []*utils.IPPool{{
|
||||
ID: "POOL1",
|
||||
FilterIDs: []string{},
|
||||
Type: "*ipv4",
|
||||
Range: "192.168.122.1/32",
|
||||
Strategy: "*ascending",
|
||||
Message: "bench pool",
|
||||
Weights: utils.DynamicWeights{{Weight: 10}},
|
||||
Blockers: utils.DynamicBlockers{{Blocker: false}},
|
||||
}},
|
||||
}
|
||||
if err := dm.SetIPProfile(ctx, profile, true); err != nil {
|
||||
b.Fatalf("Failed to set IPProfile: %v", err)
|
||||
}
|
||||
|
||||
for b.Loop() {
|
||||
b.StopTimer()
|
||||
args := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: utils.UUIDSha1Prefix(),
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
APIOpts: map[string]any{
|
||||
utils.OptsIPsAllocationID: "alloc1",
|
||||
},
|
||||
}
|
||||
var reply string
|
||||
b.StartTimer()
|
||||
if err := ipService.V1ReleaseIP(ctx, args, &reply); err != nil {
|
||||
b.Error("AuthorizeIP failed:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1133,8 +1133,8 @@ const (
|
||||
MetaRemoveSessionCosts = "*remove_session_costs"
|
||||
MetaPostEvent = "*post_event"
|
||||
MetaCDRAccount = "*reset_account_cdr"
|
||||
MetaResetThreshold = "*reset_threshold"
|
||||
MetaResetStatQueue = "*reset_stat_queue"
|
||||
MetaResetThreshold = "*resetThreshold"
|
||||
MetaResetStatQueue = "*resetStatQueue"
|
||||
MetaRemoteSetAccount = "*remote_set_account"
|
||||
ActionID = "ActionID"
|
||||
ActionType = "ActionType"
|
||||
@@ -1143,14 +1143,14 @@ const (
|
||||
BalanceUnitFactors = "BalanceUnitFactors"
|
||||
ExtraParameters = "ExtraParameters"
|
||||
|
||||
MetaAddBalance = "*add_balance"
|
||||
MetaSetBalance = "*set_balance"
|
||||
MetaRemBalance = "*rem_balance"
|
||||
MetaAddBalance = "*addBalance"
|
||||
MetaSetBalance = "*setBalance"
|
||||
MetaRemBalance = "*remBalance"
|
||||
DynaprepaidActionplansCfg = "dynaprepaid_actionprofile"
|
||||
MetaDynamicThreshold = "*dynamic_threshold"
|
||||
MetaDynamicStats = "*dynamic_stats"
|
||||
MetaDynamicAttribute = "*dynamic_attribute"
|
||||
MetaDynamicResource = "*dynamic_resource"
|
||||
MetaDynamicThreshold = "*dynamicThreshold"
|
||||
MetaDynamicStats = "*dynamicStats"
|
||||
MetaDynamicAttribute = "*dynamicAttribute"
|
||||
MetaDynamicResource = "*dynamicResource"
|
||||
MetaDynamicTrend = "*dynamicTrend"
|
||||
MetaDynamicRanking = "*dynamicRanking"
|
||||
MetaDynamicFilter = "*dynamicFilter"
|
||||
|
||||
Reference in New Issue
Block a user