From e13a64590bb3de1f5e40f16b48d3d4e562b2597c Mon Sep 17 00:00:00 2001 From: TeoV Date: Wed, 13 Sep 2017 17:28:47 +0300 Subject: [PATCH 01/20] Add test for TPLcrRules --- apier/v1/tplcrrules.go | 22 ++- apier/v1/tplcrrules_it_test.go | 259 +++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 apier/v1/tplcrrules_it_test.go diff --git a/apier/v1/tplcrrules.go b/apier/v1/tplcrrules.go index 37611ac38..b0e6a7514 100644 --- a/apier/v1/tplcrrules.go +++ b/apier/v1/tplcrrules.go @@ -39,6 +39,15 @@ type AttrGetTPLcrRules struct { LcrRuleId string // Lcr id } +type AttrRemTPLcrRules struct { + TPid string // Tariff plan id + Direction string + Tenant string + Category string + Account string + Subject string +} + // Queries specific LcrRules profile on tariff plan func (self *ApierV1) GetTPLcrRule(attr AttrGetTPLcrRules, reply *utils.TPLcrRules) error { if missing := utils.MissingStructFields(&attr, []string{"TPid", "LcrRuleId"}); len(missing) != 0 { //Params missing @@ -47,9 +56,10 @@ func (self *ApierV1) GetTPLcrRule(attr AttrGetTPLcrRules, reply *utils.TPLcrRule filter := &utils.TPLcrRules{TPid: attr.TPid} filter.SetId(attr.LcrRuleId) if lcrs, err := self.StorDb.GetTPLCRs(filter); err != nil { - return utils.NewErrServerError(err) - } else if len(lcrs) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { *reply = *lcrs[0] } @@ -77,11 +87,11 @@ func (self *ApierV1) GetTPLcrRuleIds(attrs AttrGetTPLcrIds, reply *[]string) err } // Removes specific LcrRules on Tariff plan -func (self *ApierV1) RemTPLcrRule(attrs AttrGetTPLcrRules, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LcrRulesId"}); len(missing) != 0 { //Params missing +func (self *ApierV1) RemTPLcrRule(attrs AttrRemTPLcrRules, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if err := self.StorDb.RemTpData(utils.TBLTPLcrs, attrs.TPid, map[string]string{"tag": attrs.LcrRuleId}); err != nil { + if err := self.StorDb.RemTpData(utils.TBLTPLcrs, attrs.TPid, map[string]string{"direction": attrs.Direction, "tenant": attrs.Tenant, "category": attrs.Category, "account": attrs.Account, "subject": attrs.Subject}); err != nil { return utils.NewErrServerError(err) } else { *reply = utils.OK diff --git a/apier/v1/tplcrrules_it_test.go b/apier/v1/tplcrrules_it_test.go new file mode 100644 index 000000000..135902c07 --- /dev/null +++ b/apier/v1/tplcrrules_it_test.go @@ -0,0 +1,259 @@ +// +build offline_tp + +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" +) + +var ( + tpLcrRuleCfgPath string + tpLcrRuleCfg *config.CGRConfig + tpLcrRuleRPC *rpc.Client + tpLcrRuleDataDir = "/usr/share/cgrates" + tpLcrRules *utils.TPLcrRules + tpLcrRuleDelay int + tpLcrRuleConfigDIR string //run tests for specific configuration + tpLcrRuleID = "*out:cgrates.org:call:1001:*any" +) + +var sTestsTPLcrRules = []func(t *testing.T){ + testTPLcrRulesInitCfg, + testTPLcrRulesResetStorDb, + testTPLcrRulesStartEngine, + testTPLcrRulesRpcConn, + testTPLcrRulesGetTPLcrRulesBeforeSet, + testTPLcrRulesSetTPLcrRules, + testTPLcrRulesGetTPLcrRulesAfterSet, + testTPLcrRulesGetTPDestinationIds, + testTPLcrRulesUpdateTPLcrRules, + testTPLcrRulesGetTPLcrRulesAfterUpdate, + testTPLcrRulesRemTPLcrRules, + testTPLcrRulesGetTPLcrRulesAfterRemove, + testTPLcrRulesKillEngine, +} + +//Test start here +func TestTPLcrRulesITMySql(t *testing.T) { + tpLcrRuleConfigDIR = "tutmysql" + for _, stest := range sTestsTPLcrRules { + t.Run(tpLcrRuleConfigDIR, stest) + } +} + +func TestTPLcrRulesITMongo(t *testing.T) { + tpLcrRuleConfigDIR = "tutmongo" + for _, stest := range sTestsTPLcrRules { + t.Run(tpLcrRuleConfigDIR, stest) + } +} + +func TestTPLcrRulesITPG(t *testing.T) { + tpLcrRuleConfigDIR = "tutpostgres" + for _, stest := range sTestsTPLcrRules { + t.Run(tpLcrRuleConfigDIR, stest) + } +} + +func testTPLcrRulesInitCfg(t *testing.T) { + var err error + tpLcrRuleCfgPath = path.Join(tpLcrRuleDataDir, "conf", "samples", tpLcrRuleConfigDIR) + tpLcrRuleCfg, err = config.NewCGRConfigFromFolder(tpLcrRuleCfgPath) + if err != nil { + t.Error(err) + } + tpLcrRuleCfg.DataFolderPath = tpLcrRuleDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(tpLcrRuleCfg) + switch tpLcrRuleConfigDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + tpLcrRuleDelay = 2000 + default: + tpLcrRuleDelay = 1000 + } +} + +// Wipe out the cdr database +func testTPLcrRulesResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpLcrRuleCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPLcrRulesStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpLcrRuleCfgPath, tpLcrRuleDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPLcrRulesRpcConn(t *testing.T) { + var err error + tpLcrRuleRPC, err = jsonrpc.Dial("tcp", tpLcrRuleCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPLcrRulesGetTPLcrRulesBeforeSet(t *testing.T) { + var reply *utils.TPRatingPlan + if err := tpLcrRuleRPC.Call("ApierV1.GetTPLcrRule", &AttrGetTPLcrRules{TPid: "TPLRC1", LcrRuleId: tpLcrRuleID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPLcrRulesSetTPLcrRules(t *testing.T) { + tpLcrRules = &utils.TPLcrRules{ + TPid: "TPLRC1", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "*any", + Rules: []*utils.TPLcrRule{ + &utils.TPLcrRule{ + DestinationId: "DST_1002", + RpCategory: "lcr_profile1", + Strategy: "*static", + StrategyParams: "suppl2;suppl1", + ActivationTime: "05:00:00", + Weight: 10, + }, + &utils.TPLcrRule{ + DestinationId: "*any", + RpCategory: "lcr_profile1", + Strategy: "*highest_cost", + StrategyParams: "", + ActivationTime: "05:00:00", + Weight: 10, + }, + }, + } + var result string + if err := tpLcrRuleRPC.Call("ApierV1.SetTPLcrRule", tpLcrRules, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPLcrRulesGetTPLcrRulesAfterSet(t *testing.T) { + var reply *utils.TPLcrRules + if err := tpLcrRuleRPC.Call("ApierV1.GetTPLcrRule", &AttrGetTPLcrRules{TPid: "TPLRC1", LcrRuleId: tpLcrRuleID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpLcrRules.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpLcrRules.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpLcrRules.GetLcrRuleId(), reply.GetLcrRuleId()) { + t.Errorf("Expecting : %+v, received: %+v", tpLcrRules.GetLcrRuleId(), reply.GetLcrRuleId()) + } else if !reflect.DeepEqual(len(tpLcrRules.Rules), len(reply.Rules)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpLcrRules.Rules), len(reply.Rules)) + } + +} + +func testTPLcrRulesGetTPDestinationIds(t *testing.T) { + var result []string + expectedTPID := []string{"*out:cgrates.org:call:1001:*any"} + if err := tpLcrRuleRPC.Call("ApierV1.GetTPLcrRuleIds", &AttrGetTPLcrIds{TPid: "TPLRC1"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } + +} + +func testTPLcrRulesUpdateTPLcrRules(t *testing.T) { + tpLcrRules.Rules = []*utils.TPLcrRule{ + &utils.TPLcrRule{ + DestinationId: "DST_1002", + RpCategory: "lcr_profile1", + Strategy: "*static", + StrategyParams: "suppl2;suppl1", + ActivationTime: "03:00:00", + Weight: 10, + }, + &utils.TPLcrRule{ + DestinationId: "*any", + RpCategory: "lcr_profile1", + Strategy: "*highest_cost", + StrategyParams: "", + ActivationTime: "05:00:00", + Weight: 10, + }, + &utils.TPLcrRule{ + DestinationId: "*any", + RpCategory: "lcr_profile2", + Strategy: "*load_distribution", + StrategyParams: "supplier1:5", + ActivationTime: "01:00:00", + Weight: 10, + }, + } + var result string + if err := tpLcrRuleRPC.Call("ApierV1.SetTPLcrRule", tpLcrRules, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPLcrRulesGetTPLcrRulesAfterUpdate(t *testing.T) { + var reply *utils.TPLcrRules + if err := tpLcrRuleRPC.Call("ApierV1.GetTPLcrRule", &AttrGetTPLcrRules{TPid: "TPLRC1", LcrRuleId: tpLcrRuleID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpLcrRules.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpLcrRules.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpLcrRules.GetLcrRuleId(), reply.GetLcrRuleId()) { + t.Errorf("Expecting : %+v, received: %+v", tpLcrRules.GetLcrRuleId(), reply.GetLcrRuleId()) + } else if !reflect.DeepEqual(len(tpLcrRules.Rules), len(reply.Rules)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpLcrRules.Rules), len(reply.Rules)) + } +} + +func testTPLcrRulesRemTPLcrRules(t *testing.T) { + var resp string + if err := tpLcrRuleRPC.Call("ApierV1.RemTPLcrRule", &AttrRemTPLcrRules{TPid: tpLcrRules.TPid, Direction: tpLcrRules.Direction, Tenant: tpLcrRules.Tenant, Category: tpLcrRules.Category, Account: tpLcrRules.Account, Subject: tpLcrRules.Subject}, &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } +} + +func testTPLcrRulesGetTPLcrRulesAfterRemove(t *testing.T) { + var reply *utils.TPRatingPlan + if err := tpLcrRuleRPC.Call("ApierV1.GetTPLcrRule", &AttrGetTPLcrRules{TPid: "TPLRC1", LcrRuleId: tpLcrRuleID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + +} + +func testTPLcrRulesKillEngine(t *testing.T) { + if err := engine.KillEngine(tpLcrRuleDelay); err != nil { + t.Error(err) + } +} From 959ffdcc75944524344a8d779f6f70a91b0966f6 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 13 Sep 2017 18:54:05 +0200 Subject: [PATCH 02/20] TPReader creating StatQueue based on StatQueueProfile, adding necessary methods in dataDB, tests --- apier/v1/apier_it_test.go | 2 +- apier/v1/smgenericv1_it_test.go | 2 +- engine/loader_csv_test.go | 8 ++--- engine/loader_it_test.go | 2 +- engine/storage_map.go | 4 ++- engine/storage_mongo_datadb.go | 45 ++++++++++++++------------ engine/storage_redis.go | 4 ++- engine/tp_reader.go | 42 +++++++++++++++++------- general_tests/tut_smgeneric_it_test.go | 2 +- general_tests/tutorial_it_test.go | 2 +- utils/consts.go | 1 - 11 files changed, 71 insertions(+), 43 deletions(-) diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go index c6a1312e8..a492833af 100644 --- a/apier/v1/apier_it_test.go +++ b/apier/v1/apier_it_test.go @@ -1273,7 +1273,7 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { rcvStats.Aliases != 1 || rcvStats.ReverseAliases != 2 || rcvStats.ResourceProfiles != 3 || - rcvStats.Resources != 0 { + rcvStats.Resources != 3 { t.Errorf("Expecting: %+v, received: %+v", expStats, rcvStats) } } diff --git a/apier/v1/smgenericv1_it_test.go b/apier/v1/smgenericv1_it_test.go index ba5a9d5af..3af5ae6e1 100644 --- a/apier/v1/smgenericv1_it_test.go +++ b/apier/v1/smgenericv1_it_test.go @@ -102,7 +102,7 @@ func TestSMGV1CacheStats(t *testing.T) { var rcvStats *utils.CacheStats expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, Actions: 8, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, - LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 0} + LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3} var args utils.AttrCacheStats if err := smgV1Rpc.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 6a79b3b9e..a6a25116f 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1453,10 +1453,10 @@ func TestLoadStats(t *testing.T) { }, } - if len(csvr.stats) != len(eStats) { - t.Error("Failed to load stats: ", len(csvr.stats)) - } else if !reflect.DeepEqual(eStats["Stats1"], csvr.stats["Stats1"]) { - t.Errorf("Expecting: %+v, received: %+v", eStats["Stats1"], csvr.stats["Stats1"]) + if len(csvr.sqProfiles) != len(eStats) { + t.Error("Failed to load stats: ", len(csvr.sqProfiles)) + } else if !reflect.DeepEqual(eStats["Stats1"], csvr.sqProfiles["Stats1"]) { + t.Errorf("Expecting: %+v, received: %+v", eStats["Stats1"], csvr.sqProfiles["Stats1"]) } } diff --git a/engine/loader_it_test.go b/engine/loader_it_test.go index 168541ef5..476480f2c 100755 --- a/engine/loader_it_test.go +++ b/engine/loader_it_test.go @@ -323,7 +323,7 @@ func TestLoaderITWriteToDatabase(t *testing.T) { } } - for k, st := range loader.stats { + for k, st := range loader.sqProfiles { rcv, err := loader.dataStorage.GetStatQueueProfile(k) if err != nil { t.Error("Failed GetStatsQueue: ", err.Error()) diff --git a/engine/storage_map.go b/engine/storage_map.go index 22a790f87..bc7d7780b 100755 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -306,7 +306,9 @@ func (ms *MapStorage) HasData(categ, subject string) (bool, error) { ms.mu.RLock() defer ms.mu.RUnlock() switch categ { - case utils.DESTINATION_PREFIX, utils.RATING_PLAN_PREFIX, utils.RATING_PROFILE_PREFIX, utils.ACTION_PREFIX, utils.ACTION_PLAN_PREFIX, utils.ACCOUNT_PREFIX, utils.DERIVEDCHARGERS_PREFIX, utils.ResourcesPrefix: + case utils.DESTINATION_PREFIX, utils.RATING_PLAN_PREFIX, utils.RATING_PROFILE_PREFIX, + utils.ACTION_PREFIX, utils.ACTION_PLAN_PREFIX, utils.ACCOUNT_PREFIX, utils.DERIVEDCHARGERS_PREFIX, + utils.ResourcesPrefix, utils.StatQueuePrefix: _, exists := ms.dict[categ+subject] return exists, nil } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index c375fc31f..66f6fb984 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -21,7 +21,6 @@ package engine import ( "bytes" "compress/zlib" - "errors" "fmt" "io/ioutil" "strings" @@ -327,7 +326,7 @@ func (ms *MongoStorage) getColNameForPrefix(prefix string) (name string, ok bool utils.LOADINST_KEY: colLht, utils.VERSION_PREFIX: colVer, utils.ResourceProfilesPrefix: colRsP, - utils.StatsPrefix: colStq, + utils.StatQueuePrefix: colStq, utils.TimingsPrefix: colTmg, utils.ResourcesPrefix: colRes, } @@ -632,10 +631,10 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er for iter.Next(&idResult) { result = append(result, utils.ResourcesPrefix+idResult.Id) } - case utils.StatsPrefix: + case utils.StatQueuePrefix: iter := db.C(colStq).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"id": 1}).Iter() for iter.Next(&idResult) { - result = append(result, utils.StatsPrefix+idResult.Id) + result = append(result, utils.StatQueuePrefix+idResult.Id) } case utils.StatQueueProfilePrefix: iter := db.C(colSqp).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"id": 1}).Iter() @@ -658,34 +657,40 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er return } -func (ms *MongoStorage) HasData(category, subject string) (bool, error) { +func (ms *MongoStorage) HasData(category, subject string) (has bool, err error) { session := ms.session.Copy() defer session.Close() db := session.DB(ms.db) + var count int switch category { case utils.DESTINATION_PREFIX: - count, err := db.C(colDst).Find(bson.M{"key": subject}).Count() - return count > 0, err + count, err = db.C(colDst).Find(bson.M{"key": subject}).Count() + has = count > 0 case utils.RATING_PLAN_PREFIX: - count, err := db.C(colRpl).Find(bson.M{"key": subject}).Count() - return count > 0, err + count, err = db.C(colRpl).Find(bson.M{"key": subject}).Count() + has = count > 0 case utils.RATING_PROFILE_PREFIX: - count, err := db.C(colRpf).Find(bson.M{"id": subject}).Count() - return count > 0, err + count, err = db.C(colRpf).Find(bson.M{"id": subject}).Count() + has = count > 0 case utils.ACTION_PREFIX: - count, err := db.C(colAct).Find(bson.M{"key": subject}).Count() - return count > 0, err + count, err = db.C(colAct).Find(bson.M{"key": subject}).Count() + has = count > 0 case utils.ACTION_PLAN_PREFIX: - count, err := db.C(colApl).Find(bson.M{"key": subject}).Count() - return count > 0, err + count, err = db.C(colApl).Find(bson.M{"key": subject}).Count() + has = count > 0 case utils.ACCOUNT_PREFIX: - count, err := db.C(colAcc).Find(bson.M{"id": subject}).Count() - return count > 0, err + count, err = db.C(colAcc).Find(bson.M{"id": subject}).Count() + has = count > 0 case utils.ResourcesPrefix: - count, err := db.C(colRes).Find(bson.M{"id": subject}).Count() - return count > 0, err + count, err = db.C(colRes).Find(bson.M{"id": subject}).Count() + has = count > 0 + case utils.StatQueuePrefix: + count, err = db.C(colRes).Find(bson.M{"id": subject}).Count() + has = count > 0 + default: + err = fmt.Errorf("unsupported category in HasData: %s", category) } - return false, errors.New("unsupported category in HasData") + return } func (ms *MongoStorage) GetRatingPlan(key string, skipCache bool, transactionID string) (rp *RatingPlan, err error) { diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 2651604f9..db1ae044d 100755 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -319,7 +319,9 @@ func (rs *RedisStorage) GetKeysForPrefix(prefix string) ([]string, error) { // Used to check if specific subject is stored using prefix key attached to entity func (rs *RedisStorage) HasData(category, subject string) (bool, error) { switch category { - case utils.DESTINATION_PREFIX, utils.RATING_PLAN_PREFIX, utils.RATING_PROFILE_PREFIX, utils.ACTION_PREFIX, utils.ACTION_PLAN_PREFIX, utils.ACCOUNT_PREFIX, utils.DERIVEDCHARGERS_PREFIX, utils.ResourcesPrefix: + case utils.DESTINATION_PREFIX, utils.RATING_PLAN_PREFIX, utils.RATING_PROFILE_PREFIX, + utils.ACTION_PREFIX, utils.ACTION_PLAN_PREFIX, utils.ACCOUNT_PREFIX, utils.DERIVEDCHARGERS_PREFIX, + utils.ResourcesPrefix, utils.StatQueuePrefix: i, err := rs.Cmd("EXISTS", category+subject).Int() return i == 1, err } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 565d6cebf..d1959c0de 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -54,9 +54,10 @@ type TpReader struct { users map[string]*UserProfile aliases map[string]*Alias resProfiles map[string]*utils.TPResource - stats map[string]*utils.TPStats + sqProfiles map[string]*utils.TPStats thresholds map[string]*utils.TPThreshold - resources []string // IDs of resources which need creation based on resourceConfigs + resources []string // IDs of resources which need creation based on resourceProfiles + statQueues []string // IDs of statQueues which need creation based on statQueueProfiles revDests, revAliases, @@ -129,7 +130,7 @@ func (tpr *TpReader) Init() { tpr.aliases = make(map[string]*Alias) tpr.derivedChargers = make(map[string]*utils.DerivedChargers) tpr.resProfiles = make(map[string]*utils.TPResource) - tpr.stats = make(map[string]*utils.TPStats) + tpr.sqProfiles = make(map[string]*utils.TPStats) tpr.thresholds = make(map[string]*utils.TPThreshold) tpr.revDests = make(map[string][]string) tpr.revAliases = make(map[string][]string) @@ -1626,7 +1627,14 @@ func (tpr *TpReader) LoadStatsFiltered(tag string) error { for _, st := range tps { mapSTs[st.ID] = st } - tpr.stats = mapSTs + tpr.sqProfiles = mapSTs + for sqID := range mapSTs { + if has, err := tpr.dataStorage.HasData(utils.StatQueuePrefix, sqID); err != nil { + return err + } else if !has { + tpr.statQueues = append(tpr.statQueues, sqID) + } + } return nil } @@ -1970,7 +1978,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if verbose { log.Print("StatQueueProfiles:") } - for _, tpST := range tpr.stats { + for _, tpST := range tpr.sqProfiles { st, err := APItoStats(tpST, tpr.timezone) if err != nil { return err @@ -1982,6 +1990,18 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err log.Print("\t", st.ID) } } + if verbose { + log.Print("StatQueues:") + } + for _, sqID := range tpr.statQueues { + if err = tpr.dataStorage.SetStoredStatQueue(&StoredStatQueue{Tenant: "", ID: sqID, + SQMetrics: make(map[string][]byte)}); err != nil { + return + } + if verbose { + log.Print("\t", sqID) + } + } if verbose { log.Print("Thresholds:") } @@ -2055,7 +2075,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err return err } } - if len(tpr.stats) > 0 { + if len(tpr.sqProfiles) > 0 { if verbose { log.Print("Indexing stats") } @@ -2063,7 +2083,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if err != nil { return err } - for _, tpST := range tpr.stats { + for _, tpST := range tpr.sqProfiles { if st, err := APItoStats(tpST, tpr.timezone); err != nil { return err } else { @@ -2163,7 +2183,7 @@ func (tpr *TpReader) ShowStatistics() { // resource limits log.Print("ResourceProfiles: ", len(tpr.resProfiles)) // stats - log.Print("Stats: ", len(tpr.stats)) + log.Print("Stats: ", len(tpr.sqProfiles)) } // Returns the identities loaded for a specific category, useful for cache reloads @@ -2297,10 +2317,10 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { i++ } return keys, nil - case utils.StatsPrefix: - keys := make([]string, len(tpr.stats)) + case utils.StatQueueProfilePrefix: + keys := make([]string, len(tpr.sqProfiles)) i := 0 - for k := range tpr.stats { + for k := range tpr.sqProfiles { keys[i] = k i++ } diff --git a/general_tests/tut_smgeneric_it_test.go b/general_tests/tut_smgeneric_it_test.go index 133c92035..d0af8c9e7 100644 --- a/general_tests/tut_smgeneric_it_test.go +++ b/general_tests/tut_smgeneric_it_test.go @@ -100,7 +100,7 @@ func TestTutSMGCacheStats(t *testing.T) { var rcvStats *utils.CacheStats expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, Actions: 8, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, - CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 0} + CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3} var args utils.AttrCacheStats if err := tutSMGRpc.Call("ApierV2.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV2.GetCacheStats: ", err.Error()) diff --git a/general_tests/tutorial_it_test.go b/general_tests/tutorial_it_test.go index 1c21a5503..ebc98c4e4 100644 --- a/general_tests/tutorial_it_test.go +++ b/general_tests/tutorial_it_test.go @@ -104,7 +104,7 @@ func TestTutITCacheStats(t *testing.T) { var rcvStats *utils.CacheStats expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, Actions: 8, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, - CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 0} + CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3} var args utils.AttrCacheStats if err := tutLocalRpc.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) diff --git a/utils/consts.go b/utils/consts.go index 69ca64ba9..70bd03e16 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -243,7 +243,6 @@ const ( ResourcesPrefix = "res_" ResourceProfilesIndex = "rsi_" ResourceProfilesPrefix = "rsp_" - StatsPrefix = "sts_" StatsIndex = "sti_" ThresholdsPrefix = "ths_" ThresholdsIndex = "thi_" From 9dd2a66ff7ff6edcb20dccd6f0fde06e27f191c6 Mon Sep 17 00:00:00 2001 From: TeoV Date: Thu, 14 Sep 2017 15:23:42 +0300 Subject: [PATCH 03/20] Add tests for TPAccountActions and TPAccountPlans --- apier/v1/tpaccountactions.go | 14 +- apier/v1/tpaccountactions_it_test.go | 212 +++++++++++++++++++++++ apier/v1/tpactionplans.go | 7 +- apier/v1/tpactionplans_it_test.go | 241 +++++++++++++++++++++++++++ 4 files changed, 465 insertions(+), 9 deletions(-) create mode 100644 apier/v1/tpaccountactions_it_test.go create mode 100644 apier/v1/tpactionplans_it_test.go diff --git a/apier/v1/tpaccountactions.go b/apier/v1/tpaccountactions.go index 796d49b7d..87045899e 100644 --- a/apier/v1/tpaccountactions.go +++ b/apier/v1/tpaccountactions.go @@ -51,9 +51,10 @@ func (self *ApierV1) GetTPAccountActionsByLoadId(attrs utils.TPAccountActions, r return utils.NewErrMandatoryIeMissing(missing...) } if aas, err := self.StorDb.GetTPAccountActions(&attrs); err != nil { - return utils.NewErrServerError(err) - } else if len(aas) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { reply = &aas } @@ -75,9 +76,10 @@ func (self *ApierV1) GetTPAccountActions(attrs AttrGetTPAccountActions, reply *u return err } if aas, err := self.StorDb.GetTPAccountActions(filter); err != nil { - return utils.NewErrServerError(err) - } else if len(aas) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { *reply = *aas[0] } diff --git a/apier/v1/tpaccountactions_it_test.go b/apier/v1/tpaccountactions_it_test.go new file mode 100644 index 000000000..d0dbe4a18 --- /dev/null +++ b/apier/v1/tpaccountactions_it_test.go @@ -0,0 +1,212 @@ +// +build offline_tp + +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" +) + +var ( + tpAccActionsCfgPath string + tpAccActionsCfg *config.CGRConfig + tpAccActionsRPC *rpc.Client + tpAccActionsDataDir = "/usr/share/cgrates" + tpAccActions *utils.TPAccountActions + tpAccActionsDelay int + tpAccActionsConfigDIR string //run tests for specific configuration + tpAccActionID = "ID:cgrates.org:1001" +) + +var sTestsTPAccActions = []func(t *testing.T){ + testTPAccActionsInitCfg, + testTPAccActionsResetStorDb, + testTPAccActionsStartEngine, + testTPAccActionsRpcConn, + testTPAccActionsGetTPAccActionBeforeSet, + testTPAccActionsSetTPAccAction, + testTPAccActionsGetTPAccActionAfterSet, + testTPAccActionsGetTPAccActionIds, + testTPAccActionsUpdateTPAccAction, + testTPAccActionsGetTPAccActionAfterUpdate, + testTPAccActionsRemTPAccAction, + testTPAccActionsGetTPAccActionAfterRemove, + testTPAccActionsKillEngine, +} + +//Test start here +func TestTPAccActionsITMySql(t *testing.T) { + tpAccActionsConfigDIR = "tutmysql" + for _, stest := range sTestsTPAccActions { + t.Run(tpAccActionsConfigDIR, stest) + } +} + +func TestTPAccActionsITMongo(t *testing.T) { + tpAccActionsConfigDIR = "tutmongo" + for _, stest := range sTestsTPAccActions { + t.Run(tpAccActionsConfigDIR, stest) + } +} + +func TestTTPAccActionsITPG(t *testing.T) { + tpAccActionsConfigDIR = "tutpostgres" + for _, stest := range sTestsTPAccActions { + t.Run(tpAccActionsConfigDIR, stest) + } +} + +func testTPAccActionsInitCfg(t *testing.T) { + var err error + tpAccActionsCfgPath = path.Join(tpAccActionsDataDir, "conf", "samples", tpAccActionsConfigDIR) + tpAccActionsCfg, err = config.NewCGRConfigFromFolder(tpAccActionsCfgPath) + if err != nil { + t.Error(err) + } + tpAccActionsCfg.DataFolderPath = tpAccActionsDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(tpAccActionsCfg) + switch tpAccActionsConfigDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + tpAccActionsDelay = 2000 + default: + tpAccActionsDelay = 1000 + } +} + +// Wipe out the cdr database +func testTPAccActionsResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpAccActionsCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPAccActionsStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpAccActionsCfgPath, tpAccActionsDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPAccActionsRpcConn(t *testing.T) { + var err error + tpAccActionsRPC, err = jsonrpc.Dial("tcp", tpAccActionsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPAccActionsGetTPAccActionBeforeSet(t *testing.T) { + var reply *utils.TPAccountActions + if err := tpAccActionsRPC.Call("ApierV1.GetTPAccountActions", &AttrGetTPAccountActions{TPid: "TPAcc", AccountActionsId: tpAccActionID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + +} + +func testTPAccActionsSetTPAccAction(t *testing.T) { + tpAccActions = &utils.TPAccountActions{ + TPid: "TPAcc", + LoadId: "ID", + Tenant: "cgrates.org", + Account: "1001", + ActionPlanId: "PREPAID_10", + ActionTriggersId: "STANDARD_TRIGGERS", + AllowNegative: true, + Disabled: false, + } + var result string + if err := tpAccActionsRPC.Call("ApierV1.SetTPAccountActions", tpAccActions, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPAccActionsGetTPAccActionAfterSet(t *testing.T) { + var reply *utils.TPAccountActions + if err := tpAccActionsRPC.Call("ApierV1.GetTPAccountActions", &AttrGetTPAccountActions{TPid: "TPAcc", AccountActionsId: tpAccActionID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpAccActions, reply) { + t.Errorf("Expecting : %+v, received: %+v", tpAccActions, reply) + } +} + +func testTPAccActionsGetTPAccActionIds(t *testing.T) { + var result []string + expectedTPID := []string{"ID"} + if err := tpAccActionsRPC.Call("ApierV1.GetTPAccountActionLoadIds", &AttrGetTPAccountActionIds{TPid: "TPAcc"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } + +} + +func testTPAccActionsUpdateTPAccAction(t *testing.T) { + tpAccActions.ActionPlanId = "PlanOne" + var result string + if err := tpAccActionsRPC.Call("ApierV1.SetTPAccountActions", tpAccActions, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + +} + +func testTPAccActionsGetTPAccActionAfterUpdate(t *testing.T) { + var reply *utils.TPAccountActions + if err := tpAccActionsRPC.Call("ApierV1.GetTPAccountActions", &AttrGetTPAccountActions{TPid: "TPAcc", AccountActionsId: tpAccActionID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpAccActions, reply) { + t.Errorf("Expecting : %+v, received: %+v", tpAccActions, reply) + } + +} + +func testTPAccActionsRemTPAccAction(t *testing.T) { + var resp string + if err := tpAccActionsRPC.Call("ApierV1.RemTPAccountActions", &AttrGetTPAccountActions{TPid: "TPAcc", AccountActionsId: tpAccActionID}, &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } + +} + +func testTPAccActionsGetTPAccActionAfterRemove(t *testing.T) { + var reply *utils.TPAccountActions + if err := tpAccActionsRPC.Call("ApierV1.GetTPAccountActions", &AttrGetTPAccountActions{TPid: "TPAcc", AccountActionsId: tpAccActionID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPAccActionsKillEngine(t *testing.T) { + if err := engine.KillEngine(tpDestinationDelay); err != nil { + t.Error(err) + } +} diff --git a/apier/v1/tpactionplans.go b/apier/v1/tpactionplans.go index a94951298..e28e57ace 100644 --- a/apier/v1/tpactionplans.go +++ b/apier/v1/tpactionplans.go @@ -53,9 +53,10 @@ func (self *ApierV1) GetTPActionPlan(attrs AttrGetTPActionPlan, reply *utils.TPA return utils.NewErrMandatoryIeMissing(missing...) } if aps, err := self.StorDb.GetTPActionPlans(attrs.TPid, attrs.ID); err != nil { - return utils.NewErrServerError(err) - } else if len(aps) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { *reply = *aps[0] } diff --git a/apier/v1/tpactionplans_it_test.go b/apier/v1/tpactionplans_it_test.go new file mode 100644 index 000000000..05b594159 --- /dev/null +++ b/apier/v1/tpactionplans_it_test.go @@ -0,0 +1,241 @@ +// +build offline_tp + +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" +) + +var ( + tpAccPlansCfgPath string + tpAccPlansCfg *config.CGRConfig + tpAccPlansRPC *rpc.Client + tpAccPlansDataDir = "/usr/share/cgrates" + tpAccPlan *utils.TPActionPlan + tpAccPlansDelay int + tpAccPlansConfigDIR string //run tests for specific configuration + +) + +var sTestsTPAccPlans = []func(t *testing.T){ + testTPAccPlansInitCfg, + testTPAccPlansResetStorDb, + testTPAccPlansStartEngine, + testTPAccPlansRpcConn, + testTPAccPlansGetTPAccPlanBeforeSet, + testTPAccPlansSetTPAccPlan, + testTPAccPlansGetTPAccPlanAfterSet, + testTPAccPlansGetTPAccPlanIds, + testTPAccPlansUpdateTPAccPlan, + testTPAccPlansGetTPAccPlanAfterUpdate, + testTPAccPlansRemTPAccPlan, + testTPAccPlansGetTPAccPlanAfterRemove, + testTPAccPlansKillEngine, +} + +//Test start here +func TestTPAccPlansITMySql(t *testing.T) { + tpAccPlansConfigDIR = "tutmysql" + for _, stest := range sTestsTPAccPlans { + t.Run(tpAccPlansConfigDIR, stest) + } +} + +func TestTPAccPlansITMongo(t *testing.T) { + tpAccPlansConfigDIR = "tutmongo" + for _, stest := range sTestsTPAccPlans { + t.Run(tpAccPlansConfigDIR, stest) + } +} + +func TestTPAccPlansITPG(t *testing.T) { + tpAccPlansConfigDIR = "tutpostgres" + for _, stest := range sTestsTPAccPlans { + t.Run(tpAccPlansConfigDIR, stest) + } +} + +func testTPAccPlansInitCfg(t *testing.T) { + var err error + tpAccPlansCfgPath = path.Join(tpAccPlansDataDir, "conf", "samples", tpAccPlansConfigDIR) + tpAccPlansCfg, err = config.NewCGRConfigFromFolder(tpAccPlansCfgPath) + if err != nil { + t.Error(err) + } + tpAccPlansCfg.DataFolderPath = tpAccPlansDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(tpAccPlansCfg) + switch tpAccPlansConfigDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + tpAccPlansDelay = 2000 + default: + tpAccPlansDelay = 1000 + } +} + +// Wipe out the cdr database +func testTPAccPlansResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpAccPlansCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPAccPlansStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpAccPlansCfgPath, tpAccPlansDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPAccPlansRpcConn(t *testing.T) { + var err error + tpAccPlansRPC, err = jsonrpc.Dial("tcp", tpAccPlansCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPAccPlansGetTPAccPlanBeforeSet(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpAccPlansRPC.Call("ApierV1.GetTPActionPlan", &AttrGetTPActionPlan{TPid: "TPAcc", ID: "ID"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPAccPlansSetTPAccPlan(t *testing.T) { + tpAccPlan = &utils.TPActionPlan{ + TPid: "TPAcc", + ID: "ID", + ActionPlan: []*utils.TPActionTiming{ + &utils.TPActionTiming{ + ActionsId: "AccId", + TimingId: "TimingID", + Weight: 10, + }, + &utils.TPActionTiming{ + ActionsId: "AccId2", + TimingId: "TimingID2", + Weight: 11, + }, + }, + } + var result string + if err := tpAccPlansRPC.Call("ApierV1.SetTPActionPlan", tpAccPlan, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPAccPlansGetTPAccPlanAfterSet(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpAccPlansRPC.Call("ApierV1.GetTPActionPlan", &AttrGetTPActionPlan{TPid: "TPAcc", ID: "ID"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpAccPlan.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpAccPlan.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpAccPlan.ID, reply.ID) { + t.Errorf("Expecting : %+v, received: %+v", tpAccPlan.ID, reply.ID) + } else if !reflect.DeepEqual(len(tpAccPlan.ActionPlan), len(reply.ActionPlan)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpAccPlan.ActionPlan), len(reply.ActionPlan)) + } +} + +func testTPAccPlansGetTPAccPlanIds(t *testing.T) { + var result []string + expectedTPID := []string{"ID"} + if err := tpAccPlansRPC.Call("ApierV1.GetTPActionPlanIds", &AttrGetTPActionPlanIds{TPid: "TPAcc"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } + +} + +func testTPAccPlansUpdateTPAccPlan(t *testing.T) { + tpAccPlan.ActionPlan = []*utils.TPActionTiming{ + &utils.TPActionTiming{ + ActionsId: "AccId", + TimingId: "TimingID", + Weight: 10, + }, + &utils.TPActionTiming{ + ActionsId: "AccId2", + TimingId: "TimingID2", + Weight: 11, + }, + &utils.TPActionTiming{ + ActionsId: "AccId3", + TimingId: "TimingID3", + Weight: 12, + }, + } + var result string + if err := tpAccPlansRPC.Call("ApierV1.SetTPActionPlan", tpAccPlan, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + +} + +func testTPAccPlansGetTPAccPlanAfterUpdate(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpAccPlansRPC.Call("ApierV1.GetTPActionPlan", &AttrGetTPActionPlan{TPid: "TPAcc", ID: "ID"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpAccPlan.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpAccPlan.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpAccPlan.ID, reply.ID) { + t.Errorf("Expecting : %+v, received: %+v", tpAccPlan.ID, reply.ID) + } else if !reflect.DeepEqual(len(tpAccPlan.ActionPlan), len(reply.ActionPlan)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpAccPlan.ActionPlan), len(reply.ActionPlan)) + } + +} + +func testTPAccPlansRemTPAccPlan(t *testing.T) { + var resp string + if err := tpAccPlansRPC.Call("ApierV1.RemTPActionPlan", &AttrGetTPActionPlan{TPid: "TPAcc", ID: "ID"}, &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } + +} + +func testTPAccPlansGetTPAccPlanAfterRemove(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpAccPlansRPC.Call("ApierV1.GetTPActionPlan", &AttrGetTPActionPlan{TPid: "TPAcc", ID: "ID"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPAccPlansKillEngine(t *testing.T) { + if err := engine.KillEngine(tpAccPlansDelay); err != nil { + t.Error(err) + } +} From 96f2d0fc170fe3cbe5706bd5da286bd40e8a25e6 Mon Sep 17 00:00:00 2001 From: TeoV Date: Thu, 14 Sep 2017 16:56:13 +0300 Subject: [PATCH 04/20] Add test fot TPActions --- apier/v1/tpactions.go | 7 +- apier/v1/tpactions_it_test.go | 315 ++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 apier/v1/tpactions_it_test.go diff --git a/apier/v1/tpactions.go b/apier/v1/tpactions.go index b141ba93d..8615f6b52 100644 --- a/apier/v1/tpactions.go +++ b/apier/v1/tpactions.go @@ -45,9 +45,10 @@ func (self *ApierV1) GetTPActions(attrs AttrGetTPActions, reply *utils.TPActions return utils.NewErrMandatoryIeMissing(missing...) } if as, err := self.StorDb.GetTPActions(attrs.TPid, attrs.ID); err != nil { - return utils.NewErrServerError(err) - } else if len(as) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { *reply = *as[0] } diff --git a/apier/v1/tpactions_it_test.go b/apier/v1/tpactions_it_test.go new file mode 100644 index 000000000..4eac031b1 --- /dev/null +++ b/apier/v1/tpactions_it_test.go @@ -0,0 +1,315 @@ +// +build offline_tp + +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" +) + +var ( + tpActionCfgPath string + tpActionCfg *config.CGRConfig + tpActionRPC *rpc.Client + tpActionDataDir = "/usr/share/cgrates" + tpActions *utils.TPActions + tpActionDelay int + tpActionConfigDIR string //run tests for specific configuration + +) + +var sTestsTPActions = []func(t *testing.T){ + testTPActionsInitCfg, + testTPActionsResetStorDb, + testTPActionsStartEngine, + testTPActionsRpcConn, + testTPActionsGetTPActionBeforeSet, + testTPActionsSetTPAction, + testTPActionsGetTPActionAfterSet, + testTPActionsGetTPActionIds, + testTPActionsUpdateTPAction, + testTPActionsGetTPActionAfterUpdate, + testTPActionsRemTPAction, + testTPActionsGetTPActionAfterRemove, + testTPActionsKillEngine, +} + +//Test start here +func TestTPActionsITMySql(t *testing.T) { + tpActionConfigDIR = "tutmysql" + for _, stest := range sTestsTPActions { + t.Run(tpActionConfigDIR, stest) + } +} + +func TestTPActionsITMongo(t *testing.T) { + tpActionConfigDIR = "tutmongo" + for _, stest := range sTestsTPActions { + t.Run(tpActionConfigDIR, stest) + } +} + +func TestTPActionsITPG(t *testing.T) { + tpActionConfigDIR = "tutpostgres" + for _, stest := range sTestsTPActions { + t.Run(tpActionConfigDIR, stest) + } +} + +func testTPActionsInitCfg(t *testing.T) { + var err error + tpActionCfgPath = path.Join(tpActionDataDir, "conf", "samples", tpActionConfigDIR) + tpActionCfg, err = config.NewCGRConfigFromFolder(tpActionCfgPath) + if err != nil { + t.Error(err) + } + tpActionCfg.DataFolderPath = tpActionDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(tpActionCfg) + switch tpActionConfigDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + tpActionDelay = 2000 + default: + tpActionDelay = 1000 + } +} + +// Wipe out the cdr database +func testTPActionsResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpActionCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPActionsStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpActionCfgPath, tpActionDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPActionsRpcConn(t *testing.T) { + var err error + tpActionRPC, err = jsonrpc.Dial("tcp", tpActionCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPActionsGetTPActionBeforeSet(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpActionRPC.Call("ApierV1.GetTPActions", &AttrGetTPActions{TPid: "TPAcc", ID: "ID"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPActionsSetTPAction(t *testing.T) { + tpActions = &utils.TPActions{ + TPid: "TPAcc", + ID: "ID", + Actions: []*utils.TPAction{ + &utils.TPAction{ + Identifier: "*topup_reset", + BalanceId: "BalID", + BalanceUuid: "BalUuid", + BalanceType: "*data", + Directions: "*out", + Units: "10", + ExpiryTime: "*unlimited", + Filter: "", + TimingTags: "2014-01-14T00:00:00Z", + DestinationIds: "DST_1002", + RatingSubject: "SPECIAL_1002", + Categories: "", + SharedGroups: "SHARED_A", + BalanceWeight: "10", + ExtraParameters: "", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 10, + }, + &utils.TPAction{ + Identifier: "*log", + BalanceId: "BalID", + BalanceUuid: "BalUuid", + BalanceType: "*monetary", + Directions: "*out", + Units: "120", + ExpiryTime: "*unlimited", + Filter: "", + TimingTags: "2014-01-14T00:00:00Z", + DestinationIds: "*any", + RatingSubject: "SPECIAL_1002", + Categories: "", + SharedGroups: "SHARED_A", + BalanceWeight: "11", + ExtraParameters: "", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 11, + }, + }, + } + var result string + if err := tpActionRPC.Call("ApierV1.SetTPActions", tpActions, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPActionsGetTPActionAfterSet(t *testing.T) { + var reply *utils.TPActions + if err := tpActionRPC.Call("ApierV1.GetTPActions", &AttrGetTPActions{TPid: "TPAcc", ID: "ID"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpActions.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpActions.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpActions.ID, reply.ID) { + t.Errorf("Expecting : %+v, received: %+v", tpActions.ID, reply.ID) + } else if !reflect.DeepEqual(len(tpActions.Actions), len(reply.Actions)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpActions.Actions), len(reply.Actions)) + } +} + +func testTPActionsGetTPActionIds(t *testing.T) { + var result []string + expectedTPID := []string{"ID"} + if err := tpActionRPC.Call("ApierV1.GetTPActionIds", &AttrGetTPActionIds{TPid: "TPAcc"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } +} + +func testTPActionsUpdateTPAction(t *testing.T) { + tpActions.Actions = []*utils.TPAction{ + &utils.TPAction{ + Identifier: "*topup_reset", + BalanceId: "BalID", + BalanceUuid: "BalUuid", + BalanceType: "*data", + Directions: "*out", + Units: "10", + ExpiryTime: "*unlimited", + Filter: "", + TimingTags: "2014-01-14T00:00:00Z", + DestinationIds: "DST_1002", + RatingSubject: "SPECIAL_1002", + Categories: "", + SharedGroups: "SHARED_A", + BalanceWeight: "10", + ExtraParameters: "", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 10, + }, + &utils.TPAction{ + Identifier: "*log", + BalanceId: "BalID", + BalanceUuid: "BalUuid", + BalanceType: "*monetary", + Directions: "*out", + Units: "120", + ExpiryTime: "*unlimited", + Filter: "", + TimingTags: "2014-01-14T00:00:00Z", + DestinationIds: "*any", + RatingSubject: "SPECIAL_1002", + Categories: "", + SharedGroups: "SHARED_A", + BalanceWeight: "11", + ExtraParameters: "", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 11, + }, + &utils.TPAction{ + Identifier: "*topup", + BalanceId: "BalID", + BalanceUuid: "BalUuid", + BalanceType: "*voice", + Directions: "*out", + Units: "102400", + ExpiryTime: "*unlimited", + Filter: "", + TimingTags: "2014-01-14T00:00:00Z", + DestinationIds: "*any", + RatingSubject: "SPECIAL_1002", + Categories: "", + SharedGroups: "SHARED_A", + BalanceWeight: "20", + ExtraParameters: "", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 11, + }, + } + var result string + if err := tpActionRPC.Call("ApierV1.SetTPActions", tpActions, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + +} + +func testTPActionsGetTPActionAfterUpdate(t *testing.T) { + var reply *utils.TPActions + if err := tpActionRPC.Call("ApierV1.GetTPActions", &AttrGetTPActions{TPid: "TPAcc", ID: "ID"}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpActions.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpActions.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpActions.ID, reply.ID) { + t.Errorf("Expecting : %+v, received: %+v", tpActions.ID, reply.ID) + } else if !reflect.DeepEqual(len(tpActions.Actions), len(reply.Actions)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpActions.Actions), len(reply.Actions)) + } + +} + +func testTPActionsRemTPAction(t *testing.T) { + var resp string + if err := tpActionRPC.Call("ApierV1.RemTPActions", &AttrGetTPActions{TPid: "TPAcc", ID: "ID"}, &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } + +} + +func testTPActionsGetTPActionAfterRemove(t *testing.T) { + var reply *utils.TPActionPlan + if err := tpActionRPC.Call("ApierV1.GetTPActions", &AttrGetTPActions{TPid: "TPAcc", ID: "ID"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPActionsKillEngine(t *testing.T) { + if err := engine.KillEngine(tpActionDelay); err != nil { + t.Error(err) + } +} From ba67a515449f5eb17537694537915b8756df6d26 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 12:49:06 +0300 Subject: [PATCH 05/20] Add test for TPDerivedCahrges --- apier/v1/tpderivedcharges.go | 7 +- apier/v1/tpderivedcharges_it_test.go | 305 +++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 apier/v1/tpderivedcharges_it_test.go diff --git a/apier/v1/tpderivedcharges.go b/apier/v1/tpderivedcharges.go index 2ba3b3655..89002d0b8 100644 --- a/apier/v1/tpderivedcharges.go +++ b/apier/v1/tpderivedcharges.go @@ -48,9 +48,10 @@ func (self *ApierV1) GetTPDerivedChargers(attrs AttrGetTPDerivedChargers, reply filter := &utils.TPDerivedChargers{TPid: attrs.TPid} filter.SetDerivedChargersId(attrs.DerivedChargersId) if dcs, err := self.StorDb.GetTPDerivedChargers(filter); err != nil { - return utils.NewErrServerError(err) - } else if len(dcs) == 0 { - return utils.ErrNotFound + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err } else { *reply = *dcs[0] } diff --git a/apier/v1/tpderivedcharges_it_test.go b/apier/v1/tpderivedcharges_it_test.go new file mode 100644 index 000000000..9aeba19c0 --- /dev/null +++ b/apier/v1/tpderivedcharges_it_test.go @@ -0,0 +1,305 @@ +// +build offline_tp + +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "testing" +) + +var ( + tpDerivedChargersCfgPath string + tpDerivedChargersCfg *config.CGRConfig + tpDerivedChargersRPC *rpc.Client + tpDerivedChargersDataDir = "/usr/share/cgrates" + tpDerivedChargers *utils.TPDerivedChargers + tpDerivedChargersDelay int + tpDerivedChargersConfigDIR string //run tests for specific configuration + tpDerivedChargersID = "LoadID:*out:cgrates.org:call:1001:1001" +) + +var sTestsTPDerivedChargers = []func(t *testing.T){ + testTPDerivedChargersInitCfg, + testTPDerivedChargersResetStorDb, + testTPDerivedChargersStartEngine, + testTPDerivedChargersRpcConn, + testTPDerivedChargersGetTPDerivedChargersBeforeSet, + testTPDerivedChargersSetTPDerivedChargers, + testTPDerivedChargersGetTPDerivedChargersAfterSet, + testTPDerivedChargersGetTPDerivedChargerIds, + testTPDerivedChargersUpdateTPDerivedChargers, + testTPDerivedChargersGetTPDerivedChargersAfterUpdate, + testTPDerivedChargersRemTPDerivedChargers, + testTPDerivedChargersGetTPDerivedChargersAfterRemove, + testTPDerivedChargersKillEngine, +} + +//Test start here +func TestTPDerivedChargersITMySql(t *testing.T) { + tpDerivedChargersConfigDIR = "tutmysql" + for _, stest := range sTestsTPDerivedChargers { + t.Run(tpDerivedChargersConfigDIR, stest) + } +} + +func TestTPDerivedChargersITMongo(t *testing.T) { + tpDerivedChargersConfigDIR = "tutmongo" + for _, stest := range sTestsTPDerivedChargers { + t.Run(tpDerivedChargersConfigDIR, stest) + } +} + +func TestTPDerivedChargersITPG(t *testing.T) { + tpDerivedChargersConfigDIR = "tutpostgres" + for _, stest := range sTestsTPDerivedChargers { + t.Run(tpDerivedChargersConfigDIR, stest) + } +} + +func testTPDerivedChargersInitCfg(t *testing.T) { + var err error + tpDerivedChargersCfgPath = path.Join(tpDerivedChargersDataDir, "conf", "samples", tpDerivedChargersConfigDIR) + tpDerivedChargersCfg, err = config.NewCGRConfigFromFolder(tpDerivedChargersCfgPath) + if err != nil { + t.Error(err) + } + tpDerivedChargersCfg.DataFolderPath = tpDerivedChargersDataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(tpDerivedChargersCfg) + switch tpDerivedChargersConfigDIR { + case "tutmongo": // Mongo needs more time to reset db, need to investigate + tpDerivedChargersDelay = 2000 + default: + tpDerivedChargersDelay = 1000 + } +} + +// Wipe out the cdr database +func testTPDerivedChargersResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpDerivedChargersCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPDerivedChargersStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpDerivedChargersCfgPath, tpDerivedChargersDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPDerivedChargersRpcConn(t *testing.T) { + var err error + tpDerivedChargersRPC, err = jsonrpc.Dial("tcp", tpDerivedChargersCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPDerivedChargersGetTPDerivedChargersBeforeSet(t *testing.T) { + var reply *utils.TPDerivedChargers + if err := tpDerivedChargersRPC.Call("ApierV1.GetTPDerivedChargers", &AttrGetTPDerivedChargers{TPid: "TPD", DerivedChargersId: tpDerivedChargersID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + +} + +func testTPDerivedChargersSetTPDerivedChargers(t *testing.T) { + tpDerivedChargers = &utils.TPDerivedChargers{ + TPid: "TPD", + LoadId: "LoadID", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + DestinationIds: "", + DerivedChargers: []*utils.TPDerivedCharger{ + &utils.TPDerivedCharger{ + RunId: "derived_run1", + RunFilters: "", + ReqTypeField: "^*rated", + DirectionField: "*default", + TenantField: "*default", + CategoryField: "*default", + AccountField: "*default", + SubjectField: "^1002", + DestinationField: "*default", + SetupTimeField: "*default", + PddField: "*default", + AnswerTimeField: "*default", + UsageField: "*default", + SupplierField: "*default", + DisconnectCauseField: "*default", + CostField: "*default", + RatedField: "*default", + }, + }, + } + var result string + if err := tpDerivedChargersRPC.Call("ApierV1.SetTPDerivedChargers", tpDerivedChargers, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPDerivedChargersGetTPDerivedChargersAfterSet(t *testing.T) { + var reply *utils.TPDerivedChargers + if err := tpDerivedChargersRPC.Call("ApierV1.GetTPDerivedChargers", &AttrGetTPDerivedChargers{TPid: "TPD", DerivedChargersId: tpDerivedChargersID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpDerivedChargers.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpDerivedChargers.LoadId, reply.LoadId) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.LoadId, reply.LoadId) + } else if !reflect.DeepEqual(tpDerivedChargers.Direction, reply.Direction) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Direction, reply.Direction) + } else if !reflect.DeepEqual(tpDerivedChargers.Tenant, reply.Tenant) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Tenant, reply.Tenant) + } else if !reflect.DeepEqual(tpDerivedChargers.Category, reply.Category) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Category, reply.Category) + } else if !reflect.DeepEqual(tpDerivedChargers.Account, reply.Account) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Account, reply.Account) + } else if !reflect.DeepEqual(tpDerivedChargers.Subject, reply.Subject) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Subject, reply.Subject) + } else if !reflect.DeepEqual(tpDerivedChargers.DestinationIds, reply.DestinationIds) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.DestinationIds, reply.DestinationIds) + } else if !reflect.DeepEqual(len(tpDerivedChargers.DerivedChargers), len(reply.DerivedChargers)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpDerivedChargers.DerivedChargers), len(reply.DerivedChargers)) + } + +} + +func testTPDerivedChargersGetTPDerivedChargerIds(t *testing.T) { + var result []string + expectedTPID := []string{"LoadID:*out:cgrates.org:call:1001:1001"} + if err := tpDerivedChargersRPC.Call("ApierV1.GetTPDerivedChargerIds", &AttrGetTPDerivedChargeIds{TPid: "TPD"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } + +} + +func testTPDerivedChargersUpdateTPDerivedChargers(t *testing.T) { + tpDerivedChargers.DerivedChargers = []*utils.TPDerivedCharger{ + &utils.TPDerivedCharger{ + RunId: "derived_run1", + RunFilters: "", + ReqTypeField: "^*rated", + DirectionField: "*default", + TenantField: "*default", + CategoryField: "*default", + AccountField: "*default", + SubjectField: "^1002", + DestinationField: "*default", + SetupTimeField: "*default", + PddField: "*default", + AnswerTimeField: "*default", + UsageField: "*default", + SupplierField: "*default", + DisconnectCauseField: "*default", + CostField: "*default", + RatedField: "*default", + }, + &utils.TPDerivedCharger{ + RunId: "derived_run2", + RunFilters: "", + ReqTypeField: "^*rated", + DirectionField: "*default", + TenantField: "*default", + CategoryField: "*default", + AccountField: "*default", + SubjectField: "^1003", + DestinationField: "*default", + SetupTimeField: "*default", + PddField: "*default", + AnswerTimeField: "*default", + UsageField: "*default", + SupplierField: "*default", + DisconnectCauseField: "*default", + CostField: "*default", + RatedField: "*default", + }, + } + var result string + if err := tpDerivedChargersRPC.Call("ApierV1.SetTPDerivedChargers", tpDerivedChargers, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + +} + +func testTPDerivedChargersGetTPDerivedChargersAfterUpdate(t *testing.T) { + var reply *utils.TPDerivedChargers + if err := tpDerivedChargersRPC.Call("ApierV1.GetTPDerivedChargers", &AttrGetTPDerivedChargers{TPid: "TPD", DerivedChargersId: tpDerivedChargersID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tpDerivedChargers.TPid, reply.TPid) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.TPid, reply.TPid) + } else if !reflect.DeepEqual(tpDerivedChargers.LoadId, reply.LoadId) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.LoadId, reply.LoadId) + } else if !reflect.DeepEqual(tpDerivedChargers.Direction, reply.Direction) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Direction, reply.Direction) + } else if !reflect.DeepEqual(tpDerivedChargers.Tenant, reply.Tenant) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Tenant, reply.Tenant) + } else if !reflect.DeepEqual(tpDerivedChargers.Category, reply.Category) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Category, reply.Category) + } else if !reflect.DeepEqual(tpDerivedChargers.Account, reply.Account) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Account, reply.Account) + } else if !reflect.DeepEqual(tpDerivedChargers.Subject, reply.Subject) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.Subject, reply.Subject) + } else if !reflect.DeepEqual(tpDerivedChargers.DestinationIds, reply.DestinationIds) { + t.Errorf("Expecting : %+v, received: %+v", tpDerivedChargers.DestinationIds, reply.DestinationIds) + } else if !reflect.DeepEqual(len(tpDerivedChargers.DerivedChargers), len(reply.DerivedChargers)) { + t.Errorf("Expecting : %+v, received: %+v", len(tpDerivedChargers.DerivedChargers), len(reply.DerivedChargers)) + } + +} + +func testTPDerivedChargersRemTPDerivedChargers(t *testing.T) { + var resp string + if err := tpDerivedChargersRPC.Call("ApierV1.RemTPDerivedChargers", &AttrGetTPDerivedChargers{TPid: "TPD", DerivedChargersId: tpDerivedChargersID}, &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } + +} + +func testTPDerivedChargersGetTPDerivedChargersAfterRemove(t *testing.T) { + var reply *utils.TPDerivedChargers + if err := tpDerivedChargersRPC.Call("ApierV1.GetTPDerivedChargers", &AttrGetTPDerivedChargers{TPid: "TPD", DerivedChargersId: tpDerivedChargersID}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPDerivedChargersKillEngine(t *testing.T) { + if err := engine.KillEngine(tpDerivedChargersDelay); err != nil { + t.Error(err) + } +} From 95565e8ff088fb8ccacdb735f4c127c82894d822 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 15 Sep 2017 11:55:50 +0200 Subject: [PATCH 06/20] Loader to consider tenant when building up resource indexes --- data/tariffplans/tutorial/Resources.csv | 14 ++-- engine/loader_csv_test.go | 78 ++++++++++---------- engine/resources.go | 5 ++ engine/tp_reader.go | 98 ++++++++++++++----------- utils/coreutils.go | 20 +++++ 5 files changed, 126 insertions(+), 89 deletions(-) diff --git a/data/tariffplans/tutorial/Resources.csv b/data/tariffplans/tutorial/Resources.csv index 30f655dcc..e67f01a71 100755 --- a/data/tariffplans/tutorial/Resources.csv +++ b/data/tariffplans/tutorial/Resources.csv @@ -1,8 +1,8 @@ #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -Tester,ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, -Tester,ResGroup1,*string_prefix,Destination,10;20,,,,,,,, -Tester,ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, -Tester,ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, -Tester,ResGroup3,*string,Account,3001,2014-07-29T15:00:00Z,1s,3,,true,true,20, -#ResGroup3,*timings,SetupTime,PEAK,,,,,,,, -#ResGroup3,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, +cgrates.org,ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, +cgrates.org,ResGroup1,*string_prefix,Destination,10;20,,,,,,,, +cgrates.org,ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, +cgrates.org,ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, +cgrates.org,ResGroup3,*string,Account,3001,2014-07-29T15:00:00Z,1s,3,,true,true,20, +#cgrates.org,ResGroup3,*timings,SetupTime,PEAK,,,,,,,, +#cgrates.org,ResGroup3,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0ff3b6eee..39c0c3823 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -267,10 +267,10 @@ cgrates.org,mas,true,another,value,10 ` resProfiles = ` #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -Tester,ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, -Tester,ResGroup21,*string_prefix,HdrDestination,10;20,,,,,,,, -Tester,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, -Tester,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, +cgrates.org,ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, +cgrates.org,ResGroup21,*string_prefix,HdrDestination,10;20,,,,,,,, +cgrates.org,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, +cgrates.org,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, ` stats = ` #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] @@ -1390,44 +1390,46 @@ func TestLoadReverseAliases(t *testing.T) { } func TestLoadResourceProfiles(t *testing.T) { - eResProfiles := map[string]*utils.TPResource{ - "ResGroup21": &utils.TPResource{ - TPid: testTPID, - Tenant: "Tester", - ID: "ResGroup21", - Filters: []*utils.TPRequestFilter{ - &utils.TPRequestFilter{Type: MetaString, FieldName: "HdrAccount", Values: []string{"1001", "1002"}}, - &utils.TPRequestFilter{Type: MetaStringPrefix, FieldName: "HdrDestination", Values: []string{"10", "20"}}, - &utils.TPRequestFilter{Type: MetaRSRFields, Values: []string{"HdrSubject(~^1.*1$)", "HdrDestination(1002)"}}, + eResProfiles := map[string]map[string]*utils.TPResource{ + "cgrates.org": map[string]*utils.TPResource{ + "ResGroup21": &utils.TPResource{ + TPid: testTPID, + Tenant: "cgrates.org", + ID: "ResGroup21", + Filters: []*utils.TPRequestFilter{ + &utils.TPRequestFilter{Type: MetaString, FieldName: "HdrAccount", Values: []string{"1001", "1002"}}, + &utils.TPRequestFilter{Type: MetaStringPrefix, FieldName: "HdrDestination", Values: []string{"10", "20"}}, + &utils.TPRequestFilter{Type: MetaRSRFields, Values: []string{"HdrSubject(~^1.*1$)", "HdrDestination(1002)"}}, + }, + ActivationInterval: &utils.TPActivationInterval{ + ActivationTime: "2014-07-29T15:00:00Z", + }, + UsageTTL: "1s", + AllocationMessage: "call", + Weight: 10, + Limit: "2", }, - ActivationInterval: &utils.TPActivationInterval{ - ActivationTime: "2014-07-29T15:00:00Z", + "ResGroup22": &utils.TPResource{ + TPid: testTPID, + Tenant: "cgrates.org", + ID: "ResGroup22", + Filters: []*utils.TPRequestFilter{ + &utils.TPRequestFilter{Type: MetaDestinations, FieldName: "HdrDestination", Values: []string{"DST_FS"}}, + }, + ActivationInterval: &utils.TPActivationInterval{ + ActivationTime: "2014-07-29T15:00:00Z", + }, + UsageTTL: "3600s", + AllocationMessage: "premium_call", + Blocker: true, + Stored: true, + Weight: 10, + Limit: "2", }, - UsageTTL: "1s", - AllocationMessage: "call", - Weight: 10, - Limit: "2", - }, - "ResGroup22": &utils.TPResource{ - TPid: testTPID, - Tenant: "Tester", - ID: "ResGroup22", - Filters: []*utils.TPRequestFilter{ - &utils.TPRequestFilter{Type: MetaDestinations, FieldName: "HdrDestination", Values: []string{"DST_FS"}}, - }, - ActivationInterval: &utils.TPActivationInterval{ - ActivationTime: "2014-07-29T15:00:00Z", - }, - UsageTTL: "3600s", - AllocationMessage: "premium_call", - Blocker: true, - Stored: true, - Weight: 10, - Limit: "2", }, } - if len(csvr.resProfiles) != len(eResProfiles) { - t.Error("Failed to load resourceProfiles: ", len(csvr.resProfiles)) + if len(csvr.resProfiles["cgrates.org"]) != len(eResProfiles["cgrates.org"]) { + t.Errorf("Failed to load resourceProfiles: %s", utils.ToIJSON(csvr.resProfiles)) } else if !reflect.DeepEqual(eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) { t.Errorf("Expecting: %+v, received: %+v", eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) diff --git a/engine/resources.go b/engine/resources.go index 40a2c5ba7..5936ba07e 100755 --- a/engine/resources.go +++ b/engine/resources.go @@ -52,6 +52,11 @@ type ResourceProfile struct { Thresholds []string // Thresholds to check after changing Limit } +// TenantID returns unique identifier of the ResourceProfile in a multi-tenant environment +func (rp *ResourceProfile) TenantID() string { + return utils.ConcatenatedKey(rp.Tenant, rp.ID) +} + // ResourceUsage represents an usage counted type ResourceUsage struct { Tenant string diff --git a/engine/tp_reader.go b/engine/tp_reader.go index d1959c0de..62d509641 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -53,11 +53,11 @@ type TpReader struct { cdrStats map[string]*CdrStats users map[string]*UserProfile aliases map[string]*Alias - resProfiles map[string]*utils.TPResource + resProfiles map[string]map[string]*utils.TPResource sqProfiles map[string]*utils.TPStats thresholds map[string]*utils.TPThreshold - resources []string // IDs of resources which need creation based on resourceProfiles - statQueues []string // IDs of statQueues which need creation based on statQueueProfiles + resources []*utils.TenantID // IDs of resources which need creation based on resourceProfiles + statQueues []string // IDs of statQueues which need creation based on statQueueProfiles revDests, revAliases, @@ -129,7 +129,7 @@ func (tpr *TpReader) Init() { tpr.users = make(map[string]*UserProfile) tpr.aliases = make(map[string]*Alias) tpr.derivedChargers = make(map[string]*utils.DerivedChargers) - tpr.resProfiles = make(map[string]*utils.TPResource) + tpr.resProfiles = make(map[string]map[string]*utils.TPResource) tpr.sqProfiles = make(map[string]*utils.TPStats) tpr.thresholds = make(map[string]*utils.TPThreshold) tpr.revDests = make(map[string][]string) @@ -1599,16 +1599,22 @@ func (tpr *TpReader) LoadResourceProfilesFiltered(tag string) error { if err != nil { return err } - mapRsPs := make(map[string]*utils.TPResource) + mapRsPfls := make(map[string]map[string]*utils.TPResource) for _, rl := range rls { - mapRsPs[rl.ID] = rl + if _, has := mapRsPfls[rl.Tenant]; !has { + mapRsPfls[rl.Tenant] = make(map[string]*utils.TPResource) + } + mapRsPfls[rl.Tenant][rl.ID] = rl } - tpr.resProfiles = mapRsPs - for rID := range mapRsPs { - if has, err := tpr.dataStorage.HasData(utils.ResourcesPrefix, rID); err != nil { - return err - } else if !has { - tpr.resources = append(tpr.resources, rID) + tpr.resProfiles = mapRsPfls + for tenant, mpID := range mapRsPfls { + for id := range mpID { + rTid := &utils.TenantID{tenant, id} + if has, err := tpr.dataStorage.HasData(utils.ResourcesPrefix, rTid.TenantID()); err != nil { + return err + } else if !has { + tpr.resources = append(tpr.resources, rTid) + } } } return nil @@ -1952,27 +1958,29 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if verbose { log.Print("ResourceProfiles:") } - for _, tpRsp := range tpr.resProfiles { - rsp, err := APItoResource(tpRsp, tpr.timezone) - if err != nil { - return err - } - if err = tpr.dataStorage.SetResourceProfile(rsp, utils.NonTransactional); err != nil { - return err - } - if verbose { - log.Print("\t", rsp.ID) + for _, mpID := range tpr.resProfiles { + for _, tpRsp := range mpID { + rsp, err := APItoResource(tpRsp, tpr.timezone) + if err != nil { + return err + } + if err = tpr.dataStorage.SetResourceProfile(rsp, utils.NonTransactional); err != nil { + return err + } + if verbose { + log.Print("\t", rsp.TenantID()) + } } } if verbose { log.Print("Resources:") } - for _, rID := range tpr.resources { - if err = tpr.dataStorage.SetResource(&Resource{ID: rID, Usages: make(map[string]*ResourceUsage)}); err != nil { + for _, rTid := range tpr.resources { + if err = tpr.dataStorage.SetResource(&Resource{Tenant: rTid.Tenant, ID: rTid.ID, Usages: make(map[string]*ResourceUsage)}); err != nil { return } if verbose { - log.Print("\t", rID) + log.Print("\t", rTid.TenantID()) } } if verbose { @@ -2057,22 +2065,24 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if verbose { log.Print("Indexing resource profiles") } - rlIdxr, err := NewReqFilterIndexer(tpr.dataStorage, utils.ResourceProfilesIndex) - if err != nil { - return err - } - for _, tpRL := range tpr.resProfiles { - if rl, err := APItoResource(tpRL, tpr.timezone); err != nil { + for tenant, mpID := range tpr.resProfiles { + rlIdxr, err := NewReqFilterIndexer(tpr.dataStorage, utils.ResourceProfilesIndex+tenant) + if err != nil { + return err + } + for _, tpRL := range mpID { + if rl, err := APItoResource(tpRL, tpr.timezone); err != nil { + return err + } else { + rlIdxr.IndexFilters(rl.ID, rl.Filters) + } + } + if verbose { + log.Printf("Indexed ResourceProfile tenant: %s keys: %+v", tenant, rlIdxr.ChangedKeys().Slice()) + } + if err := rlIdxr.StoreIndexes(); err != nil { return err - } else { - rlIdxr.IndexFilters(rl.ID, rl.Filters) } - } - if verbose { - log.Printf("Indexed ResourceProfile keys: %+v", rlIdxr.ChangedKeys().Slice()) - } - if err := rlIdxr.StoreIndexes(); err != nil { - return err } } if len(tpr.sqProfiles) > 0 { @@ -2294,11 +2304,11 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { } return keys, nil case utils.ResourceProfilesPrefix: - keys := make([]string, len(tpr.resProfiles)) - i := 0 - for k := range tpr.resProfiles { - keys[i] = k - i++ + keys := make([]string, 0) + for tenant, mpID := range tpr.resProfiles { + for id := range mpID { + keys = append(keys, utils.ConcatenatedKey(tenant, id)) + } } return keys, nil case utils.ACTION_TRIGGER_PREFIX: diff --git a/utils/coreutils.go b/utils/coreutils.go index 7f0240d7f..df8db9ec3 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -759,3 +759,23 @@ func AppendToFile(fName, text string) error { f.Close() return nil } + +func NewTenantID(tntID string) *TenantID { + if strings.Index(tntID, CONCATENATED_KEY_SEP) == -1 { // no :, ID without Tenant + return &TenantID{ID: tntID} + } + tIDSplt := strings.Split(tntID, CONCATENATED_KEY_SEP) + if len(tIDSplt) == 1 { // only Tenant present + return &TenantID{Tenant: tIDSplt[0]} + } + return &TenantID{Tenant: tIDSplt[0], ID: tIDSplt[1]} +} + +type TenantID struct { + Tenant string + ID string +} + +func (tID *TenantID) TenantID() string { + return ConcatenatedKey(tID.Tenant, tID.ID) +} From 9fbe0eefa4c85ee9f936ffc3533bf98200ccf945 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 15 Sep 2017 14:18:56 +0200 Subject: [PATCH 07/20] Tenant in Resource --- apier/v1/resourcesv1.go | 23 ++++++++-------- engine/resources.go | 49 +++++++++++++++++++++++----------- engine/storage_interface.go | 8 +++--- engine/storage_map.go | 27 ++++++++++--------- engine/storage_mongo_datadb.go | 36 ++++++++++++++----------- engine/storage_redis.go | 26 +++++++++--------- 6 files changed, 98 insertions(+), 71 deletions(-) diff --git a/apier/v1/resourcesv1.go b/apier/v1/resourcesv1.go index 812ae545c..1bb3d5880 100644 --- a/apier/v1/resourcesv1.go +++ b/apier/v1/resourcesv1.go @@ -81,16 +81,17 @@ func (rsv1 *ResourceSV1) ReleaseResource(args utils.AttrRLsResourceUsage, reply return rsv1.rls.V1ReleaseResource(args, reply) } -type AttrGetResPrf struct { - ID string +type ArgGetResPrf struct { + Tenant string + ID string } // GetResourceProfile returns a resource configuration -func (apierV1 *ApierV1) GetResourceProfile(attr AttrGetResPrf, reply *engine.ResourceProfile) error { - if missing := utils.MissingStructFields(&attr, []string{"ID"}); len(missing) != 0 { //Params missing +func (apierV1 *ApierV1) GetResourceProfile(arg ArgGetResPrf, reply *engine.ResourceProfile) error { + if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if rcfg, err := apierV1.DataDB.GetResourceProfile(attr.ID, true, utils.NonTransactional); err != nil { + if rcfg, err := apierV1.DataDB.GetResourceProfile(arg.Tenant, arg.ID, true, utils.NonTransactional); err != nil { if err.Error() != utils.ErrNotFound.Error() { err = utils.NewErrServerError(err) } @@ -102,11 +103,11 @@ func (apierV1 *ApierV1) GetResourceProfile(attr AttrGetResPrf, reply *engine.Res } //SetResourceProfile add a new resource configuration -func (apierV1 *ApierV1) SetResourceProfile(attr *engine.ResourceProfile, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"ID"}); len(missing) != 0 { +func (apierV1 *ApierV1) SetResourceProfile(res *engine.ResourceProfile, reply *string) error { + if missing := utils.MissingStructFields(res, []string{"Tenant", "ID"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if err := apierV1.DataDB.SetResourceProfile(attr, utils.NonTransactional); err != nil { + if err := apierV1.DataDB.SetResourceProfile(res, utils.NonTransactional); err != nil { return utils.APIErrorHandler(err) } *reply = utils.OK @@ -114,11 +115,11 @@ func (apierV1 *ApierV1) SetResourceProfile(attr *engine.ResourceProfile, reply * } //RemResourceProfile remove a specific resource configuration -func (apierV1 *ApierV1) RemResourceProfile(attrs AttrGetResPrf, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"ID"}); len(missing) != 0 { //Params missing +func (apierV1 *ApierV1) RemResourceProfile(arg ArgGetResPrf, reply *string) error { + if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if err := apierV1.DataDB.RemoveResourceProfile(attrs.ID, utils.NonTransactional); err != nil { + if err := apierV1.DataDB.RemoveResourceProfile(arg.Tenant, arg.ID, utils.NonTransactional); err != nil { if err.Error() != utils.ErrNotFound.Error() { err = utils.NewErrServerError(err) } diff --git a/engine/resources.go b/engine/resources.go index 5936ba07e..422385269 100755 --- a/engine/resources.go +++ b/engine/resources.go @@ -82,6 +82,11 @@ type Resource struct { rPrf *ResourceProfile // for ordering purposes } +// TenantID returns the unique ID in a multi-tenant environment +func (r *Resource) TenantID() string { + return utils.ConcatenatedKey(r.Tenant, r.ID) +} + // removeExpiredUnits removes units which are expired from the resource func (r *Resource) removeExpiredUnits() { var firstActive int @@ -187,13 +192,21 @@ func (rs Resources) clearUsage(ruID string) (err error) { return } -// ids returns list of resource IDs in resources -func (rs Resources) ids() (ids []string) { - ids = make([]string, len(rs)) +// tenantIDs returns list of TenantIDs in resources +func (rs Resources) tenantIDs() []*utils.TenantID { + tntIDs := make([]*utils.TenantID, len(rs)) for i, r := range rs { - ids[i] = r.ID + tntIDs[i] = &utils.TenantID{r.Tenant, r.ID} } - return + return tntIDs +} + +func (rs Resources) tenatIDsStr() []string { + ids := make([]string, len(rs)) + for i, r := range rs { + ids[i] = r.TenantID() + } + return ids } // AllocateResource attempts allocating resources for a *ResourceUsage @@ -203,7 +216,7 @@ func (rs Resources) AllocateResource(ru *ResourceUsage, dryRun bool) (alcMessage if len(rs) == 0 { return "", utils.ErrResourceUnavailable } - lockIDs := utils.PrefixSliceItems(rs.ids(), utils.ResourcesPrefix) + lockIDs := utils.PrefixSliceItems(rs.tenatIDsStr(), utils.ResourcesPrefix) guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) // Simulate resource usage @@ -234,7 +247,7 @@ func NewResourceService(dataDB DataDB, storeInterval time.Duration, statS = nil } return &ResourceService{dataDB: dataDB, statS: statS, - lcEventResources: make(map[string][]string), + lcEventResources: make(map[string][]*utils.TenantID), storedResources: make(utils.StringMap), storeInterval: storeInterval, stopBackup: make(chan struct{})}, nil } @@ -243,7 +256,7 @@ func NewResourceService(dataDB DataDB, storeInterval time.Duration, type ResourceService struct { dataDB DataDB // So we can load the data in cache and index it statS rpcclient.RpcClientConnection // allows applying filters based on stats - lcEventResources map[string][]string // cache recording resources for events in alocation phase + lcEventResources map[string][]*utils.TenantID // cache recording resources for events in alocation phase lcERMux sync.RWMutex // protects the lcEventResources storedResources utils.StringMap // keep a record of resources which need saving, map[resID]bool srMux sync.RWMutex // protects storedResources @@ -340,7 +353,7 @@ func (rS *ResourceService) cachedResourcesForEvent(evUUID string) (rs Resources) if rIDsIf, has := cache.Get(utils.EventResourcesPrefix + evUUID); !has { return nil } else if rIDsIf != nil { - rIDs = rIDsIf.([]string) + rIDs = rIDsIf.([]*utils.TenantID) } shortCached = true } @@ -348,11 +361,14 @@ func (rS *ResourceService) cachedResourcesForEvent(evUUID string) (rs Resources) if len(rIDs) == 0 { return } - lockIDs := utils.PrefixSliceItems(rIDs, utils.ResourcesPrefix) + lockIDs := make([]string, len(rIDs)) + for i, rTid := range rIDs { + lockIDs[i] = utils.ResourcesPrefix + rTid.TenantID() + } guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) - for i, rID := range rIDs { - if r, err := rS.dataDB.GetResource(rID, false, ""); err != nil { + for i, rTid := range rIDs { + if r, err := rS.dataDB.GetResource(rTid.Tenant, rTid.ID, false, ""); err != nil { utils.Logger.Warning( fmt.Sprintf(" force-uncaching resources for evUUID: <%s>, error: <%s>", evUUID, err.Error())) @@ -383,7 +399,8 @@ func (rS *ResourceService) matchingResourcesForEvent(ev map[string]interface{}) guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) for resName := range rIDs { - rPrf, err := rS.dataDB.GetResourceProfile(resName, false, utils.NonTransactional) + rTntID := utils.NewTenantID(resName) + rPrf, err := rS.dataDB.GetResourceProfile(rTntID.Tenant, rTntID.ID, false, utils.NonTransactional) if err != nil { if err == utils.ErrNotFound { continue @@ -406,7 +423,7 @@ func (rS *ResourceService) matchingResourcesForEvent(ev map[string]interface{}) if !passAllFilters { continue } - r, err := rS.dataDB.GetResource(rPrf.ID, false, "") + r, err := rS.dataDB.GetResource(rPrf.Tenant, rPrf.ID, false, "") if err != nil { return nil, err } @@ -455,7 +472,7 @@ func (rS *ResourceService) V1AllowUsage(args utils.AttrRLsResourceUsage, allow * if mtcRLs, err = rS.matchingResourcesForEvent(args.Event); err != nil { return err } - cache.Set(utils.EventResourcesPrefix+args.UsageID, mtcRLs.ids(), true, "") + cache.Set(utils.EventResourcesPrefix+args.UsageID, mtcRLs.tenantIDs(), true, "") } if _, err = mtcRLs.AllocateResource( &ResourceUsage{ID: args.UsageID, @@ -498,7 +515,7 @@ func (rS *ResourceService) V1AllocateResource(args utils.AttrRLsResourceUsage, r } if wasShortCached || !wasCached { rS.lcERMux.Lock() - rS.lcEventResources[args.UsageID] = mtcRLs.ids() + rS.lcEventResources[args.UsageID] = mtcRLs.tenantIDs() rS.lcERMux.Unlock() } // index it for storing diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 9b4c4cb92..dc199ccc8 100755 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -99,12 +99,12 @@ type DataDB interface { RemoveAlias(string, string) error SetReverseAlias(*Alias, string) error GetReverseAlias(string, bool, string) ([]string, error) - GetResourceProfile(string, bool, string) (*ResourceProfile, error) + GetResourceProfile(string, string, bool, string) (*ResourceProfile, error) SetResourceProfile(*ResourceProfile, string) error - RemoveResourceProfile(string, string) error - GetResource(string, bool, string) (*Resource, error) + RemoveResourceProfile(string, string, string) error + GetResource(string, string, bool, string) (*Resource, error) SetResource(*Resource) error - RemoveResource(string, string) error + RemoveResource(string, string, string) error GetTiming(string, bool, string) (*utils.TPTiming, error) SetTiming(*utils.TPTiming, string) error RemoveTiming(string, string) error diff --git a/engine/storage_map.go b/engine/storage_map.go index bc7d7780b..fd7359a56 100755 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -193,6 +193,7 @@ func (ms *MapStorage) PreloadCacheForPrefix(prefix string) error { // CacheDataFromDB loads data to cache, // prefix represents the cache prefix, IDs should be nil if all available data should be loaded +// ToDo: convert IDs into []*utils.TenantIDs when infrastructure will be ready func (ms *MapStorage) CacheDataFromDB(prefix string, IDs []string, mustBeCached bool) (err error) { if !utils.IsSliceMember([]string{utils.DESTINATION_PREFIX, utils.REVERSE_DESTINATION_PREFIX, @@ -273,9 +274,11 @@ func (ms *MapStorage) CacheDataFromDB(prefix string, IDs []string, mustBeCached case utils.REVERSE_ALIASES_PREFIX: _, err = ms.GetReverseAlias(dataID, true, utils.NonTransactional) case utils.ResourceProfilesPrefix: - _, err = ms.GetResourceProfile(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = ms.GetResourceProfile(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.ResourcesPrefix: - _, err = ms.GetResource(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = ms.GetResource(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.TimingsPrefix: _, err = ms.GetTiming(dataID, true, utils.NonTransactional) } @@ -1276,10 +1279,10 @@ func (ms *MapStorage) GetSMCost(cgrid, source, runid, originHost, originID strin return } -func (ms *MapStorage) GetResourceProfile(id string, skipCache bool, transactionID string) (rsp *ResourceProfile, err error) { +func (ms *MapStorage) GetResourceProfile(tenant, id string, skipCache bool, transactionID string) (rsp *ResourceProfile, err error) { ms.mu.RLock() defer ms.mu.RUnlock() - key := utils.ResourceProfilesPrefix + id + key := utils.ResourceProfilesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x != nil { @@ -1313,23 +1316,23 @@ func (ms *MapStorage) SetResourceProfile(r *ResourceProfile, transactionID strin if err != nil { return err } - ms.dict[utils.ResourceProfilesPrefix+r.ID] = result + ms.dict[utils.ResourceProfilesPrefix+r.TenantID()] = result return nil } -func (ms *MapStorage) RemoveResourceProfile(id string, transactionID string) error { +func (ms *MapStorage) RemoveResourceProfile(tenant, id string, transactionID string) error { ms.mu.Lock() defer ms.mu.Unlock() - key := utils.ResourceProfilesPrefix + id + key := utils.ResourceProfilesPrefix + utils.ConcatenatedKey(tenant, id) delete(ms.dict, key) cache.RemKey(key, cacheCommit(transactionID), transactionID) return nil } -func (ms *MapStorage) GetResource(id string, skipCache bool, transactionID string) (r *Resource, err error) { +func (ms *MapStorage) GetResource(tenant, id string, skipCache bool, transactionID string) (r *Resource, err error) { ms.mu.RLock() defer ms.mu.RUnlock() - key := utils.ResourcesPrefix + id + key := utils.ResourcesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x != nil { @@ -1358,14 +1361,14 @@ func (ms *MapStorage) SetResource(r *Resource) (err error) { if err != nil { return err } - ms.dict[utils.ResourcesPrefix+r.ID] = result + ms.dict[utils.ResourcesPrefix+r.TenantID()] = result return } -func (ms *MapStorage) RemoveResource(id string, transactionID string) (err error) { +func (ms *MapStorage) RemoveResource(tenant, id string, transactionID string) (err error) { ms.mu.Lock() defer ms.mu.Unlock() - key := utils.ResourcesPrefix + id + key := utils.ResourcesPrefix + utils.ConcatenatedKey(tenant, id) delete(ms.dict, key) cache.RemKey(key, cacheCommit(transactionID), transactionID) return diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 66f6fb984..11cd4448b 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -526,9 +526,11 @@ func (ms *MongoStorage) CacheDataFromDB(prfx string, ids []string, mustBeCached case utils.REVERSE_ALIASES_PREFIX: _, err = ms.GetReverseAlias(dataID, true, utils.NonTransactional) case utils.ResourceProfilesPrefix: - _, err = ms.GetResourceProfile(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = ms.GetResourceProfile(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.ResourcesPrefix: - _, err = ms.GetResource(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = ms.GetResource(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.TimingsPrefix: _, err = ms.GetTiming(dataID, true, utils.NonTransactional) } @@ -1830,8 +1832,8 @@ func (ms *MongoStorage) GetAllCdrStats() (css []*CdrStats, err error) { return } -func (ms *MongoStorage) GetResourceProfile(id string, skipCache bool, transactionID string) (rp *ResourceProfile, err error) { - key := utils.ResourceProfilesPrefix + id +func (ms *MongoStorage) GetResourceProfile(tenant, id string, skipCache bool, transactionID string) (rp *ResourceProfile, err error) { + key := utils.ResourceProfilesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x == nil { @@ -1843,7 +1845,7 @@ func (ms *MongoStorage) GetResourceProfile(id string, skipCache bool, transactio session, col := ms.conn(colRsP) defer session.Close() rp = new(ResourceProfile) - if err = col.Find(bson.M{"id": id}).One(rp); err != nil { + if err = col.Find(bson.M{"tenant": tenant, "id": id}).One(rp); err != nil { if err == mgo.ErrNotFound { err = utils.ErrNotFound cache.Set(key, nil, cacheCommit(transactionID), transactionID) @@ -1862,22 +1864,23 @@ func (ms *MongoStorage) GetResourceProfile(id string, skipCache bool, transactio func (ms *MongoStorage) SetResourceProfile(rp *ResourceProfile, transactionID string) (err error) { session, col := ms.conn(colRsP) defer session.Close() - _, err = col.Upsert(bson.M{"id": rp.ID}, rp) + _, err = col.Upsert(bson.M{"tenant": rp.Tenant, "id": rp.ID}, rp) return } -func (ms *MongoStorage) RemoveResourceProfile(id string, transactionID string) (err error) { +func (ms *MongoStorage) RemoveResourceProfile(tenant, id string, transactionID string) (err error) { session, col := ms.conn(colRsP) defer session.Close() - if err = col.Remove(bson.M{"id": id}); err != nil { + if err = col.Remove(bson.M{"tenant": tenant, "id": id}); err != nil { return } - cache.RemKey(utils.ResourceProfilesPrefix+id, cacheCommit(transactionID), transactionID) + cache.RemKey(utils.ResourceProfilesPrefix+utils.ConcatenatedKey(tenant, id), + cacheCommit(transactionID), transactionID) return nil } -func (ms *MongoStorage) GetResource(id string, skipCache bool, transactionID string) (r *Resource, err error) { - key := utils.ResourcesPrefix + id +func (ms *MongoStorage) GetResource(tenant, id string, skipCache bool, transactionID string) (r *Resource, err error) { + key := utils.ResourcesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x == nil { @@ -1889,7 +1892,7 @@ func (ms *MongoStorage) GetResource(id string, skipCache bool, transactionID str session, col := ms.conn(colRes) defer session.Close() r = new(Resource) - if err = col.Find(bson.M{"id": id}).One(r); err != nil { + if err = col.Find(bson.M{"tenant": tenant, "id": id}).One(r); err != nil { if err == mgo.ErrNotFound { err = utils.ErrNotFound cache.Set(key, nil, cacheCommit(transactionID), transactionID) @@ -1903,17 +1906,18 @@ func (ms *MongoStorage) GetResource(id string, skipCache bool, transactionID str func (ms *MongoStorage) SetResource(r *Resource) (err error) { session, col := ms.conn(colRes) defer session.Close() - _, err = col.Upsert(bson.M{"id": r.ID}, r) + _, err = col.Upsert(bson.M{"tenant": r.Tenant, "id": r.ID}, r) return } -func (ms *MongoStorage) RemoveResource(id string, transactionID string) (err error) { +func (ms *MongoStorage) RemoveResource(tenant, id string, transactionID string) (err error) { session, col := ms.conn(colRes) defer session.Close() - if err = col.Remove(bson.M{"id": id}); err != nil { + if err = col.Remove(bson.M{"tenant": tenant, "id": id}); err != nil { return } - cache.RemKey(utils.ResourcesPrefix+id, cacheCommit(transactionID), transactionID) + cache.RemKey(utils.ResourcesPrefix+utils.ConcatenatedKey(tenant, id), + cacheCommit(transactionID), transactionID) return nil } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index db1ae044d..0b50bb8b2 100755 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -288,9 +288,11 @@ func (rs *RedisStorage) CacheDataFromDB(prfx string, ids []string, mustBeCached case utils.REVERSE_ALIASES_PREFIX: _, err = rs.GetReverseAlias(dataID, true, utils.NonTransactional) case utils.ResourceProfilesPrefix: - _, err = rs.GetResourceProfile(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = rs.GetResourceProfile(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.ResourcesPrefix: - _, err = rs.GetResource(dataID, true, utils.NonTransactional) + tntID := utils.NewTenantID(dataID) + _, err = rs.GetResource(tntID.Tenant, tntID.ID, true, utils.NonTransactional) case utils.TimingsPrefix: _, err = rs.GetTiming(dataID, true, utils.NonTransactional) } @@ -1350,8 +1352,8 @@ func (rs *RedisStorage) GetAllCdrStats() (css []*CdrStats, err error) { return } -func (rs *RedisStorage) GetResourceProfile(id string, skipCache bool, transactionID string) (rsp *ResourceProfile, err error) { - key := utils.ResourceProfilesPrefix + id +func (rs *RedisStorage) GetResourceProfile(tenant, id string, skipCache bool, transactionID string) (rsp *ResourceProfile, err error) { + key := utils.ResourceProfilesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x == nil { @@ -1385,11 +1387,11 @@ func (rs *RedisStorage) SetResourceProfile(rsp *ResourceProfile, transactionID s if err != nil { return err } - return rs.Cmd("SET", utils.ResourceProfilesPrefix+rsp.ID, result).Err + return rs.Cmd("SET", utils.ResourceProfilesPrefix+rsp.TenantID(), result).Err } -func (rs *RedisStorage) RemoveResourceProfile(id string, transactionID string) (err error) { - key := utils.ResourceProfilesPrefix + id +func (rs *RedisStorage) RemoveResourceProfile(tenant, id string, transactionID string) (err error) { + key := utils.ResourceProfilesPrefix + utils.ConcatenatedKey(tenant, id) if err = rs.Cmd("DEL", key).Err; err != nil { return } @@ -1397,8 +1399,8 @@ func (rs *RedisStorage) RemoveResourceProfile(id string, transactionID string) ( return } -func (rs *RedisStorage) GetResource(id string, skipCache bool, transactionID string) (r *Resource, err error) { - key := utils.ResourcesPrefix + id +func (rs *RedisStorage) GetResource(tenant, id string, skipCache bool, transactionID string) (r *Resource, err error) { + key := utils.ResourcesPrefix + utils.ConcatenatedKey(tenant, id) if !skipCache { if x, ok := cache.Get(key); ok { if x == nil { @@ -1427,11 +1429,11 @@ func (rs *RedisStorage) SetResource(r *Resource) (err error) { if err != nil { return err } - return rs.Cmd("SET", utils.ResourcesPrefix+r.ID, result).Err + return rs.Cmd("SET", utils.ResourcesPrefix+r.TenantID(), result).Err } -func (rs *RedisStorage) RemoveResource(id string, transactionID string) (err error) { - key := utils.ResourcesPrefix + id +func (rs *RedisStorage) RemoveResource(tenant, id string, transactionID string) (err error) { + key := utils.ResourcesPrefix + utils.ConcatenatedKey(tenant, id) if err = rs.Cmd("DEL", key).Err; err != nil { return } From bdafd9913742bf88a65dda0043e4b963fa060fa9 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 15 Sep 2017 14:54:10 +0200 Subject: [PATCH 08/20] Storage mongo returning TenantID from getKeysForPrefix for resource and resource profiles --- engine/loader_it_test.go | 24 ++++++++++--------- engine/onstor_it_test.go | 43 ++++++++++++++++++---------------- engine/storage_mongo_datadb.go | 10 ++++---- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/engine/loader_it_test.go b/engine/loader_it_test.go index 476480f2c..8528ac7a7 100755 --- a/engine/loader_it_test.go +++ b/engine/loader_it_test.go @@ -309,17 +309,19 @@ func TestLoaderITWriteToDatabase(t *testing.T) { } } - for k, rl := range loader.resProfiles { - rcv, err := loader.dataStorage.GetResourceProfile(k, true, utils.NonTransactional) - if err != nil { - t.Error("Failed GetResourceProfile: ", err.Error()) - } - rlT, err := APItoResource(rl, "UTC") - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(rlT, rcv) { - t.Errorf("Expecting: %v, received: %v", rlT, rcv) + for _, mapIDs := range loader.resProfiles { + for _, rl := range mapIDs { + rcv, err := loader.dataStorage.GetResourceProfile(rl.Tenant, rl.ID, true, utils.NonTransactional) + if err != nil { + t.Error("Failed GetResourceProfile: ", err.Error()) + } + rlT, err := APItoResource(rl, "UTC") + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(rlT, rcv) { + t.Errorf("Expecting: %v, received: %v", rlT, rcv) + } } } diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index f5b4709a1..b267baa22 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -782,6 +782,7 @@ func testOnStorITCacheReverseAlias(t *testing.T) { func testOnStorITCacheResourceProfile(t *testing.T) { rCfg := &ResourceProfile{ + Tenant: "cgrates.org", ID: "RL_TEST", Weight: 10, Filters: []*RequestFilter{ @@ -799,19 +800,19 @@ func testOnStorITCacheResourceProfile(t *testing.T) { if err := onStor.SetResourceProfile(rCfg, utils.NonTransactional); err != nil { t.Error(err) } - expectedR := []string{"rsp_RL_TEST"} + expectedR := []string{"rsp_cgrates.org:RL_TEST"} if itm, err := onStor.GetKeysForPrefix(utils.ResourceProfilesPrefix); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedR, itm) { t.Errorf("Expected : %+v, but received %+v", expectedR, itm) } - if _, hasIt := cache.Get(utils.ResourceProfilesPrefix + rCfg.ID); hasIt { + if _, hasIt := cache.Get(utils.ResourceProfilesPrefix + rCfg.TenantID()); hasIt { t.Error("Already in cache") } - if err := onStor.CacheDataFromDB(utils.ResourceProfilesPrefix, []string{rCfg.ID}, false); err != nil { + if err := onStor.CacheDataFromDB(utils.ResourceProfilesPrefix, []string{rCfg.TenantID()}, false); err != nil { t.Error(err) } - if itm, hasIt := cache.Get(utils.ResourceProfilesPrefix + rCfg.ID); !hasIt { + if itm, hasIt := cache.Get(utils.ResourceProfilesPrefix + rCfg.TenantID()); !hasIt { t.Error("Did not cache") } else if rcv := itm.(*ResourceProfile); !reflect.DeepEqual(rCfg, rcv) { t.Errorf("Expecting: %+v, received: %+v", rCfg, rcv) @@ -853,7 +854,8 @@ func testOnStorITCacheTiming(t *testing.T) { func testOnStorITCacheResource(t *testing.T) { res := &Resource{ - ID: "RL1", + Tenant: "cgrates.org", + ID: "RL1", Usages: map[string]*ResourceUsage{ "RU1": &ResourceUsage{ ID: "RU1", @@ -866,20 +868,20 @@ func testOnStorITCacheResource(t *testing.T) { if err := onStor.SetResource(res); err != nil { t.Error(err) } - expectedT := []string{"res_RL1"} + expectedT := []string{"res_cgrates.org:RL1"} if itm, err := onStor.GetKeysForPrefix(utils.ResourcesPrefix); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedT, itm) { t.Errorf("Expected : %+v, but received %+v", expectedT, itm) } - if _, hasIt := cache.Get(utils.ResourcesPrefix + res.ID); hasIt { + if _, hasIt := cache.Get(utils.ResourcesPrefix + res.TenantID()); hasIt { t.Error("Already in cache") } - if err := onStor.CacheDataFromDB(utils.ResourcesPrefix, []string{res.ID}, false); err != nil { + if err := onStor.CacheDataFromDB(utils.ResourcesPrefix, []string{res.TenantID()}, false); err != nil { t.Error(err) } - if itm, hasIt := cache.Get(utils.ResourcesPrefix + res.ID); !hasIt { + if itm, hasIt := cache.Get(utils.ResourcesPrefix + res.TenantID()); !hasIt { t.Error("Did not cache") } else if rcv := itm.(*Resource); !reflect.DeepEqual(res, rcv) { t.Errorf("Expecting: %+v, received: %+v", res, rcv) @@ -1798,13 +1800,13 @@ func testOnStorITCRUDResourceProfile(t *testing.T) { Thresholds: []string{"TEST_ACTIONS"}, UsageTTL: time.Duration(1 * time.Millisecond), } - if _, rcvErr := onStor.GetResourceProfile(rL.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { + if _, rcvErr := onStor.GetResourceProfile(rL.Tenant, rL.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { t.Error(rcvErr) } if err := onStor.SetResourceProfile(rL, utils.NonTransactional); err != nil { t.Error(err) } - if rcv, err := onStor.GetResourceProfile(rL.ID, true, utils.NonTransactional); err != nil { + if rcv, err := onStor.GetResourceProfile(rL.Tenant, rL.ID, true, utils.NonTransactional); err != nil { t.Error(err) } else if !reflect.DeepEqual(rL, rcv) { t.Errorf("Expecting: %v, received: %v", rL, rcv) @@ -1817,7 +1819,7 @@ func testOnStorITCRUDResourceProfile(t *testing.T) { // t.Error(rcvErr) // } // - if rcv, err := onStor.GetResourceProfile(rL.ID, false, utils.NonTransactional); err != nil { + if rcv, err := onStor.GetResourceProfile(rL.Tenant, rL.ID, false, utils.NonTransactional); err != nil { t.Error(err) } else if !reflect.DeepEqual(rL, rcv) { t.Errorf("Expecting: %v, received: %v", rL, rcv) @@ -1825,17 +1827,18 @@ func testOnStorITCRUDResourceProfile(t *testing.T) { // if err = onStor.SelectDatabase(onStorCfg); err != nil { // t.Error(err) // } - if err := onStor.RemoveResourceProfile(rL.ID, utils.NonTransactional); err != nil { + if err := onStor.RemoveResourceProfile(rL.Tenant, rL.ID, utils.NonTransactional); err != nil { t.Error(err) } - if _, rcvErr := onStor.GetResourceProfile(rL.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { + if _, rcvErr := onStor.GetResourceProfile(rL.Tenant, rL.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { t.Error(rcvErr) } } func testOnStorITCRUDResource(t *testing.T) { res := &Resource{ - ID: "RL1", + Tenant: "cgrates.org", + ID: "RL1", Usages: map[string]*ResourceUsage{ "RU1": &ResourceUsage{ ID: "RU1", @@ -1845,26 +1848,26 @@ func testOnStorITCRUDResource(t *testing.T) { }, TTLIdx: []string{"RU1"}, } - if _, rcvErr := onStor.GetResource("RL1", true, utils.NonTransactional); rcvErr != nil && rcvErr != utils.ErrNotFound { + if _, rcvErr := onStor.GetResource("cgrates.org", "RL1", true, utils.NonTransactional); rcvErr != nil && rcvErr != utils.ErrNotFound { t.Error(rcvErr) } if err := onStor.SetResource(res); err != nil { t.Error(err) } - if rcv, err := onStor.GetResource("RL1", true, utils.NonTransactional); err != nil { + if rcv, err := onStor.GetResource("cgrates.org", "RL1", true, utils.NonTransactional); err != nil { t.Error(err) } else if !(reflect.DeepEqual(res, rcv)) { t.Errorf("Expecting: %v, received: %v", res, rcv) } - if rcv, err := onStor.GetResource("RL1", false, utils.NonTransactional); err != nil { + if rcv, err := onStor.GetResource("cgrates.org", "RL1", false, utils.NonTransactional); err != nil { t.Error(err) } else if !reflect.DeepEqual(res, rcv) { t.Errorf("Expecting: %v, received: %v", res, rcv) } - if err := onStor.RemoveResource(res.ID, utils.NonTransactional); err != nil { + if err := onStor.RemoveResource(res.Tenant, res.ID, utils.NonTransactional); err != nil { t.Error(err) } - if _, rcvErr := onStor.GetResource(res.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { + if _, rcvErr := onStor.GetResource(res.Tenant, res.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { t.Error(rcvErr) } } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 11cd4448b..95476f443 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -556,7 +556,7 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er defer session.Close() db := session.DB(ms.db) keyResult := struct{ Key string }{} - idResult := struct{ Id string }{} + idResult := struct{ Tenant, Id string }{} switch category { case utils.DESTINATION_PREFIX: iter := db.C(colDst).Find(bson.M{"key": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"key": 1}).Iter() @@ -624,14 +624,14 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (result []string, err er result = append(result, utils.REVERSE_ALIASES_PREFIX+keyResult.Key) } case utils.ResourceProfilesPrefix: - iter := db.C(colRsP).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"id": 1}).Iter() + iter := db.C(colRsP).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"tenant": 1, "id": 1}).Iter() for iter.Next(&idResult) { - result = append(result, utils.ResourceProfilesPrefix+idResult.Id) + result = append(result, utils.ResourceProfilesPrefix+utils.ConcatenatedKey(idResult.Tenant, idResult.Id)) } case utils.ResourcesPrefix: - iter := db.C(colRes).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"id": 1}).Iter() + iter := db.C(colRes).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"tenant": 1, "id": 1}).Iter() for iter.Next(&idResult) { - result = append(result, utils.ResourcesPrefix+idResult.Id) + result = append(result, utils.ResourcesPrefix+utils.ConcatenatedKey(idResult.Tenant, idResult.Id)) } case utils.StatQueuePrefix: iter := db.C(colStq).Find(bson.M{"id": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"id": 1}).Iter() From 0f288af1704094e7a566602f79bb3d20a6953e06 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 16:21:32 +0300 Subject: [PATCH 09/20] Add a new fild in models TPResource ID and replace primary key with PK and set tenant after ID --- apier/v1/tpresources.go | 4 +-- apier/v1/tpresources_it_test.go | 28 +++++++++---------- .../mysql/create_tariffplan_tables.sql | 13 ++++----- .../postgres/create_tariffplan_tables.sql | 11 +++----- data/tariffplans/testtp/Resources.csv | 12 ++++---- engine/loader_csv_test.go | 14 +++++----- engine/model_helpers.go | 14 ++++++---- engine/model_helpers_test.go | 16 +++++------ engine/models.go | 6 ++-- engine/storage_sql.go | 4 +-- utils/apitpdata.go | 4 +-- 11 files changed, 61 insertions(+), 65 deletions(-) diff --git a/apier/v1/tpresources.go b/apier/v1/tpresources.go index 830a5c838..5b9ddd0ec 100644 --- a/apier/v1/tpresources.go +++ b/apier/v1/tpresources.go @@ -65,7 +65,7 @@ func (self *ApierV1) GetTPResourceIDs(attrs AttrGetTPResourceIds, reply *[]strin if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPResources, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil { + if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPResources, utils.TPDistinctIds{"id"}, nil, &attrs.Paginator); err != nil { return utils.NewErrServerError(err) } else if ids == nil { return utils.ErrNotFound @@ -80,7 +80,7 @@ func (self *ApierV1) RemTPResource(attrs AttrGetTPResource, reply *string) error if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if err := self.StorDb.RemTpData(utils.TBLTPResources, attrs.TPid, map[string]string{"tag": attrs.ID}); err != nil { + if err := self.StorDb.RemTpData(utils.TBLTPResources, attrs.TPid, map[string]string{"id": attrs.ID}); err != nil { return utils.NewErrServerError(err) } else { *reply = utils.OK diff --git a/apier/v1/tpresources_it_test.go b/apier/v1/tpresources_it_test.go index a49521dce..d0bc02552 100644 --- a/apier/v1/tpresources_it_test.go +++ b/apier/v1/tpresources_it_test.go @@ -44,7 +44,7 @@ var ( var sTestsTPResources = []func(t *testing.T){ testTPResInitCfg, testTPResResetStorDb, - testTPResStartEngine, + //testTPResStartEngine, testTPResRpcConn, testTPResGetTPResourceBeforeSet, testTPResSetTPResource, @@ -120,16 +120,16 @@ func testTPResRpcConn(t *testing.T) { func testTPResGetTPResourceBeforeSet(t *testing.T) { var reply *utils.TPResource - if err := tpResRPC.Call("ApierV1.GetTPResource", AttrGetTPResource{TPid: "TPR1", ID: "Res"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := tpResRPC.Call("ApierV1.GetTPResource", AttrGetTPResource{TPid: "TPR1", ID: "ResGroup1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } func testTPResSetTPResource(t *testing.T) { tpRes = &utils.TPResource{ - Tenant:"Tester", - TPid: "TPR1", - ID: "Res", + Tenant: "cgrates.org", + TPid: "TPR1", + ID: "ResGroup1", Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{ Type: "*string", @@ -141,11 +141,11 @@ func testTPResSetTPResource(t *testing.T) { ActivationTime: "2014-07-29T15:00:00Z", ExpiryTime: "", }, - UsageTTL: "1", - Limit: "1", - AllocationMessage: "Message", - Blocker: false, - Stored: false, + UsageTTL: "1s", + Limit: "7", + AllocationMessage: "", + Blocker: true, + Stored: true, Weight: 20, Thresholds: []string{"ValOne", "ValTwo"}, } @@ -159,7 +159,7 @@ func testTPResSetTPResource(t *testing.T) { func testTPResGetTPResourceAfterSet(t *testing.T) { var respond *utils.TPResource - if err := tpResRPC.Call("ApierV1.GetTPResource", &AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &respond); err != nil { + if err := tpResRPC.Call("ApierV1.GetTPResource", AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &respond); err != nil { t.Error(err) } else if !reflect.DeepEqual(tpRes, respond) { t.Errorf("Expecting : %+v, received: %+v", tpRes, respond) @@ -189,7 +189,7 @@ func testTPResUpdateTPResource(t *testing.T) { func testTPResGetTPResourceAfterUpdate(t *testing.T) { var expectedTPR *utils.TPResource - if err := tpResRPC.Call("ApierV1.GetTPResource", &AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &expectedTPR); err != nil { + if err := tpResRPC.Call("ApierV1.GetTPResource", AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &expectedTPR); err != nil { t.Error(err) } else if !reflect.DeepEqual(tpRes, expectedTPR) { t.Errorf("Expecting: %+v, received: %+v", tpRes, expectedTPR) @@ -198,7 +198,7 @@ func testTPResGetTPResourceAfterUpdate(t *testing.T) { func testTPResRemTPResource(t *testing.T) { var resp string - if err := tpResRPC.Call("ApierV1.RemTPResource", &AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &resp); err != nil { + if err := tpResRPC.Call("ApierV1.RemTPResource", AttrGetTPResource{TPid: tpRes.TPid, ID: tpRes.ID}, &resp); err != nil { t.Error(err) } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) @@ -207,7 +207,7 @@ func testTPResRemTPResource(t *testing.T) { func testTPResGetTPResourceAfterRemove(t *testing.T) { var respond *utils.TPResource - if err := tpResRPC.Call("ApierV1.GetTPResource", &AttrGetTPStat{TPid: "TPS1", ID: "Stat1"}, &respond); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := tpResRPC.Call("ApierV1.GetTPResource", AttrGetTPStat{TPid: "TPS1", ID: "ResGroup1"}, &respond); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index e62bd1146..3e2fe6fa3 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -397,10 +397,10 @@ CREATE TABLE tp_aliases ( DROP TABLE IF EXISTS tp_resources; CREATE TABLE tp_resources ( - `tenant` varchar(64) NOT NULL, - `id` int(11) NOT NULL AUTO_INCREMENT, + `pk` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, - `tag` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, `filter_field_values` varchar(256) NOT NULL, @@ -413,9 +413,9 @@ CREATE TABLE tp_resources ( `weight` decimal(8,2) NOT NULL, `thresholds` varchar(64) NOT NULL, `created_at` TIMESTAMP, - PRIMARY KEY (`id`), + PRIMARY KEY (`pk`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_resource` (`tpid`, `tag`, `filter_type`, `filter_field_name`) + UNIQUE KEY `unique_tp_resource` (`tpid`, `id`, `filter_type`, `filter_field_name`) ); -- @@ -485,6 +485,3 @@ CREATE TABLE versions ( PRIMARY KEY (`id`), UNIQUE KEY `item` (`item`) ); - - - diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index c0772103c..d065f2ca9 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -393,10 +393,10 @@ CREATE INDEX tpaliases_idx ON tp_aliases (tpid,direction,tenant,category,account DROP TABLE IF EXISTS tp_resources; CREATE TABLE tp_resources ( - "tenant"varchar(64) NOT NULL, - "id" SERIAL PRIMARY KEY, + "pk" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, - "tag" varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, + "tenant"varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, "filter_field_values" varchar(256) NOT NULL, @@ -411,7 +411,7 @@ CREATE TABLE tp_resources ( "created_at" TIMESTAMP WITH TIME ZONE ); CREATE INDEX tp_resources_idx ON tp_resources (tpid); -CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "tag", "filter_type", "filter_field_name"); +CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "id", "filter_type", "filter_field_name"); -- @@ -479,6 +479,3 @@ CREATE TABLE versions ( "version" INTEGER NOT NULL, UNIQUE (item) ); - - - diff --git a/data/tariffplans/testtp/Resources.csv b/data/tariffplans/testtp/Resources.csv index 6597b961d..0077b1c66 100755 --- a/data/tariffplans/testtp/Resources.csv +++ b/data/tariffplans/testtp/Resources.csv @@ -1,6 +1,6 @@ -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -Tester,ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, -Tester,ResGroup1,*string_prefix,Destination,10;20,,,,,,,, -Tester,ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, -Tester,ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, -Tester,ResGroup3,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, +#Id[0],Tenant[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] +ResGroup1,cgrates.org,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, +ResGroup1,cgrates.org,*string_prefix,Destination,10;20,,,,,,,, +ResGroup1,cgrates.org,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, +ResGroup2,cgrates.org,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, +ResGroup3,cgrates.org,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0ff3b6eee..4b4a8f5fb 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -266,11 +266,11 @@ cgrates.org,mas,true,another,value,10 *out,cgrates.org,call,remo,remo,*any,*rating,Account,remo,minu,10 ` resProfiles = ` -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -Tester,ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, -Tester,ResGroup21,*string_prefix,HdrDestination,10;20,,,,,,,, -Tester,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, -Tester,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, +#Id[0]Tenant[1],,FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] +ResGroup21,cgrates.org,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, +ResGroup21,cgrates.org,*string_prefix,HdrDestination,10;20,,,,,,,, +ResGroup21,cgrates.org,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, +ResGroup22,cgrates.org,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, ` stats = ` #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] @@ -1393,7 +1393,7 @@ func TestLoadResourceProfiles(t *testing.T) { eResProfiles := map[string]*utils.TPResource{ "ResGroup21": &utils.TPResource{ TPid: testTPID, - Tenant: "Tester", + Tenant: "cgrates.org", ID: "ResGroup21", Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{Type: MetaString, FieldName: "HdrAccount", Values: []string{"1001", "1002"}}, @@ -1410,7 +1410,7 @@ func TestLoadResourceProfiles(t *testing.T) { }, "ResGroup22": &utils.TPResource{ TPid: testTPID, - Tenant: "Tester", + Tenant: "cgrates.org", ID: "ResGroup22", Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{Type: MetaDestinations, FieldName: "HdrDestination", Values: []string{"DST_FS"}}, diff --git a/engine/model_helpers.go b/engine/model_helpers.go index c34ea2175..05a4916bd 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1817,12 +1817,12 @@ type TpResources []*TpResource func (tps TpResources) AsTPResources() (result []*utils.TPResource) { mrl := make(map[string]*utils.TPResource) for _, tp := range tps { - rl, found := mrl[tp.Tag] + rl, found := mrl[tp.ID] if !found { rl = &utils.TPResource{ TPid: tp.Tpid, Tenant: tp.Tenant, - ID: tp.Tag, + ID: tp.ID, Blocker: tp.Blocker, Stored: tp.Stored, } @@ -1863,7 +1863,7 @@ func (tps TpResources) AsTPResources() (result []*utils.TPResource) { FieldName: tp.FilterFieldName, Values: strings.Split(tp.FilterFieldValues, utils.INFIELD_SEP)}) } - mrl[tp.Tag] = rl + mrl[tp.ID] = rl } result = make([]*utils.TPResource, len(mrl)) i := 0 @@ -1880,9 +1880,11 @@ func APItoModelResource(rl *utils.TPResource) (mdls TpResources) { } for i, fltr := range rl.Filters { mdl := &TpResource{ - Tpid: rl.TPid, - Tenant: rl.Tenant, - Tag: rl.ID, + Tpid: rl.TPid, + Tenant: rl.Tenant, + ID: rl.ID, + Blocker: rl.Blocker, + Stored: rl.Stored, } if i == 0 { mdl.UsageTTL = rl.UsageTTL diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index bad16444a..7a911e381 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -713,8 +713,8 @@ func TestTpResourcesAsTpResources(t *testing.T) { tps := []*TpResource{ &TpResource{ Tpid: "TEST_TPID", - Tenant: "Tester", - Tag: "ResGroup1", + Tenant: "cgrates.org", + ID: "ResGroup1", FilterType: MetaStringPrefix, FilterFieldName: "Destination", FilterFieldValues: "+49151;+49161", @@ -726,16 +726,16 @@ func TestTpResourcesAsTpResources(t *testing.T) { Thresholds: "WARN_RES1;WARN_RES2"}, &TpResource{ Tpid: "TEST_TPID", - Tag: "ResGroup1", - Tenant: "Tester", + ID: "ResGroup1", + Tenant: "cgrates.org", FilterType: MetaStringPrefix, FilterFieldName: "Category", FilterFieldValues: "call;inbound_call", Thresholds: "WARN3"}, &TpResource{ Tpid: "TEST_TPID", - Tenant: "Tester", - Tag: "ResGroup2", + Tenant: "cgrates.org", + ID: "ResGroup2", FilterType: MetaStringPrefix, FilterFieldName: "Destination", FilterFieldValues: "+40", @@ -749,7 +749,7 @@ func TestTpResourcesAsTpResources(t *testing.T) { &utils.TPResource{ TPid: tps[0].Tpid, Tenant: tps[0].Tenant, - ID: tps[0].Tag, + ID: tps[0].ID, Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{ Type: tps[0].FilterType, @@ -774,7 +774,7 @@ func TestTpResourcesAsTpResources(t *testing.T) { &utils.TPResource{ TPid: tps[2].Tpid, Tenant: tps[2].Tenant, - ID: tps[2].Tag, + ID: tps[2].ID, Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{ Type: tps[2].FilterType, diff --git a/engine/models.go b/engine/models.go index e769f00a9..76abdcabd 100755 --- a/engine/models.go +++ b/engine/models.go @@ -452,10 +452,10 @@ func (t TBLSMCosts) TableName() string { } type TpResource struct { - ID int64 + PK uint `gorm:"primary_key"` Tpid string - Tenant string `index:"0" re:""` - Tag string `index:"1" re:""` + ID string `index:"0" re:""` + Tenant string `index:"1" re:""` FilterType string `index:"2" re:"^\*[A-Za-z].*"` FilterFieldName string `index:"3" re:""` FilterFieldValues string `index:"4" re:""` diff --git a/engine/storage_sql.go b/engine/storage_sql.go index fdf8c4de5..80398a7f1 100755 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -565,7 +565,7 @@ func (self *SQLStorage) SetTPResources(rls []*utils.TPResource) error { tx := self.db.Begin() for _, rl := range rls { // Remove previous - if err := tx.Where(&TpResource{Tpid: rl.TPid, Tag: rl.ID}).Delete(TpResource{}).Error; err != nil { + if err := tx.Where(&TpResource{Tpid: rl.TPid, ID: rl.ID}).Delete(TpResource{}).Error; err != nil { tx.Rollback() return err } @@ -1542,7 +1542,7 @@ func (self *SQLStorage) GetTPResources(tpid, id string) ([]*utils.TPResource, er var rls TpResources q := self.db.Where("tpid = ?", tpid) if len(id) != 0 { - q = q.Where("tag = ?", id) + q = q.Where("id = ?", id) } if err := q.Find(&rls).Error; err != nil { return nil, err diff --git a/utils/apitpdata.go b/utils/apitpdata.go index db55e86b7..2e50b9bd0 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1270,7 +1270,7 @@ type AttrSetBalance struct { type TPResource struct { TPid string - Tenant string + Tenant string ID string // Identifier of this limit Filters []*TPRequestFilter // Filters for the request ActivationInterval *TPActivationInterval // Time when this limit becomes active/expires @@ -1336,7 +1336,7 @@ type AttrDisconnectSession struct { // TPStats is used in APIs to manage remotely offline Stats config type TPStats struct { TPid string - Tenant string + Tenant string ID string Filters []*TPRequestFilter ActivationInterval *TPActivationInterval From 37f1c66d1fc0ea5e49b5492dd7962892a81905f4 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 16:51:26 +0300 Subject: [PATCH 10/20] Fix Merge --- engine/loader_csv_test.go | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index fd82ed2ce..51ce99556 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -266,19 +266,11 @@ cgrates.org,mas,true,another,value,10 *out,cgrates.org,call,remo,remo,*any,*rating,Account,remo,minu,10 ` resProfiles = ` -<<<<<<< HEAD #Id[0]Tenant[1],,FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] ResGroup21,cgrates.org,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, ResGroup21,cgrates.org,*string_prefix,HdrDestination,10;20,,,,,,,, ResGroup21,cgrates.org,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, ResGroup22,cgrates.org,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, -======= -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -cgrates.org,ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, -cgrates.org,ResGroup21,*string_prefix,HdrDestination,10;20,,,,,,,, -cgrates.org,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, -cgrates.org,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, ->>>>>>> 72be574e2485078deacebbbd01c7df1d05991615 ` stats = ` #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] @@ -1398,32 +1390,6 @@ func TestLoadReverseAliases(t *testing.T) { } func TestLoadResourceProfiles(t *testing.T) { -<<<<<<< HEAD - eResProfiles := map[string]*utils.TPResource{ - "ResGroup21": &utils.TPResource{ - TPid: testTPID, - Tenant: "cgrates.org", - ID: "ResGroup21", - Filters: []*utils.TPRequestFilter{ - &utils.TPRequestFilter{Type: MetaString, FieldName: "HdrAccount", Values: []string{"1001", "1002"}}, - &utils.TPRequestFilter{Type: MetaStringPrefix, FieldName: "HdrDestination", Values: []string{"10", "20"}}, - &utils.TPRequestFilter{Type: MetaRSRFields, Values: []string{"HdrSubject(~^1.*1$)", "HdrDestination(1002)"}}, - }, - ActivationInterval: &utils.TPActivationInterval{ - ActivationTime: "2014-07-29T15:00:00Z", - }, - UsageTTL: "1s", - AllocationMessage: "call", - Weight: 10, - Limit: "2", - }, - "ResGroup22": &utils.TPResource{ - TPid: testTPID, - Tenant: "cgrates.org", - ID: "ResGroup22", - Filters: []*utils.TPRequestFilter{ - &utils.TPRequestFilter{Type: MetaDestinations, FieldName: "HdrDestination", Values: []string{"DST_FS"}}, -======= eResProfiles := map[string]map[string]*utils.TPResource{ "cgrates.org": map[string]*utils.TPResource{ "ResGroup21": &utils.TPResource{ @@ -1442,7 +1408,6 @@ func TestLoadResourceProfiles(t *testing.T) { AllocationMessage: "call", Weight: 10, Limit: "2", ->>>>>>> 72be574e2485078deacebbbd01c7df1d05991615 }, "ResGroup22": &utils.TPResource{ TPid: testTPID, From 73399bef7a212e03eced12bd5bd9aaa0ef90d0d2 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 17:02:22 +0300 Subject: [PATCH 11/20] Tenant before ID on TPResources and TPStats --- data/tariffplans/testtp/Resources.csv | 12 ++++++------ data/tariffplans/testtp/Stats.csv | 2 +- engine/models.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/data/tariffplans/testtp/Resources.csv b/data/tariffplans/testtp/Resources.csv index 0077b1c66..a508329df 100755 --- a/data/tariffplans/testtp/Resources.csv +++ b/data/tariffplans/testtp/Resources.csv @@ -1,6 +1,6 @@ -#Id[0],Tenant[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -ResGroup1,cgrates.org,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, -ResGroup1,cgrates.org,*string_prefix,Destination,10;20,,,,,,,, -ResGroup1,cgrates.org,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, -ResGroup2,cgrates.org,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, -ResGroup3,cgrates.org,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] +cgrates.org,ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,7,,true,true,20, +cgrates.org,ResGroup1,*string_prefix,Destination,10;20,,,,,,,, +cgrates.org,ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, +cgrates.org,ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, +cgrates.org,ResGroup3,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, diff --git a/data/tariffplans/testtp/Stats.csv b/data/tariffplans/testtp/Stats.csv index 93311a0e6..28fb633ff 100755 --- a/data/tariffplans/testtp/Stats.csv +++ b/data/tariffplans/testtp/Stats.csv @@ -1,2 +1,2 @@ #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -Tester,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acd;*acc,true,true,20,THRESH1;THRESH2 \ No newline at end of file +cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acd;*acc,true,true,20,THRESH1;THRESH2 diff --git a/engine/models.go b/engine/models.go index 76abdcabd..0031f11f8 100755 --- a/engine/models.go +++ b/engine/models.go @@ -454,8 +454,8 @@ func (t TBLSMCosts) TableName() string { type TpResource struct { PK uint `gorm:"primary_key"` Tpid string - ID string `index:"0" re:""` - Tenant string `index:"1" re:""` + Tenant string `index:"0" re:""` + ID string `index:"1" re:""` FilterType string `index:"2" re:"^\*[A-Za-z].*"` FilterFieldName string `index:"3" re:""` FilterFieldValues string `index:"4" re:""` From 2f887b1ebb68d77e190ec4109973d652fc371a8d Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 17:12:21 +0300 Subject: [PATCH 12/20] Small fix --- engine/loader_csv_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 51ce99556..2191d0e2e 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -266,11 +266,11 @@ cgrates.org,mas,true,another,value,10 *out,cgrates.org,call,remo,remo,*any,*rating,Account,remo,minu,10 ` resProfiles = ` -#Id[0]Tenant[1],,FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -ResGroup21,cgrates.org,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, -ResGroup21,cgrates.org,*string_prefix,HdrDestination,10;20,,,,,,,, -ResGroup21,cgrates.org,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, -ResGroup22,cgrates.org,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],TTL[6],Limit[7],AllocationMessage[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] +cgrates.org,ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,true,true,10, +cgrates.org,ResGroup21,*string_prefix,HdrDestination,10;20,,,,,,,, +cgrates.org,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,,,, +cgrates.org,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, ` stats = ` #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] @@ -1428,7 +1428,7 @@ func TestLoadResourceProfiles(t *testing.T) { }, }, } - if len(csvr.resProfiles["cgrates.org"]) != len(eResProfiles["cgrates.org"]) { + if len(csvr.resProfiles["ResGroup21"]) != len(eResProfiles["ResGroup21"]) { t.Errorf("Failed to load resourceProfiles: %s", utils.ToIJSON(csvr.resProfiles)) } else if !reflect.DeepEqual(eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) { t.Errorf("Expecting: %+v, received: %+v", eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) From d8f3931b7f0fd1674afadee0857d7e76e9bad2b3 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 17:21:24 +0300 Subject: [PATCH 13/20] Small fix for loader_csv_test.go --- engine/loader_csv_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 2191d0e2e..39c0c3823 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1428,7 +1428,7 @@ func TestLoadResourceProfiles(t *testing.T) { }, }, } - if len(csvr.resProfiles["ResGroup21"]) != len(eResProfiles["ResGroup21"]) { + if len(csvr.resProfiles["cgrates.org"]) != len(eResProfiles["cgrates.org"]) { t.Errorf("Failed to load resourceProfiles: %s", utils.ToIJSON(csvr.resProfiles)) } else if !reflect.DeepEqual(eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) { t.Errorf("Expecting: %+v, received: %+v", eResProfiles["ResGroup22"], csvr.resProfiles["ResGroup22"]) From 9188aad445142da3b583358a21b24f17877332e7 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 18:01:51 +0300 Subject: [PATCH 14/20] Small fix on data/storage/mysql and data/sorage/postgres --- data/storage/mysql/create_tariffplan_tables.sql | 8 ++++---- data/storage/postgres/create_tariffplan_tables.sql | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 3e2fe6fa3..8bd959e31 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -399,8 +399,8 @@ DROP TABLE IF EXISTS tp_resources; CREATE TABLE tp_resources ( `pk` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, - `id` varchar(64) NOT NULL, `tenant` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, `filter_field_values` varchar(256) NOT NULL, @@ -415,7 +415,7 @@ CREATE TABLE tp_resources ( `created_at` TIMESTAMP, PRIMARY KEY (`pk`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_resource` (`tpid`, `id`, `filter_type`, `filter_field_name`) + UNIQUE KEY `unique_tp_resource` (`tpid`,`tenant`, `id`, `filter_type`, `filter_field_name`) ); -- @@ -424,9 +424,9 @@ CREATE TABLE tp_resources ( DROP TABLE IF EXISTS tp_stats; CREATE TABLE tp_stats ( - `tenant` varchar(64) NOT NULL, `id` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, `tag` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, @@ -442,7 +442,7 @@ CREATE TABLE tp_stats ( `created_at` TIMESTAMP, PRIMARY KEY (`id`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_stats` (`tpid`, `tag`, `filter_type`, `filter_field_name`) + UNIQUE KEY `unique_tp_stats` (`tpid`, `tenant`, `tag`, `filter_type`, `filter_field_name`) ); -- diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index d065f2ca9..c2e9ef515 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -395,8 +395,8 @@ DROP TABLE IF EXISTS tp_resources; CREATE TABLE tp_resources ( "pk" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, - "id" varchar(64) NOT NULL, "tenant"varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, "filter_field_values" varchar(256) NOT NULL, @@ -411,7 +411,7 @@ CREATE TABLE tp_resources ( "created_at" TIMESTAMP WITH TIME ZONE ); CREATE INDEX tp_resources_idx ON tp_resources (tpid); -CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "id", "filter_type", "filter_field_name"); +CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "tenant", "id", "filter_type", "filter_field_name"); -- @@ -420,9 +420,9 @@ CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "id", "filter_type", DROP TABLE IF EXISTS tp_stats; CREATE TABLE tp_stats ( - "tenant"varchar(64) NOT NULL, "id" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, + "tenant"varchar(64) NOT NULL, "tag" varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, @@ -438,7 +438,7 @@ CREATE TABLE tp_stats ( "created_at" TIMESTAMP WITH TIME ZONE ); CREATE INDEX tp_stats_idx ON tp_stats (tpid); -CREATE INDEX tp_stats_unique ON tp_stats ("tpid", "tag", "filter_type", "filter_field_name"); +CREATE INDEX tp_stats_unique ON tp_stats ("tpid","tenant", "tag", "filter_type", "filter_field_name"); -- -- Table structure for table `tp_threshold_cfgs` From da0746b9aa0ce4e3bc87af70d7c53849a978e770 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 18:16:28 +0300 Subject: [PATCH 15/20] New check for TPid in apier/v2/tp.go --- apier/v2/tp.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apier/v2/tp.go b/apier/v2/tp.go index 2053fc17e..99afc3c4d 100644 --- a/apier/v2/tp.go +++ b/apier/v2/tp.go @@ -30,8 +30,8 @@ type AttrRemTp struct { } func (self *ApierV2) RemTP(attrs AttrRemTp, reply *string) error { - if len(attrs.TPid) == 0 { - return utils.NewErrMandatoryIeMissing("TPid") + if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) } if err := self.StorDb.RemTpData("", attrs.TPid, nil); err != nil { return utils.NewErrServerError(err) @@ -42,8 +42,8 @@ func (self *ApierV2) RemTP(attrs AttrRemTp, reply *string) error { } func (self *ApierV2) ExportTPToFolder(attrs utils.AttrDirExportTP, exported *utils.ExportedTPStats) error { - if len(*attrs.TPid) == 0 { - return utils.NewErrMandatoryIeMissing("TPid") + if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ExportPath"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) } dir := self.Config.TpExportPath if attrs.ExportPath != nil { @@ -75,8 +75,8 @@ func (self *ApierV2) ExportTPToFolder(attrs utils.AttrDirExportTP, exported *uti } func (self *ApierV2) ExportTPToZipString(attrs utils.AttrDirExportTP, reply *string) error { - if len(*attrs.TPid) == 0 { - return utils.NewErrMandatoryIeMissing("TPid") + if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) } dir := "" fileFormat := utils.CSV From 43c713f3a59100711f2a4b208ba77a55d1e9b17c Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 15 Sep 2017 18:29:24 +0300 Subject: [PATCH 16/20] Fix on apier/v2/tp.go ExportTPToFolder --- apier/v2/tp.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apier/v2/tp.go b/apier/v2/tp.go index 99afc3c4d..2513f87f0 100644 --- a/apier/v2/tp.go +++ b/apier/v2/tp.go @@ -30,8 +30,8 @@ type AttrRemTp struct { } func (self *ApierV2) RemTP(attrs AttrRemTp, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) + if len(attrs.TPid) == 0 { + return utils.NewErrMandatoryIeMissing("TPid") } if err := self.StorDb.RemTpData("", attrs.TPid, nil); err != nil { return utils.NewErrServerError(err) @@ -42,8 +42,8 @@ func (self *ApierV2) RemTP(attrs AttrRemTp, reply *string) error { } func (self *ApierV2) ExportTPToFolder(attrs utils.AttrDirExportTP, exported *utils.ExportedTPStats) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ExportPath"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) + if attrs.TPid == nil || *attrs.TPid == "" { + return utils.NewErrMandatoryIeMissing("TPid") } dir := self.Config.TpExportPath if attrs.ExportPath != nil { @@ -75,8 +75,8 @@ func (self *ApierV2) ExportTPToFolder(attrs utils.AttrDirExportTP, exported *uti } func (self *ApierV2) ExportTPToZipString(attrs utils.AttrDirExportTP, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) + if attrs.TPid == nil || *attrs.TPid == "" { + return utils.NewErrMandatoryIeMissing("TPid") } dir := "" fileFormat := utils.CSV From ba0b68f0b8155b564ee60582808cda83b9d0a5eb Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 15 Sep 2017 20:40:15 +0200 Subject: [PATCH 17/20] Indexing resources with tenant in ID, resources integration tests for mysql --- apier/v1/resourcesv1.go | 19 +++--- apier/v1/resourcesv1_it_test.go | 68 +++++++++++--------- data/conf/samples/tutmongo/cgrates.json | 57 +++++++++++++++-- data/tariffplans/tutorial/Resources.csv | 2 +- engine/resources.go | 84 ++++++++++++++++--------- engine/tp_reader.go | 4 +- sessionmanager/fssessionmanager.go | 4 +- sessionmanager/kamailiosm.go | 4 +- utils/apitpdata.go | 13 ++-- utils/consts.go | 2 +- 10 files changed, 166 insertions(+), 91 deletions(-) diff --git a/apier/v1/resourcesv1.go b/apier/v1/resourcesv1.go index 1bb3d5880..db7fb9648 100644 --- a/apier/v1/resourcesv1.go +++ b/apier/v1/resourcesv1.go @@ -62,32 +62,27 @@ func (rsv1 *ResourceSV1) Call(serviceMethod string, args interface{}, reply inte } // GetResourcesForEvent returns Resources matching a specific event -func (rsv1 *ResourceSV1) GetResourcesForEvent(ev map[string]interface{}, reply *[]*engine.ResourceProfile) error { - return rsv1.rls.V1ResourcesForEvent(ev, reply) +func (rsv1 *ResourceSV1) GetResourcesForEvent(args utils.ArgRSv1ResourceUsage, reply *[]*engine.ResourceProfile) error { + return rsv1.rls.V1ResourcesForEvent(args, reply) } // AllowUsage checks if there are limits imposed for event -func (rsv1 *ResourceSV1) AllowUsage(args utils.AttrRLsResourceUsage, allowed *bool) error { +func (rsv1 *ResourceSV1) AllowUsage(args utils.ArgRSv1ResourceUsage, allowed *bool) error { return rsv1.rls.V1AllowUsage(args, allowed) } // V1InitiateResourceUsage records usage for an event -func (rsv1 *ResourceSV1) AllocateResource(args utils.AttrRLsResourceUsage, reply *string) error { +func (rsv1 *ResourceSV1) AllocateResource(args utils.ArgRSv1ResourceUsage, reply *string) error { return rsv1.rls.V1AllocateResource(args, reply) } // V1TerminateResourceUsage releases usage for an event -func (rsv1 *ResourceSV1) ReleaseResource(args utils.AttrRLsResourceUsage, reply *string) error { +func (rsv1 *ResourceSV1) ReleaseResource(args utils.ArgRSv1ResourceUsage, reply *string) error { return rsv1.rls.V1ReleaseResource(args, reply) } -type ArgGetResPrf struct { - Tenant string - ID string -} - // GetResourceProfile returns a resource configuration -func (apierV1 *ApierV1) GetResourceProfile(arg ArgGetResPrf, reply *engine.ResourceProfile) error { +func (apierV1 *ApierV1) GetResourceProfile(arg utils.TenantID, reply *engine.ResourceProfile) error { if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } @@ -115,7 +110,7 @@ func (apierV1 *ApierV1) SetResourceProfile(res *engine.ResourceProfile, reply *s } //RemResourceProfile remove a specific resource configuration -func (apierV1 *ApierV1) RemResourceProfile(arg ArgGetResPrf, reply *string) error { +func (apierV1 *ApierV1) RemResourceProfile(arg utils.TenantID, reply *string) error { if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } diff --git a/apier/v1/resourcesv1_it_test.go b/apier/v1/resourcesv1_it_test.go index a59a8c52c..cb32d7681 100644 --- a/apier/v1/resourcesv1_it_test.go +++ b/apier/v1/resourcesv1_it_test.go @@ -19,7 +19,6 @@ along with this program. If not, see */ package v1 -/* import ( "net/rpc" "net/rpc/jsonrpc" @@ -38,7 +37,7 @@ var ( rlsV1Cfg *config.CGRConfig rlsV1Rpc *rpc.Client rlsV1ConfDIR string //run tests for specific configuration - rlsConfig *engine.ResourceCfg + rlsConfig *engine.ResourceProfile resDelay int ) @@ -49,8 +48,8 @@ var sTestsRLSV1 = []func(t *testing.T){ testV1RsStartEngine, testV1RsRpcConn, testV1RsFromFolder, - testV1RsGetResourcesFromEvent, - testV1RsAllocateResource, + testV1RsGetResourcesForEvent, + /*testV1RsAllocateResource, testV1RsAllowUsage, testV1RsReleaseResource, testV1RsGetResourceConfigBeforeSet, @@ -60,6 +59,7 @@ var sTestsRLSV1 = []func(t *testing.T){ testV1RsGetResourceConfigAfterUpdate, testV1RsRemResourceCOnfig, testV1RsGetResourceConfigAfterDelete, + */ testV1RsStopEngine, } @@ -129,14 +129,16 @@ func testV1RsFromFolder(t *testing.T) { time.Sleep(time.Duration(1000) * time.Millisecond) } -func testV1RsGetResourcesFromEvent(t *testing.T) { - var reply *[]*engine.ResourceCfg - ev := map[string]interface{}{"Unknown": "unknown"} - if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", ev, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { +func testV1RsGetResourcesForEvent(t *testing.T) { + var reply *[]*engine.ResourceProfile + args := &utils.ArgRSv1ResourceUsage{ + Tenant: "cgrates.org", + Event: map[string]interface{}{"Unknown": "unknown"}} + if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", args, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } - ev = map[string]interface{}{"Destination": "10"} - if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", ev, &reply); err != nil { + args.Event = map[string]interface{}{"Destination": "10"} + if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", args, &reply); err != nil { t.Error(err) } if len(*reply) != 1 { @@ -146,21 +148,21 @@ func testV1RsGetResourcesFromEvent(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", "ResGroup2", (*reply)[0].ID) } - ev = map[string]interface{}{"Destination": "20"} - if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", ev, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + args.Event = map[string]interface{}{"Destination": "20"} + if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", args, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } - ev = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1002"} - if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", ev, &reply); err != nil { + args.Event = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1002"} + if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", args, &reply); err != nil { t.Error(err) } if len(*reply) != 2 { t.Errorf("Expecting: %+v, received: %+v", 2, len(*reply)) } - ev = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1001"} - if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", ev, &reply); err != nil { + args.Event = map[string]interface{}{"Account": "1002", "Subject": "test", "Destination": "1001"} + if err := rlsV1Rpc.Call("ResourceSV1.GetResourcesForEvent", args, &reply); err != nil { t.Error(err) } if len(*reply) != 1 { @@ -174,7 +176,7 @@ func testV1RsGetResourcesFromEvent(t *testing.T) { func testV1RsAllocateResource(t *testing.T) { var reply string - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"}, Units: 3, @@ -188,7 +190,7 @@ func testV1RsAllocateResource(t *testing.T) { time.Sleep(time.Duration(1000) * time.Millisecond) - attrRU = utils.AttrRLsResourceUsage{ + attrRU = utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e52", Event: map[string]interface{}{"Destination": "100"}, Units: 5, @@ -202,7 +204,7 @@ func testV1RsAllocateResource(t *testing.T) { time.Sleep(time.Duration(1000) * time.Millisecond) - attrRU = utils.AttrRLsResourceUsage{ + attrRU = utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e53", Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"}, Units: 3, @@ -218,7 +220,7 @@ func testV1RsAllocateResource(t *testing.T) { func testV1RsAllowUsage(t *testing.T) { var allowed bool - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"}, Units: 1, @@ -229,7 +231,7 @@ func testV1RsAllowUsage(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", true, allowed) } - attrRU = utils.AttrRLsResourceUsage{ + attrRU = utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", Event: map[string]interface{}{"Account": "1002", "Subject": "1001", "Destination": "1002"}, Units: 2, @@ -243,7 +245,7 @@ func testV1RsAllowUsage(t *testing.T) { func testV1RsReleaseResource(t *testing.T) { var reply string - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e52", Event: map[string]interface{}{"Destination": "100"}, Units: 2, @@ -267,13 +269,14 @@ func testV1RsReleaseResource(t *testing.T) { func testV1RsGetResourceConfigBeforeSet(t *testing.T) { var reply *string - if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", &AttrGetResCfg{ID: "RCFG1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", &utils.TenantID{Tenant: "cgrates.org", ID: "RCFG1"}, + &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } func testV1RsSetResourceConfig(t *testing.T) { - rlsConfig = &engine.ResourceCfg{ + rlsConfig = &engine.ResourceProfile{ ID: "RCFG1", Filters: []*engine.RequestFilter{ &engine.RequestFilter{ @@ -303,8 +306,9 @@ func testV1RsSetResourceConfig(t *testing.T) { } func testV1RsGetResourceConfigAfterSet(t *testing.T) { - var reply *engine.ResourceCfg - if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", &AttrGetResCfg{ID: rlsConfig.ID}, &reply); err != nil { + var reply *engine.ResourceProfile + if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", + &utils.TenantID{Tenant: "cgrates.org", ID: rlsConfig.ID}, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, rlsConfig) { t.Errorf("Expecting: %+v, received: %+v", rlsConfig, reply) @@ -333,8 +337,9 @@ func testV1RsUpdateResourceConfig(t *testing.T) { } func testV1RsGetResourceConfigAfterUpdate(t *testing.T) { - var reply *engine.ResourceCfg - if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", &AttrGetResCfg{ID: rlsConfig.ID}, &reply); err != nil { + var reply *engine.ResourceProfile + if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", + &utils.TenantID{Tenant: "cgrates.org", ID: rlsConfig.ID}, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, rlsConfig) { t.Errorf("Expecting: %+v, received: %+v", rlsConfig, reply) @@ -343,7 +348,8 @@ func testV1RsGetResourceConfigAfterUpdate(t *testing.T) { func testV1RsRemResourceCOnfig(t *testing.T) { var resp string - if err := rlsV1Rpc.Call("ApierV1.RemResourceConfig", &AttrGetResCfg{ID: rlsConfig.ID}, &resp); err != nil { + if err := rlsV1Rpc.Call("ApierV1.RemResourceConfig", + &utils.TenantID{Tenant: "cgrates.org", ID: rlsConfig.ID}, &resp); err != nil { t.Error(err) } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) @@ -352,7 +358,8 @@ func testV1RsRemResourceCOnfig(t *testing.T) { func testV1RsGetResourceConfigAfterDelete(t *testing.T) { var reply *string - if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", &AttrGetResCfg{ID: "RCFG1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := rlsV1Rpc.Call("ApierV1.GetResourceConfig", + &utils.TenantID{Tenant: "cgrates.org", ID: "RCFG1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } @@ -362,4 +369,3 @@ func testV1RsStopEngine(t *testing.T) { t.Error(err) } } -*/ diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json index dd8d774d6..e406a0baf 100644 --- a/data/conf/samples/tutmongo/cgrates.json +++ b/data/conf/samples/tutmongo/cgrates.json @@ -27,6 +27,26 @@ }, +"cache":{ + "destinations": {"limit": 10000, "ttl":"0s", "precache": true}, + "reverse_destinations": {"limit": 10000, "ttl":"0s", "precache": true}, + "rating_plans": {"limit": 10000, "ttl":"0s","precache": true}, + "rating_profiles": {"limit": 10000, "ttl":"0s", "precache": true}, + "lcr_rules": {"limit": 10000, "ttl":"0s", "precache": true}, + "cdr_stats": {"limit": 10000, "ttl":"0s", "precache": true}, + "actions": {"limit": 10000, "ttl":"0s", "precache": true}, + "action_plans": {"limit": 10000, "ttl":"0s", "precache": true}, + "account_action_plans": {"limit": 10000, "ttl":"0s", "precache": true}, + "action_triggers": {"limit": 10000, "ttl":"0s", "precache": true}, + "shared_groups": {"limit": 10000, "ttl":"0s", "precache": true}, + "aliases": {"limit": 10000, "ttl":"0s", "precache": true}, + "reverse_aliases": {"limit": 10000, "ttl":"0s", "precache": true}, + "derived_chargers": {"limit": 10000, "ttl":"0s", "precache": true}, + "resource_profiles": {"limit": 10000, "ttl":"0s", "precache": true}, + "resources": {"limit": 10000, "ttl":"0s", "precache": true}, +}, + + "rals": { "enabled": true, "cdrstats_conns": [ @@ -57,6 +77,26 @@ }, +"cdre": { + "TestTutITExportCDR": { + "content_fields": [ + {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, + {"tag": "RunID", "type": "*composed", "value": "RunID"}, + {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, + {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, + {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, + {"tag":"Category", "type": "*composed", "value": "Category"}, + {"tag":"Account", "type": "*composed", "value": "Account"}, + {"tag":"Destination", "type": "*composed", "value": "Destination"}, + {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"tag":"Usage", "type": "*composed", "value": "Usage"}, + {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, + {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + ], + }, +}, + + "cdrstats": { "enabled": true, }, @@ -73,11 +113,6 @@ }, -"aliases": { - "enabled": true, -}, - - "resources": { "enabled": true, }, @@ -89,9 +124,19 @@ }, +"historys": { + "enabled": true, +}, + + +"aliases": { + "enabled": true, +}, + + "sm_generic": { "enabled": true, }, -} +} \ No newline at end of file diff --git a/data/tariffplans/tutorial/Resources.csv b/data/tariffplans/tutorial/Resources.csv index e67f01a71..ecd659285 100755 --- a/data/tariffplans/tutorial/Resources.csv +++ b/data/tariffplans/tutorial/Resources.csv @@ -5,4 +5,4 @@ cgrates.org,ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,,,, cgrates.org,ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,8,SPECIAL_1002,true,true,10, cgrates.org,ResGroup3,*string,Account,3001,2014-07-29T15:00:00Z,1s,3,,true,true,20, #cgrates.org,ResGroup3,*timings,SetupTime,PEAK,,,,,,,, -#cgrates.org,ResGroup3,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, +#cgrates.org,ResGroup3,*stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,,,, diff --git a/engine/resources.go b/engine/resources.go index 422385269..93d558903 100755 --- a/engine/resources.go +++ b/engine/resources.go @@ -65,6 +65,10 @@ type ResourceUsage struct { Units float64 // Number of units used } +func (ru *ResourceUsage) TenantID() string { + return utils.ConcatenatedKey(ru.Tenant, ru.ID) +} + // isActive checks ExpiryTime at some time func (ru *ResourceUsage) isActive(atTime time.Time) bool { return ru.ExpiryTime.IsZero() || ru.ExpiryTime.Sub(atTime) > 0 @@ -132,10 +136,10 @@ func (r *Resource) totalUsage() (tU float64) { // recordUsage records a new usage func (r *Resource) recordUsage(ru *ResourceUsage) (err error) { - if _, hasID := r.Usages[ru.ID]; hasID { - return fmt.Errorf("duplicate resource usage with id: %s", ru.ID) + if _, hasID := r.Usages[ru.TenantID()]; hasID { + return fmt.Errorf("duplicate resource usage with id: %s", ru.TenantID()) } - r.Usages[ru.ID] = ru + r.Usages[ru.TenantID()] = ru if r.tUsage != nil { *r.tUsage += ru.Units } @@ -143,12 +147,12 @@ func (r *Resource) recordUsage(ru *ResourceUsage) (err error) { } // clearUsage clears the usage for an ID -func (r *Resource) clearUsage(ruID string) (err error) { - ru, hasIt := r.Usages[ruID] +func (r *Resource) clearUsage(ruTntID string) (err error) { + ru, hasIt := r.Usages[ruTntID] if !hasIt { - return fmt.Errorf("Cannot find usage record with id: %s", ruID) + return fmt.Errorf("Cannot find usage record with id: %s", ruTntID) } - delete(r.Usages, ruID) + delete(r.Usages, ruTntID) if r.tUsage != nil { *r.tUsage -= ru.Units } @@ -175,17 +179,17 @@ func (rs Resources) recordUsage(ru *ResourceUsage) (err error) { } if err != nil { for _, r := range rs[:nonReservedIdx] { - r.clearUsage(ru.ID) // best effort + r.clearUsage(ru.TenantID()) // best effort } } return } // clearUsage gives back the units to the pool -func (rs Resources) clearUsage(ruID string) (err error) { +func (rs Resources) clearUsage(ruTntID string) (err error) { for _, r := range rs { - if errClear := r.clearUsage(ruID); errClear != nil { - utils.Logger.Warning(fmt.Sprintf(", err: %s", errClear.Error())) + if errClear := r.clearUsage(ruTntID); errClear != nil { + utils.Logger.Warning(fmt.Sprintf(", clear ruID: %s, err: %s", ruTntID, errClear.Error())) err = errClear } } @@ -389,13 +393,13 @@ func (rS *ResourceService) cachedResourcesForEvent(evUUID string) (rs Resources) } // matchingResourcesForEvent returns ordered list of matching resources which are active by the time of the call -func (rS *ResourceService) matchingResourcesForEvent(ev map[string]interface{}) (rs Resources, err error) { +func (rS *ResourceService) matchingResourcesForEvent(tenant string, ev map[string]interface{}) (rs Resources, err error) { matchingResources := make(map[string]*Resource) - rIDs, err := matchingItemIDsForEvent(ev, rS.dataDB, utils.ResourceProfilesIndex) + rIDs, err := matchingItemIDsForEvent(ev, rS.dataDB, utils.ResourceProfilesStringIndex+tenant) if err != nil { return nil, err } - lockIDs := utils.PrefixSliceItems(rIDs.Slice(), utils.ResourceProfilesIndex) + lockIDs := utils.PrefixSliceItems(rIDs.Slice(), utils.ResourceProfilesStringIndex) guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) for resName := range rIDs { @@ -451,32 +455,46 @@ func (rS *ResourceService) matchingResourcesForEvent(ev map[string]interface{}) } // V1ResourcesForEvent returns active resource configs matching the event -func (rS *ResourceService) V1ResourcesForEvent(ev map[string]interface{}, reply *[]*ResourceProfile) error { - matchingRLForEv, err := rS.matchingResourcesForEvent(ev) - if err != nil { - return err +func (rS *ResourceService) V1ResourcesForEvent(args utils.ArgRSv1ResourceUsage, reply *[]*ResourceProfile) (err error) { + if args.Tenant == "" { + return utils.NewErrMandatoryIeMissing("Tenant") } - if len(matchingRLForEv) == 0 { + var mtcRLs Resources + if args.UsageID != "" { // only cached if UsageID is present + mtcRLs = rS.cachedResourcesForEvent(args.TenantID()) + } + if mtcRLs == nil { + if mtcRLs, err = rS.matchingResourcesForEvent(args.Tenant, args.Event); err != nil { + return err + } + cache.Set(utils.EventResourcesPrefix+args.TenantID(), mtcRLs.tenantIDs(), true, "") + } + if len(mtcRLs) == 0 { return utils.ErrNotFound } - for _, r := range matchingRLForEv { + for _, r := range mtcRLs { *reply = append(*reply, r.rPrf) } return nil } // V1AllowUsage queries service to find if an Usage is allowed -func (rS *ResourceService) V1AllowUsage(args utils.AttrRLsResourceUsage, allow *bool) (err error) { - mtcRLs := rS.cachedResourcesForEvent(args.UsageID) +func (rS *ResourceService) V1AllowUsage(args utils.ArgRSv1ResourceUsage, allow *bool) (err error) { + if missing := utils.MissingStructFields(&args, []string{"Tenant", "UsageID"}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + mtcRLs := rS.cachedResourcesForEvent(args.TenantID()) if mtcRLs == nil { - if mtcRLs, err = rS.matchingResourcesForEvent(args.Event); err != nil { + if mtcRLs, err = rS.matchingResourcesForEvent(args.Tenant, args.Event); err != nil { return err } - cache.Set(utils.EventResourcesPrefix+args.UsageID, mtcRLs.tenantIDs(), true, "") + cache.Set(utils.EventResourcesPrefix+args.TenantID(), mtcRLs.tenantIDs(), true, "") } if _, err = mtcRLs.AllocateResource( - &ResourceUsage{ID: args.UsageID, - Units: args.Units}, true); err != nil { + &ResourceUsage{ + Tenant: args.Tenant, + ID: args.UsageID, + Units: args.Units}, true); err != nil { if err == utils.ErrResourceUnavailable { cache.Set(utils.EventResourcesPrefix+args.UsageID, nil, true, "") err = nil @@ -489,11 +507,14 @@ func (rS *ResourceService) V1AllowUsage(args utils.AttrRLsResourceUsage, allow * } // V1AllocateResource is called when a resource requires allocation -func (rS *ResourceService) V1AllocateResource(args utils.AttrRLsResourceUsage, reply *string) (err error) { +func (rS *ResourceService) V1AllocateResource(args utils.ArgRSv1ResourceUsage, reply *string) (err error) { + if missing := utils.MissingStructFields(&args, []string{"Tenant", "UsageID"}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } var wasCached bool mtcRLs := rS.cachedResourcesForEvent(args.UsageID) if mtcRLs == nil { - if mtcRLs, err = rS.matchingResourcesForEvent(args.Event); err != nil { + if mtcRLs, err = rS.matchingResourcesForEvent(args.Tenant, args.Event); err != nil { return } } else { @@ -534,10 +555,13 @@ func (rS *ResourceService) V1AllocateResource(args utils.AttrRLsResourceUsage, r } // V1ReleaseResource is called when we need to clear an allocation -func (rS *ResourceService) V1ReleaseResource(args utils.AttrRLsResourceUsage, reply *string) (err error) { +func (rS *ResourceService) V1ReleaseResource(args utils.ArgRSv1ResourceUsage, reply *string) (err error) { + if missing := utils.MissingStructFields(&args, []string{"Tenant", "UsageID"}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } mtcRLs := rS.cachedResourcesForEvent(args.UsageID) if mtcRLs == nil { - if mtcRLs, err = rS.matchingResourcesForEvent(args.Event); err != nil { + if mtcRLs, err = rS.matchingResourcesForEvent(args.Tenant, args.Event); err != nil { return } } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 62d509641..51092cbaa 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -2066,7 +2066,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err log.Print("Indexing resource profiles") } for tenant, mpID := range tpr.resProfiles { - rlIdxr, err := NewReqFilterIndexer(tpr.dataStorage, utils.ResourceProfilesIndex+tenant) + rlIdxr, err := NewReqFilterIndexer(tpr.dataStorage, utils.ResourceProfilesStringIndex+tenant) if err != nil { return err } @@ -2074,7 +2074,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if rl, err := APItoResource(tpRL, tpr.timezone); err != nil { return err } else { - rlIdxr.IndexFilters(rl.ID, rl.Filters) + rlIdxr.IndexFilters(rl.TenantID(), rl.Filters) } } if verbose { diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 022b1609f..1d84e30a2 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -213,7 +213,7 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) { } if sm.rls != nil { var reply string - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: ev.GetUUID(), Event: ev.(FSEvent).AsMapStringInterface(sm.timezone), Units: 1, @@ -278,7 +278,7 @@ func (sm *FSSessionManager) onChannelHangupComplete(ev engine.Event) { sm.ProcessCdr(ev.AsStoredCdr(config.CgrConfig().DefaultTimezone)) } var reply string - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: ev.GetUUID(), Event: ev.(FSEvent).AsMapStringInterface(sm.timezone), Units: 1, diff --git a/sessionmanager/kamailiosm.go b/sessionmanager/kamailiosm.go index 40b72e191..556930a0b 100644 --- a/sessionmanager/kamailiosm.go +++ b/sessionmanager/kamailiosm.go @@ -79,7 +79,7 @@ func (self *KamailioSessionManager) allocateResources(kev KamEvent) (err error) if ev, err = kev.AsMapStringIface(); err != nil { return } - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: kev.GetUUID(), Event: ev, Units: 1, // One channel reserved @@ -212,7 +212,7 @@ func (self *KamailioSessionManager) onCallEnd(evData []byte, connId string) { return } var reply string - attrRU := utils.AttrRLsResourceUsage{ + attrRU := utils.ArgRSv1ResourceUsage{ UsageID: kev.GetUUID(), Event: ev, Units: 1, diff --git a/utils/apitpdata.go b/utils/apitpdata.go index db55e86b7..a24cd45ec 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1270,7 +1270,7 @@ type AttrSetBalance struct { type TPResource struct { TPid string - Tenant string + Tenant string ID string // Identifier of this limit Filters []*TPRequestFilter // Filters for the request ActivationInterval *TPActivationInterval // Time when this limit becomes active/expires @@ -1300,10 +1300,15 @@ type AttrRLsCache struct { ResourceIDs []string } -type AttrRLsResourceUsage struct { - Event map[string]interface{} +type ArgRSv1ResourceUsage struct { + Tenant string UsageID string // ResourceUsage Identifier Units float64 + Event map[string]interface{} +} + +func (args *ArgRSv1ResourceUsage) TenantID() string { + return ConcatenatedKey(args.Tenant, args.UsageID) } // AsActivationTime converts TPActivationInterval into ActivationInterval @@ -1336,7 +1341,7 @@ type AttrDisconnectSession struct { // TPStats is used in APIs to manage remotely offline Stats config type TPStats struct { TPid string - Tenant string + Tenant string ID string Filters []*TPRequestFilter ActivationInterval *TPActivationInterval diff --git a/utils/consts.go b/utils/consts.go index 70bd03e16..691d7ae87 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -241,7 +241,7 @@ const ( ALIASES_PREFIX = "als_" REVERSE_ALIASES_PREFIX = "rls_" ResourcesPrefix = "res_" - ResourceProfilesIndex = "rsi_" + ResourceProfilesStringIndex = "rsi_" ResourceProfilesPrefix = "rsp_" StatsIndex = "sti_" ThresholdsPrefix = "ths_" From 26efe5456ee2f4bacddf07ad4a7b63a49ac04270 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 16 Sep 2017 08:33:11 +0200 Subject: [PATCH 18/20] Resource indexing usage on ID instead of TenantID --- apier/v1/resourcesv1_it_test.go | 2 ++ engine/onstor_it_test.go | 8 ++++---- engine/resources.go | 15 +++++++-------- engine/resources_test.go | 26 +++++++++++++++++--------- engine/tp_reader.go | 2 +- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apier/v1/resourcesv1_it_test.go b/apier/v1/resourcesv1_it_test.go index cb32d7681..e506693e5 100644 --- a/apier/v1/resourcesv1_it_test.go +++ b/apier/v1/resourcesv1_it_test.go @@ -71,12 +71,14 @@ func TestRsV1ITMySQL(t *testing.T) { } } +/* func TestRsV1ITMongo(t *testing.T) { rlsV1ConfDIR = "tutmongo" for _, stest := range sTestsRLSV1 { t.Run(rlsV1ConfDIR, stest) } } +*/ func testV1RsLoadConfig(t *testing.T) { var err error diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index b267baa22..6b326f23c 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -196,7 +196,7 @@ func testOnStorITSetReqFilterIndexes(t *testing.T) { }, }, } - if err := onStor.SetReqFilterIndexes(utils.ResourceProfilesIndex, idxes); err != nil { + if err := onStor.SetReqFilterIndexes(utils.ResourceProfilesStringIndex, idxes); err != nil { t.Error(err) } } @@ -227,7 +227,7 @@ func testOnStorITGetReqFilterIndexes(t *testing.T) { }, }, } - if idxes, err := onStor.GetReqFilterIndexes(utils.ResourceProfilesIndex); err != nil { + if idxes, err := onStor.GetReqFilterIndexes(utils.ResourceProfilesStringIndex); err != nil { t.Error(err) } else if !reflect.DeepEqual(eIdxes, idxes) { t.Errorf("Expecting: %+v, received: %+v", eIdxes, idxes) @@ -242,12 +242,12 @@ func testOnStorITMatchReqFilterIndex(t *testing.T) { "RL1": true, "RL2": true, } - if rcvMp, err := onStor.MatchReqFilterIndex(utils.ResourceProfilesIndex, "Account", "1002"); err != nil { + if rcvMp, err := onStor.MatchReqFilterIndex(utils.ResourceProfilesStringIndex, "Account", "1002"); err != nil { t.Error(err) } else if !reflect.DeepEqual(eMp, rcvMp) { t.Errorf("Expecting: %+v, received: %+v", eMp, rcvMp) } - if _, err := onStor.MatchReqFilterIndex(utils.ResourceProfilesIndex, "NonexistentField", "1002"); err == nil || err != utils.ErrNotFound { + if _, err := onStor.MatchReqFilterIndex(utils.ResourceProfilesStringIndex, "NonexistentField", "1002"); err == nil || err != utils.ErrNotFound { t.Error(err) } } diff --git a/engine/resources.go b/engine/resources.go index 93d558903..2bd601bd0 100755 --- a/engine/resources.go +++ b/engine/resources.go @@ -136,10 +136,10 @@ func (r *Resource) totalUsage() (tU float64) { // recordUsage records a new usage func (r *Resource) recordUsage(ru *ResourceUsage) (err error) { - if _, hasID := r.Usages[ru.TenantID()]; hasID { + if _, hasID := r.Usages[ru.ID]; hasID { return fmt.Errorf("duplicate resource usage with id: %s", ru.TenantID()) } - r.Usages[ru.TenantID()] = ru + r.Usages[ru.ID] = ru if r.tUsage != nil { *r.tUsage += ru.Units } @@ -147,12 +147,12 @@ func (r *Resource) recordUsage(ru *ResourceUsage) (err error) { } // clearUsage clears the usage for an ID -func (r *Resource) clearUsage(ruTntID string) (err error) { - ru, hasIt := r.Usages[ruTntID] +func (r *Resource) clearUsage(ruID string) (err error) { + ru, hasIt := r.Usages[ruID] if !hasIt { - return fmt.Errorf("Cannot find usage record with id: %s", ruTntID) + return fmt.Errorf("cannot find usage record with id: %s", ruID) } - delete(r.Usages, ruTntID) + delete(r.Usages, ruID) if r.tUsage != nil { *r.tUsage -= ru.Units } @@ -403,8 +403,7 @@ func (rS *ResourceService) matchingResourcesForEvent(tenant string, ev map[strin guardian.Guardian.GuardIDs(config.CgrConfig().LockingTimeout, lockIDs...) defer guardian.Guardian.UnguardIDs(lockIDs...) for resName := range rIDs { - rTntID := utils.NewTenantID(resName) - rPrf, err := rS.dataDB.GetResourceProfile(rTntID.Tenant, rTntID.ID, false, utils.NonTransactional) + rPrf, err := rS.dataDB.GetResourceProfile(tenant, resName, false, utils.NonTransactional) if err != nil { if err == utils.ErrNotFound { continue diff --git a/engine/resources_test.go b/engine/resources_test.go index 66816ad12..b1b154b06 100644 --- a/engine/resources_test.go +++ b/engine/resources_test.go @@ -32,23 +32,27 @@ var ( rs Resources ) -func TestRSRecordUsage(t *testing.T) { +func TestRSRecordUsage1(t *testing.T) { ru1 = &ResourceUsage{ + Tenant: "cgrates.org", ID: "RU1", ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Units: 2, } ru2 = &ResourceUsage{ + Tenant: "cgrates.org", ID: "RU2", ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Units: 2, } r1 = &Resource{ - ID: "RL1", + Tenant: "cgrates.org", + ID: "RL1", rPrf: &ResourceProfile{ - ID: "RL1", + Tenant: "cgrates.org", + ID: "RL1", Filters: []*RequestFilter{ &RequestFilter{ Type: MetaString, @@ -83,13 +87,13 @@ func TestRSRecordUsage(t *testing.T) { t.Error(err.Error()) } else { if err := r1.recordUsage(ru1); err == nil { - t.Error("Duplicate ResourceUsage id should not be allowed") + t.Error("duplicate ResourceUsage id should not be allowed") } if _, found := r1.Usages[ru2.ID]; !found { t.Error("ResourceUsage was not recorded") } if *r1.tUsage != 4 { - t.Errorf("Expecting: %+v, received: %+v", 4, r1.tUsage) + t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage) } } @@ -126,7 +130,8 @@ func TestRSUsedUnits(t *testing.T) { func TestRSRsort(t *testing.T) { r2 = &Resource{ - ID: "RL2", + Tenant: "cgrates.org", + ID: "RL2", rPrf: &ResourceProfile{ ID: "RL2", Filters: []*RequestFilter{ @@ -246,9 +251,11 @@ func TestRSAllocateResource(t *testing.T) { // TestRSCacheSetGet assurace the presence of private params in cached resource func TestRSCacheSetGet(t *testing.T) { r := &Resource{ - ID: "RL", + Tenant: "cgrates.org", + ID: "RL", rPrf: &ResourceProfile{ - ID: "RL", + Tenant: "cgrates.org", + ID: "RL", Filters: []*RequestFilter{ &RequestFilter{ Type: MetaString, @@ -273,6 +280,7 @@ func TestRSCacheSetGet(t *testing.T) { }, Usages: map[string]*ResourceUsage{ "RU2": &ResourceUsage{ + Tenant: "cgrates.org", ID: "RU2", ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Units: 2, @@ -281,7 +289,7 @@ func TestRSCacheSetGet(t *testing.T) { tUsage: utils.Float64Pointer(2), dirty: utils.BoolPointer(true), } - key := utils.ResourcesPrefix + r.ID + key := utils.ResourcesPrefix + r.TenantID() cache.Set(key, r, true, "") if x, ok := cache.Get(key); !ok { t.Error("not in cache") diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 51092cbaa..94b5341e6 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -2074,7 +2074,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err if rl, err := APItoResource(tpRL, tpr.timezone); err != nil { return err } else { - rlIdxr.IndexFilters(rl.TenantID(), rl.Filters) + rlIdxr.IndexFilters(rl.ID, rl.Filters) } } if verbose { From 0bc847de39def2a118c912949162a0bdd7be3307 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 16 Sep 2017 09:35:03 +0200 Subject: [PATCH 19/20] Load TTL from profile on Resource init --- apier/v1/resourcesv1_it_test.go | 2 -- engine/resources.go | 37 ++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/apier/v1/resourcesv1_it_test.go b/apier/v1/resourcesv1_it_test.go index e506693e5..cb32d7681 100644 --- a/apier/v1/resourcesv1_it_test.go +++ b/apier/v1/resourcesv1_it_test.go @@ -71,14 +71,12 @@ func TestRsV1ITMySQL(t *testing.T) { } } -/* func TestRsV1ITMongo(t *testing.T) { rlsV1ConfDIR = "tutmongo" for _, stest := range sTestsRLSV1 { t.Run(rlsV1ConfDIR, stest) } } -*/ func testV1RsLoadConfig(t *testing.T) { var err error diff --git a/engine/resources.go b/engine/resources.go index 2bd601bd0..54131b224 100755 --- a/engine/resources.go +++ b/engine/resources.go @@ -74,6 +74,13 @@ func (ru *ResourceUsage) isActive(atTime time.Time) bool { return ru.ExpiryTime.IsZero() || ru.ExpiryTime.Sub(atTime) > 0 } +// clone duplicates ru +func (ru *ResourceUsage) Clone() (cln *ResourceUsage) { + cln = new(ResourceUsage) + *cln = *ru + return +} + // Resource represents a resource in the system // not thread safe, needs locking at process level type Resource struct { @@ -81,6 +88,7 @@ type Resource struct { ID string Usages map[string]*ResourceUsage TTLIdx []string // holds ordered list of ResourceIDs based on their TTL, empty if feature is disabled + ttl *time.Duration // time to leave for this resource, picked up on each Resource initialization out of config tUsage *float64 // sum of all usages dirty *bool // the usages were modified, needs save, *bool so we only save if enabled in config rPrf *ResourceProfile // for ordering purposes @@ -139,10 +147,17 @@ func (r *Resource) recordUsage(ru *ResourceUsage) (err error) { if _, hasID := r.Usages[ru.ID]; hasID { return fmt.Errorf("duplicate resource usage with id: %s", ru.TenantID()) } + if r.ttl != nil { + ru = ru.Clone() // don't influence the initial ru + ru.ExpiryTime = time.Now().Add(*r.ttl) + } r.Usages[ru.ID] = ru if r.tUsage != nil { *r.tUsage += ru.Units } + if !ru.ExpiryTime.IsZero() { + r.TTLIdx = append(r.TTLIdx, ru.ID) + } return } @@ -152,10 +167,18 @@ func (r *Resource) clearUsage(ruID string) (err error) { if !hasIt { return fmt.Errorf("cannot find usage record with id: %s", ruID) } - delete(r.Usages, ruID) + if !ru.ExpiryTime.IsZero() { + for i, ruIDIdx := range r.TTLIdx { + if ruIDIdx == ruID { + r.TTLIdx = append(r.TTLIdx[:i], r.TTLIdx[i+1:]...) + break + } + } + } if r.tUsage != nil { *r.tUsage -= ru.Units } + delete(r.Usages, ruID) return } @@ -227,10 +250,11 @@ func (rs Resources) AllocateResource(ru *ResourceUsage, dryRun bool) (alcMessage for _, r := range rs { if r.rPrf.Limit >= r.totalUsage()+ru.Units { if alcMessage == "" { - alcMessage = r.rPrf.AllocationMessage - } - if alcMessage == "" { // rl.AllocationMessage is not populated - alcMessage = r.rPrf.ID + if r.rPrf.AllocationMessage != "" { + alcMessage = r.rPrf.AllocationMessage + } else { + alcMessage = r.rPrf.ID + } } } } @@ -433,6 +457,9 @@ func (rS *ResourceService) matchingResourcesForEvent(tenant string, ev map[strin if rPrf.Stored { r.dirty = utils.BoolPointer(false) } + if rPrf.UsageTTL > 0 { + r.ttl = utils.DurationPointer(rPrf.UsageTTL) + } r.rPrf = rPrf matchingResources[rPrf.ID] = r // Cannot save it here since we could have errors after and resource will remain unused } From 042402cad5b48914390439c0fbac351af427356d Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 16 Sep 2017 09:46:11 +0200 Subject: [PATCH 20/20] Adding Tenant in resources allocation process for SM-FreeSWITCH and SM-Kamailio --- sessionmanager/fssessionmanager.go | 2 ++ sessionmanager/kamailiosm.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 1d84e30a2..f68ef9eb9 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -214,6 +214,7 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) { if sm.rls != nil { var reply string attrRU := utils.ArgRSv1ResourceUsage{ + Tenant: ev.(FSEvent).GetTenant(utils.META_DEFAULT), UsageID: ev.GetUUID(), Event: ev.(FSEvent).AsMapStringInterface(sm.timezone), Units: 1, @@ -279,6 +280,7 @@ func (sm *FSSessionManager) onChannelHangupComplete(ev engine.Event) { } var reply string attrRU := utils.ArgRSv1ResourceUsage{ + Tenant: ev.(FSEvent).GetTenant(utils.META_DEFAULT), UsageID: ev.GetUUID(), Event: ev.(FSEvent).AsMapStringInterface(sm.timezone), Units: 1, diff --git a/sessionmanager/kamailiosm.go b/sessionmanager/kamailiosm.go index 556930a0b..22bf58a72 100644 --- a/sessionmanager/kamailiosm.go +++ b/sessionmanager/kamailiosm.go @@ -80,6 +80,7 @@ func (self *KamailioSessionManager) allocateResources(kev KamEvent) (err error) return } attrRU := utils.ArgRSv1ResourceUsage{ + Tenant: kev.GetTenant(utils.META_DEFAULT), UsageID: kev.GetUUID(), Event: ev, Units: 1, // One channel reserved @@ -213,6 +214,7 @@ func (self *KamailioSessionManager) onCallEnd(evData []byte, connId string) { } var reply string attrRU := utils.ArgRSv1ResourceUsage{ + Tenant: kev.GetTenant(utils.META_DEFAULT), UsageID: kev.GetUUID(), Event: ev, Units: 1,