From b6f6562567438e1596e9e0cad05aae440ece2c09 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 18 Mar 2016 10:02:15 +0100 Subject: [PATCH 01/26] improve cacll cost calculation after refund --- sessionmanager/smg_session.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 815daa4e3..6f94b1a0e 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -175,7 +175,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { return err } } - firstCC.Cost -= refundIncrements.GetTotalCost() + //firstCC.Cost -= refundIncrements.GetTotalCost() // use updateCost instead + firstCC.UpdateCost() firstCC.UpdateRatedUsage() firstCC.Timespans.Compress() return nil @@ -183,11 +184,12 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { // Session has ended, check debits and refund the extra charged duration func (self *SMGSession) close(endTime time.Time) error { - firstCC := self.callCosts[0] - for _, cc := range self.callCosts[1:] { - firstCC.Merge(cc) - } if len(self.callCosts) != 0 { // We have had at least one cost calculation + firstCC := self.callCosts[0] + for _, cc := range self.callCosts[1:] { + firstCC.Merge(cc) + } + //utils.Logger.Debug(fmt.Sprintf("MergedCC: %+v", firstCC)) end := firstCC.GetEndTime() refundDuration := end.Sub(endTime) self.refund(refundDuration) @@ -219,8 +221,7 @@ func (self *SMGSession) saveOperations() error { if len(self.callCosts) == 0 { return nil // There are no costs to save, ignore the operation } - firstCC := self.callCosts[0] // was merged in close methos - firstCC.Timespans.Compress() + firstCC := self.callCosts[0] // was merged in close method firstCC.Round() roundIncrements := firstCC.GetRoundIncrements() if len(roundIncrements) != 0 { From 84a796226ddde96f86ec9c7f46ea98398388546a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 18 Mar 2016 12:05:41 +0100 Subject: [PATCH 02/26] added *not operator to structmatcher --- structmatcher/README.md | 2 ++ structmatcher/structmatcher.go | 11 +++++++++-- structmatcher/structmatcher_test.go | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/structmatcher/README.md b/structmatcher/README.md index 6220a301d..6f5539eed 100644 --- a/structmatcher/README.md +++ b/structmatcher/README.md @@ -20,7 +20,9 @@ Available operators: - *exp: expired - *or: logical or - *and: logical and +- *not: logical not - *has: receives a list of elements and checks that the elements are present in the specified field (StringMap type) +- *rsr: will apply a rsr check to the field (see utils/rsrfield.go) Equal (*eq) and local and (*and) operators are implicit for shortcuts. In this way: diff --git a/structmatcher/structmatcher.go b/structmatcher/structmatcher.go index 419e23095..50cfa37ca 100644 --- a/structmatcher/structmatcher.go +++ b/structmatcher/structmatcher.go @@ -16,7 +16,9 @@ Available operators: - *exp: expired - *or: logical or - *and: logical and +- *not: logical not - *has: receives a list of elements and checks that the elements are present in the specified field (StringMap type) +- *rsr: will apply a rsr check to the field (see utils/rsrfield.go) Equal (*eq) and local and (*and) operators are implicit for shortcuts. In this way: @@ -43,6 +45,7 @@ const ( CondEXP = "*exp" CondOR = "*or" CondAND = "*and" + CondNOT = "*not" CondHAS = "*has" CondRSR = "*rsr" ) @@ -177,7 +180,7 @@ func (os *operatorSlice) checkStruct(o interface{}) (bool, error) { return true, nil } } - case CondAND: + case CondAND, CondNOT: accumulator := true for _, cond := range os.slice { check, err := cond.checkStruct(o) @@ -186,7 +189,11 @@ func (os *operatorSlice) checkStruct(o interface{}) (bool, error) { } accumulator = accumulator && check } - return accumulator, nil + if os.operator == CondAND { + return accumulator, nil + } else { + return !accumulator, nil + } } return false, nil } diff --git a/structmatcher/structmatcher_test.go b/structmatcher/structmatcher_test.go index e221d1272..e4a30c51e 100644 --- a/structmatcher/structmatcher_test.go +++ b/structmatcher/structmatcher_test.go @@ -95,6 +95,13 @@ func TestStructMatcherKeyValue(t *testing.T) { if check, err := cl.Match(o); !check || err != nil { t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) } + err = cl.Parse(`{"*not":[{"Other":true, "Field":{"*gt":5}}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } err = cl.Parse(`{"Other":true, "Field":{"*gt":7}}`) if err != nil { t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) @@ -345,6 +352,13 @@ func TestStructMatcherOperatorSlice(t *testing.T) { if check, err := cl.Match(o); !check || err != nil { t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) } + err = cl.Parse(`{"*not":[{"*or":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`) if err != nil { t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) @@ -352,6 +366,13 @@ func TestStructMatcherOperatorSlice(t *testing.T) { if check, err := cl.Match(o); !check || err != nil { t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) } + err = cl.Parse(`{"*not":[{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`) if err != nil { t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) From 1c60e50523dad70e91a233767203fb55166f8e43 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 21 Mar 2016 21:42:48 +0200 Subject: [PATCH 03/26] travis to use glide --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 936b99045..899b7c359 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,10 @@ language: go go: - 1.6 +install: + - go get github.com/Masterminds/glide + - glide install + script: $TRAVIS_BUILD_DIR/test.sh branches: From aeeab4b37aeb6c2fc55ad8f0bc4bacd94cb6d671 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 21 Mar 2016 21:50:36 +0200 Subject: [PATCH 04/26] added structmatcher to test.sh --- test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index d298e6a34..3a4a57e95 100755 --- a/test.sh +++ b/test.sh @@ -13,6 +13,7 @@ go test -i github.com/cgrates/cgrates/utils go test -i github.com/cgrates/cgrates/history go test -i github.com/cgrates/cgrates/cdre go test -i github.com/cgrates/cgrates/agents +go test -i github.com/cgrates/cgrates/structmatcher go test github.com/cgrates/cgrates/apier/v1 v1=$? @@ -42,6 +43,8 @@ go test github.com/cgrates/cgrates/cdre cdre=$? go test github.com/cgrates/cgrates/agents ag=$? +go test github.com/cgrates/cgrates/structmatcher +sc=$? -exit $v1 && $v2 && $en && $gt && $sm && $cfg && $bl && $cr && $con && $cdrc && $ut && $hs && $c2g && $cdre && $ag +exit $v1 && $v2 && $en && $gt && $sm && $cfg && $bl && $cr && $con && $cdrc && $ut && $hs && $c2g && $cdre && $ag && $sc From 3a3cb64af8f6240946bb3896eeaf516104da9b75 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 22 Mar 2016 14:30:53 +0200 Subject: [PATCH 05/26] update installing from source --- docs/installation.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 060939f2f..ebe40b156 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -27,23 +27,16 @@ After post-install actions are performed, CGRateS will be configured in */etc/cg 3.2. Using source ----------------- -After the go environment is installed_ (at least go1.2) and configured_ issue the following commands: -:: - - go get github.com/cgrates/cgrates/... - -This command will install the trunk version of CGRateS together with all the necessary dependencies. - -For developing CGRateS and switching betwen lts versions we are using the new (experimental) vendor directory feature introduced in go 1.5. In a nutshell all the dependencies are installed and used from a folder named vendor placed in the root of the project. +For developing CGRateS and switching betwen lts versions we are using the new vendor directory feature introduced in go 1.6. In a nutshell all the dependencies are installed and used from a folder named vendor placed in the root of the project. To manage this vendor folder we use a tool named glide_ which will download specific versions of the external packages used by CGRateS. To configure the project with glide use the following commands: :: - export GO15VENDOREXPERIMENT=1 #this should be placed in the rc script of your shell + go get github.com/Masterminds/glide go get github.com/cgrates/cgrates cd $GOPATH/src/github.com/cgrates/cgrates glide install -The glide install command will install the external dependencies versions specified in the glide.lock file in the vendor folder. There are different versions for each CGRateS branch, versions that are recorded in the yaml file when the GCRateS releases are made (using glide up command). +The glide install command will install the external dependencies versions specified in the glide.lock file in the vendor folder. There are different versions for each CGRateS branch, versions that are recorded in the yaml file when the GCRateS releases are made. Note that the vendor folder should not be registered with the VCS we are using. For more information and command options for use glide_ readme page. From 16c7a3ce8dcbe55804f168523120a76f097e8ecd Mon Sep 17 00:00:00 2001 From: rbarrabe Date: Tue, 22 Mar 2016 13:52:57 +0100 Subject: [PATCH 06/26] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b2a3821bc..6c3e9a5e2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -42,6 +42,7 @@ information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file. | @bhepp | Brice Heppner | | @noahmehl | Noah Mehl | | @elfranne | Tom Braarup Cuykens | +| @rbarrabe | Régis Barrabé | From 2058e885566a100d741f79970f29cf539b011cdb Mon Sep 17 00:00:00 2001 From: rbarrabe Date: Tue, 22 Mar 2016 13:59:27 +0100 Subject: [PATCH 07/26] Update huawei.xml --- data/diameter/dict/huawei/huawei.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/diameter/dict/huawei/huawei.xml b/data/diameter/dict/huawei/huawei.xml index b180087a7..8553ad05b 100644 --- a/data/diameter/dict/huawei/huawei.xml +++ b/data/diameter/dict/huawei/huawei.xml @@ -133,7 +133,7 @@ - + @@ -143,4 +143,4 @@ - \ No newline at end of file + From 31e33aa8d092f0a27627fbd166947257f498c9ca Mon Sep 17 00:00:00 2001 From: rbarrabe Date: Tue, 22 Mar 2016 14:01:38 +0100 Subject: [PATCH 08/26] Update vodafone.xml --- data/diameter/dict/huawei/vodafone.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/diameter/dict/huawei/vodafone.xml b/data/diameter/dict/huawei/vodafone.xml index ff56332c0..be03b5746 100644 --- a/data/diameter/dict/huawei/vodafone.xml +++ b/data/diameter/dict/huawei/vodafone.xml @@ -43,8 +43,8 @@ - + - \ No newline at end of file + From fbbc067a5d2f6d5ec83e56dbf55b7cd26f0b15cf Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 22 Mar 2016 17:48:04 +0200 Subject: [PATCH 09/26] more lastusage tests --- sessionmanager/smg_it_test.go | 184 ++++++++++++++++++++++++++++++++++ sessionmanager/smg_session.go | 15 +-- 2 files changed, 193 insertions(+), 6 deletions(-) diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index cd246596e..30e7af12f 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -422,3 +422,187 @@ func TestSMGLastUsed(t *testing.T) { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) } } + +func TestSMGLastUsedEnd(t *testing.T) { + if !*testIntegration { + return + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 7.59000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:49", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "2m", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 6.190020 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "2m", + utils.LastUsed: "30s", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 6.090030 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.LastUsed: "0s", + } + var rpl string + if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { + t.Error(err) + } + eAcntVal = 6.590000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } +} + +func TestSMGLastUsedNotFixed(t *testing.T) { + if !*testIntegration { + return + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 6.59000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:49", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "2m", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 5.190020 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "2m", + utils.LastUsed: "13s", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 5.123360 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.LastUsed: "0s", + } + var rpl string + if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { + t.Error(err) + } + eAcntVal = 5.590000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } +} + + diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 6f94b1a0e..936f4ac56 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -85,12 +85,14 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } else { // We have debitted less than we have consumed, add the difference to duration debitted lastUsedCorrection = lastUsed - self.lastUsage } + + // apply the lastUsed correction + dur += lastUsedCorrection + self.totalUsage += dur // Should reflect the total usage so far + } else { + // apply correction from previous run + dur -= self.extraDuration } - // apply the lastUsed correction - dur += lastUsedCorrection - self.totalUsage += dur // Should reflect the total usage so far - // apply correction from previous run - dur -= self.extraDuration self.extraDuration = 0 if self.cd.LoopIndex > 0 { self.cd.TimeStart = self.cd.TimeEnd @@ -189,7 +191,7 @@ func (self *SMGSession) close(endTime time.Time) error { for _, cc := range self.callCosts[1:] { firstCC.Merge(cc) } - //utils.Logger.Debug(fmt.Sprintf("MergedCC: %+v", firstCC)) + //utils.Logger.Debug("MergedCC: " + utils.ToJSON(firstCC)) end := firstCC.GetEndTime() refundDuration := end.Sub(endTime) self.refund(refundDuration) @@ -223,6 +225,7 @@ func (self *SMGSession) saveOperations() error { } firstCC := self.callCosts[0] // was merged in close method firstCC.Round() + //utils.Logger.Debug("Saved CC: " + utils.ToJSON(firstCC)) roundIncrements := firstCC.GetRoundIncrements() if len(roundIncrements) != 0 { cd := firstCC.CreateCallDescriptor() From e07acf23f74a70a975531150d33891c0bbcbfc20 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 22 Mar 2016 22:04:26 +0200 Subject: [PATCH 10/26] started smg data tests --- data/tariffplans/testtp/AccountActions.csv | 3 +- data/tariffplans/testtp/ActionPlans.csv | 3 +- data/tariffplans/testtp/Actions.csv | 3 +- data/tariffplans/testtp/DestinationRates.csv | 1 + data/tariffplans/testtp/Destinations.csv | 1 + data/tariffplans/testtp/Rates.csv | 2 + data/tariffplans/testtp/RatingPlans.csv | 1 + data/tariffplans/testtp/RatingProfiles.csv | 1 + sessionmanager/data_it_test.go | 184 +++++++++++++++++++ sessionmanager/smg_it_test.go | 13 -- 10 files changed, 196 insertions(+), 16 deletions(-) create mode 100644 sessionmanager/data_it_test.go diff --git a/data/tariffplans/testtp/AccountActions.csv b/data/tariffplans/testtp/AccountActions.csv index ca940949b..4b6dddf29 100644 --- a/data/tariffplans/testtp/AccountActions.csv +++ b/data/tariffplans/testtp/AccountActions.csv @@ -4,4 +4,5 @@ cgrates.org,1002,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1003,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1004,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1005,PREPAID_10,STANDARD_TRIGGERS,, -cgrates.org,1009,TEST_EXE,,, \ No newline at end of file +cgrates.org,1009,TEST_EXE,,, +cgrates.org,1010,TEST_DATA_r,,, diff --git a/data/tariffplans/testtp/ActionPlans.csv b/data/tariffplans/testtp/ActionPlans.csv index 670cf8c93..f41668f9b 100644 --- a/data/tariffplans/testtp/ActionPlans.csv +++ b/data/tariffplans/testtp/ActionPlans.csv @@ -1,4 +1,5 @@ #Tag,ActionsTag,TimingTag,Weight PREPAID_10,PREPAID_10,ASAP,10 PREPAID_10,BONUS_1,ASAP,10 -TEST_EXE,TOPUP_EXE,ALWAYS,10 \ No newline at end of file +TEST_EXE,TOPUP_EXE,ALWAYS,10 +TEST_DATA_r,TOPUP_DATA_r,ASAP,10 \ No newline at end of file diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 045fc0666..0c39073a9 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -4,4 +4,5 @@ BONUS_1,*topup,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 -TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 \ No newline at end of file +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 +TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,,,*unlimited,,50000000000,10,false,false,10 \ No newline at end of file diff --git a/data/tariffplans/testtp/DestinationRates.csv b/data/tariffplans/testtp/DestinationRates.csv index b5d2474b3..9be903aef 100644 --- a/data/tariffplans/testtp/DestinationRates.csv +++ b/data/tariffplans/testtp/DestinationRates.csv @@ -3,3 +3,4 @@ DR_RETAIL,GERMANY,RT_1CENT,*up,4,0, DR_RETAIL,GERMANY_MOBILE,RT_1CENT,*up,4,0, DR_DATA_1,*any,RT_DATA_2c,*up,4,0, DR_SMS_1,*any,RT_SMS_5c,*up,4,0, +DR_DATA_r,DATA_DEST,RT_DATA_r,*up,5,0, \ No newline at end of file diff --git a/data/tariffplans/testtp/Destinations.csv b/data/tariffplans/testtp/Destinations.csv index 192146546..37385ea00 100644 --- a/data/tariffplans/testtp/Destinations.csv +++ b/data/tariffplans/testtp/Destinations.csv @@ -3,3 +3,4 @@ GERMANY,+49 GERMANY_MOBILE,+4915 GERMANY_MOBILE,+4916 GERMANY_MOBILE,+4917 +DATA_DEST,222 diff --git a/data/tariffplans/testtp/Rates.csv b/data/tariffplans/testtp/Rates.csv index 991ce0720..dd4dd35a7 100644 --- a/data/tariffplans/testtp/Rates.csv +++ b/data/tariffplans/testtp/Rates.csv @@ -2,3 +2,5 @@ RT_1CENT,0,1,1s,1s,0s RT_DATA_2c,0,0.002,10,10,0 RT_SMS_5c,0,0.005,1,1,0 +RT_DATA_r,0,0,1,10240000,0 +RT_DATA_r,0,0,1,10240000,60 \ No newline at end of file diff --git a/data/tariffplans/testtp/RatingPlans.csv b/data/tariffplans/testtp/RatingPlans.csv index 7d15f1a92..f7003dcf4 100644 --- a/data/tariffplans/testtp/RatingPlans.csv +++ b/data/tariffplans/testtp/RatingPlans.csv @@ -2,3 +2,4 @@ RP_RETAIL,DR_RETAIL,ALWAYS,10 RP_DATA1,DR_DATA_1,ALWAYS,10 RP_SMS1,DR_SMS_1,ALWAYS,10 +RP_DATAr,DR_DATA_r,ALWAYS,10 diff --git a/data/tariffplans/testtp/RatingProfiles.csv b/data/tariffplans/testtp/RatingProfiles.csv index db758126b..fed2e73c6 100644 --- a/data/tariffplans/testtp/RatingProfiles.csv +++ b/data/tariffplans/testtp/RatingProfiles.csv @@ -2,3 +2,4 @@ *out,cgrates.org,call,*any,2012-01-01T00:00:00Z,RP_RETAIL,, *out,cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1,, *out,cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,, +*out,cgrates.org,data,*any,2016-01-01T00:00:00Z,RP_DATAr,, diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go new file mode 100644 index 000000000..2cef5654e --- /dev/null +++ b/sessionmanager/data_it_test.go @@ -0,0 +1,184 @@ +package sessionmanager + +import ( + "flag" + "net/rpc" + "net/rpc/jsonrpc" + "path" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var testIntegration = flag.Bool("integration", false, "Perform the tests in integration mode, not by default.") // This flag will be passed here via "go test -local" args +var waitRater = flag.Int("wait_rater", 150, "Number of miliseconds to wait for rater to start and cache") +var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") + +var daCfgPath string +var daCfg *config.CGRConfig +var smgRPC *rpc.Client +var err error + +func TestSMGDataInitCfg(t *testing.T) { + if !*testIntegration { + return + } + daCfgPath = path.Join(*dataDir, "conf", "samples", "smg") + // Init config first + var err error + daCfg, err = config.NewCGRConfigFromFolder(daCfgPath) + if err != nil { + t.Error(err) + } + daCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush() + config.SetCgrConfig(daCfg) +} + +// Remove data in both rating and accounting db +func TestSMGDataResetDataDb(t *testing.T) { + if !*testIntegration { + return + } + if err := engine.InitDataDb(daCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func TestSMGDataResetStorDb(t *testing.T) { + if !*testIntegration { + return + } + if err := engine.InitStorDb(daCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func TestSMGDataStartEngine(t *testing.T) { + if !*testIntegration { + return + } + if _, err := engine.StopStartEngine(daCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func TestSMGDataApierRpcConn(t *testing.T) { + if !*testIntegration { + return + } + var err error + smgRPC, err = jsonrpc.Dial("tcp", daCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +// Load the tariff plan, creating accounts and their balances +func TestSMGDataTPFromFolder(t *testing.T) { + if !*testIntegration { + return + } + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "testtp")} + var loadInst engine.LoadInstance + if err := smgRPC.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { + t.Error(err) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups +} + +func TestSMGDataLastUsedNotFixed(t *testing.T) { + if !*testIntegration { + return + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 6.59000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:49", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "2m", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 5.190020 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "2m", + utils.LastUsed: "13s", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 5.123360 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.LastUsed: "0s", + } + var rpl string + if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { + t.Error(err) + } + eAcntVal = 5.590000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } +} diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 30e7af12f..129505986 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -19,8 +19,6 @@ along with this program. If not, see package sessionmanager import ( - "flag" - "net/rpc" "net/rpc/jsonrpc" "path" "testing" @@ -31,15 +29,6 @@ import ( "github.com/cgrates/cgrates/utils" ) -var testIntegration = flag.Bool("integration", false, "Perform the tests in integration mode, not by default.") // This flag will be passed here via "go test -local" args -var waitRater = flag.Int("wait_rater", 150, "Number of miliseconds to wait for rater to start and cache") -var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") - -var daCfgPath string -var daCfg *config.CGRConfig -var smgRPC *rpc.Client -var err error - func TestSMGInitCfg(t *testing.T) { if !*testIntegration { return @@ -604,5 +593,3 @@ func TestSMGLastUsedNotFixed(t *testing.T) { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) } } - - From 5fc1efc055458bccd31a3c3fe1d7783f6063f4bf Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 11:08:21 +0200 Subject: [PATCH 11/26] updated data tests --- data/tariffplans/testtp/Rates.csv | 3 +- sessionmanager/data_it_test.go | 58 +++++++++++++++---------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/data/tariffplans/testtp/Rates.csv b/data/tariffplans/testtp/Rates.csv index dd4dd35a7..bff768bf1 100644 --- a/data/tariffplans/testtp/Rates.csv +++ b/data/tariffplans/testtp/Rates.csv @@ -2,5 +2,4 @@ RT_1CENT,0,1,1s,1s,0s RT_DATA_2c,0,0.002,10,10,0 RT_SMS_5c,0,0.005,1,1,0 -RT_DATA_r,0,0,1,10240000,0 -RT_DATA_r,0,0,1,10240000,60 \ No newline at end of file +RT_DATA_r,0,0.19,1048576,10240,0 diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index 2cef5654e..d99a287e2 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -92,32 +92,32 @@ func TestSMGDataTPFromFolder(t *testing.T) { time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups } -func TestSMGDataLastUsedNotFixed(t *testing.T) { +func TestSMGDataLastUsedData(t *testing.T) { if !*testIntegration { return } var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} eAcntVal := 6.59000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.VOICE, + utils.TOR: utils.DATA, utils.ACCID: "12349", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1001", - utils.SUBJECT: "1001", - utils.DESTINATION: "1006", - utils.CATEGORY: "call", + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, utils.SETUP_TIME: "2016-01-05 18:30:49", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "2m", + utils.USAGE: "1048576", } var maxUsage float64 if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { @@ -129,22 +129,22 @@ func TestSMGDataLastUsedNotFixed(t *testing.T) { eAcntVal = 5.190020 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.VOICE, + utils.TOR: utils.DATA, utils.ACCID: "12349", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1001", - utils.SUBJECT: "1001", - utils.DESTINATION: "1006", - utils.CATEGORY: "call", + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "2m", - utils.LastUsed: "13s", + utils.USAGE: "1048576", + utils.LastUsed: "20000", } if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { t.Error(err) @@ -155,21 +155,21 @@ func TestSMGDataLastUsedNotFixed(t *testing.T) { eAcntVal = 5.123360 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.VOICE, + utils.TOR: utils.DATA, utils.ACCID: "12349", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1001", - utils.SUBJECT: "1001", - utils.DESTINATION: "1006", - utils.CATEGORY: "call", + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.LastUsed: "0s", + utils.LastUsed: "0", } var rpl string if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { @@ -178,7 +178,7 @@ func TestSMGDataLastUsedNotFixed(t *testing.T) { eAcntVal = 5.590000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } } From 1170a7ba21c9e43e934cac6f3c4ab137c69e43dd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 13:24:49 +0200 Subject: [PATCH 12/26] updated test data --- data/tariffplans/testtp/Actions.csv | 2 +- data/tariffplans/testtp/RatingProfiles.csv | 2 +- sessionmanager/data_it_test.go | 23 ++++++---------------- sessionmanager/smg_it_test.go | 11 +++++++++++ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 0c39073a9..0f478dd4f 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -5,4 +5,4 @@ LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 -TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,,,*unlimited,,50000000000,10,false,false,10 \ No newline at end of file +TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,datar,,*unlimited,,50000000000,10,false,false,10 \ No newline at end of file diff --git a/data/tariffplans/testtp/RatingProfiles.csv b/data/tariffplans/testtp/RatingProfiles.csv index fed2e73c6..ffa6bf2f6 100644 --- a/data/tariffplans/testtp/RatingProfiles.csv +++ b/data/tariffplans/testtp/RatingProfiles.csv @@ -2,4 +2,4 @@ *out,cgrates.org,call,*any,2012-01-01T00:00:00Z,RP_RETAIL,, *out,cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1,, *out,cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,, -*out,cgrates.org,data,*any,2016-01-01T00:00:00Z,RP_DATAr,, +*out,cgrates.org,data,datar,2016-01-01T00:00:00Z,RP_DATAr,, diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index d99a287e2..d3804dfcf 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -1,8 +1,6 @@ package sessionmanager import ( - "flag" - "net/rpc" "net/rpc/jsonrpc" "path" "testing" @@ -13,15 +11,6 @@ import ( "github.com/cgrates/cgrates/utils" ) -var testIntegration = flag.Bool("integration", false, "Perform the tests in integration mode, not by default.") // This flag will be passed here via "go test -local" args -var waitRater = flag.Int("wait_rater", 150, "Number of miliseconds to wait for rater to start and cache") -var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") - -var daCfgPath string -var daCfg *config.CGRConfig -var smgRPC *rpc.Client -var err error - func TestSMGDataInitCfg(t *testing.T) { if !*testIntegration { return @@ -98,7 +87,7 @@ func TestSMGDataLastUsedData(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 6.59000 + eAcntVal := 50000000000.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -123,10 +112,10 @@ func TestSMGDataLastUsedData(t *testing.T) { if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 5.190020 + eAcntVal = 49998951424.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -149,10 +138,10 @@ func TestSMGDataLastUsedData(t *testing.T) { if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 5.123360 + eAcntVal = 49998931424.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -175,7 +164,7 @@ func TestSMGDataLastUsedData(t *testing.T) { if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 5.590000 + eAcntVal = 49998931424.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 129505986..435778513 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -19,6 +19,8 @@ along with this program. If not, see package sessionmanager import ( + "flag" + "net/rpc" "net/rpc/jsonrpc" "path" "testing" @@ -29,6 +31,15 @@ import ( "github.com/cgrates/cgrates/utils" ) +var testIntegration = flag.Bool("integration", false, "Perform the tests in integration mode, not by default.") // This flag will be passed here via "go test -local" args +var waitRater = flag.Int("wait_rater", 150, "Number of miliseconds to wait for rater to start and cache") +var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") + +var daCfgPath string +var daCfg *config.CGRConfig +var smgRPC *rpc.Client +var err error + func TestSMGInitCfg(t *testing.T) { if !*testIntegration { return From c40efaad382fbb7c308505e5e076aa8f61d09465 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 13:31:51 +0200 Subject: [PATCH 13/26] allow negative in test account --- data/tariffplans/testtp/AccountActions.csv | 2 +- data/tariffplans/testtp/Rates.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/tariffplans/testtp/AccountActions.csv b/data/tariffplans/testtp/AccountActions.csv index 4b6dddf29..4cf06f896 100644 --- a/data/tariffplans/testtp/AccountActions.csv +++ b/data/tariffplans/testtp/AccountActions.csv @@ -5,4 +5,4 @@ cgrates.org,1003,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1004,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1005,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1009,TEST_EXE,,, -cgrates.org,1010,TEST_DATA_r,,, +cgrates.org,1010,TEST_DATA_r,,true, diff --git a/data/tariffplans/testtp/Rates.csv b/data/tariffplans/testtp/Rates.csv index bff768bf1..21c9abc4a 100644 --- a/data/tariffplans/testtp/Rates.csv +++ b/data/tariffplans/testtp/Rates.csv @@ -2,4 +2,4 @@ RT_1CENT,0,1,1s,1s,0s RT_DATA_2c,0,0.002,10,10,0 RT_SMS_5c,0,0.005,1,1,0 -RT_DATA_r,0,0.19,1048576,10240,0 +RT_DATA_r,0,0.1,1048576,10240,0 From ca2375aba4b26becc2e426f03bfc564dfba9c3af Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 15:49:08 +0200 Subject: [PATCH 14/26] data monetary balance --- data/tariffplans/testtp/Actions.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 0f478dd4f..666038a38 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -5,4 +5,5 @@ LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 +TOPUP_DATA_r,*topup,,,,*monetary,*out,,DATA_DEST,,,*unlimited,,5000000,10,false,false,10 TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,datar,,*unlimited,,50000000000,10,false,false,10 \ No newline at end of file From a1118d24236fea8e57245494a8ceedaa96ae98a6 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 21:52:43 +0200 Subject: [PATCH 15/26] usage corrections --- sessionmanager/data_it_test.go | 177 ++++++++++++++++++++++++++++++++- sessionmanager/smg_session.go | 2 +- 2 files changed, 175 insertions(+), 4 deletions(-) diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index d3804dfcf..826f8653c 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -115,7 +115,7 @@ func TestSMGDataLastUsedData(t *testing.T) { if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998951424.000000 + eAcntVal = 49998945280.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -141,7 +141,7 @@ func TestSMGDataLastUsedData(t *testing.T) { if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998931424.000000 + eAcntVal = 49998924800.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -164,7 +164,178 @@ func TestSMGDataLastUsedData(t *testing.T) { if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 49998931424.000000 + eAcntVal = 49999979520.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } +} + +func TestSMGDataLastUsedMultipleData(t *testing.T) { + if !*testIntegration { + return + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} + eAcntVal := 49999979520.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:49", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "1048576", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998924800.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "1048576", + utils.LastUsed: "20000", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998904320.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "1048576", + utils.LastUsed: "20000", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998883840.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "1048576", + utils.LastUsed: "20000", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998904320.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.USAGE: "1048576", + utils.LastUsed: "20000", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998883840.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.LastUsed: "0", + } + var rpl string + if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { + t.Error(err) + } + eAcntVal = 49999948800.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 936f4ac56..6eace1506 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -88,7 +88,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D // apply the lastUsed correction dur += lastUsedCorrection - self.totalUsage += dur // Should reflect the total usage so far + self.totalUsage += lastUsed // Should reflect the total usage so far } else { // apply correction from previous run dur -= self.extraDuration From 4f87aae12af39af1c1cdf66d6e0e85b993d729bf Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 23 Mar 2016 21:56:59 +0200 Subject: [PATCH 16/26] test fixes --- sessionmanager/data_it_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index 826f8653c..69b6d95a5 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -285,7 +285,7 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) { if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998904320.000000 + eAcntVal = 49998863360.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -311,7 +311,7 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) { if maxUsage != 1.048576e+06 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998883840.000000 + eAcntVal = 49998842880.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -335,7 +335,7 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) { if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 49999948800.000000 + eAcntVal = 49999897600.000000 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { From bf745d1ea5bded866da735e3975b2821c7c00a56 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 24 Mar 2016 13:17:38 +0200 Subject: [PATCH 17/26] add cgdr usage in lastusage scenario --- sessionmanager/smg_session.go | 1 + sessionmanager/smgeneric.go | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 6eace1506..53a655db6 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -225,6 +225,7 @@ func (self *SMGSession) saveOperations() error { } firstCC := self.callCosts[0] // was merged in close method firstCC.Round() + self.totalUsage = time.Duration(firstCC.RatedUsage) // save final usage //utils.Logger.Debug("Saved CC: " + utils.ToJSON(firstCC)) roundIncrements := firstCC.GetRoundIncrements() if len(roundIncrements) != 0 { diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index e80417211..ff5931434 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -333,8 +333,18 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } func (self *SMGeneric) ProcessCdr(gev SMGenericEvent) error { + cdr := gev.AsStoredCdr(self.cgrCfg, self.timezone) + if cdr.Usage == 0 { + var s *SMGSession + for _, s = range self.getSession(gev.GetUUID()) { + break + } + if s != nil { + cdr.Usage = s.TotalUsage() + } + } var reply string - if err := self.cdrsrv.ProcessCdr(gev.AsStoredCdr(self.cgrCfg, self.timezone), &reply); err != nil { + if err := self.cdrsrv.ProcessCdr(cdr, &reply); err != nil { return err } return nil From 73286e3c7184c2cd2d83ef591e654c397219c73d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 24 Mar 2016 13:24:31 +0200 Subject: [PATCH 18/26] update integration test --- apier/v1/apier_local_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 47da716b5..88ad8c819 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1294,10 +1294,10 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) } else { - if rcvStats.Destinations != 4 || - rcvStats.RatingPlans != 3 || - rcvStats.RatingProfiles != 3 || - rcvStats.Actions != 6 || + if rcvStats.Destinations != 5 || + rcvStats.RatingPlans != 4 || + rcvStats.RatingProfiles != 4 || + rcvStats.Actions != 7 || rcvStats.DerivedChargers != 2 { t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats) } From 4ff66e088378f762aefb199db7ff932e6e2bf7d9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 24 Mar 2016 17:39:47 +0200 Subject: [PATCH 19/26] populate cdr Usage --- apier/v1/cdrs.go | 8 +++--- data/storage/mysql/create_cdrs_tables.sql | 1 + data/storage/postgres/create_cdrs_tables.sql | 1 + engine/cdrs.go | 16 +++++++++--- engine/guardian_test.go | 20 ++++++++------- engine/models.go | 1 + engine/pubsub.go | 4 +-- engine/storage_cdrs_it_test.go | 8 +++--- engine/storage_interface.go | 4 +-- engine/storage_mongo_stordb.go | 8 +++--- engine/storage_sql.go | 27 ++++++++++++-------- engine/storage_utils.go | 1 + sessionmanager/smg_session.go | 2 +- sessionmanager/smgeneric.go | 13 ++-------- 14 files changed, 63 insertions(+), 51 deletions(-) diff --git a/apier/v1/cdrs.go b/apier/v1/cdrs.go index fe52b49ab..39dcd5378 100644 --- a/apier/v1/cdrs.go +++ b/apier/v1/cdrs.go @@ -26,19 +26,19 @@ import ( ) // Retrieves the callCost out of CGR logDb -func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine.CallCost) error { +func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine.SMCost) error { if attrs.CgrId == "" { return utils.NewErrMandatoryIeMissing("CgrId") } if attrs.RunId == "" { attrs.RunId = utils.META_DEFAULT } - if cc, err := apier.CdrDb.GetCallCostLog(attrs.CgrId, attrs.RunId); err != nil { + if smc, err := apier.CdrDb.GetCallCostLog(attrs.CgrId, attrs.RunId); err != nil { return utils.NewErrServerError(err) - } else if cc == nil { + } else if smc == nil { return utils.ErrNotFound } else { - *reply = *cc + *reply = *smc } return nil } diff --git a/data/storage/mysql/create_cdrs_tables.sql b/data/storage/mysql/create_cdrs_tables.sql index 3f5e0269d..463ea922e 100644 --- a/data/storage/mysql/create_cdrs_tables.sql +++ b/data/storage/mysql/create_cdrs_tables.sql @@ -42,6 +42,7 @@ CREATE TABLE sm_costs ( cgrid char(40) NOT NULL, run_id varchar(64) NOT NULL, cost_source varchar(64) NOT NULL, + `usage` DECIMAL(30,9) NOT NULL, cost_details text, created_at TIMESTAMP, deleted_at TIMESTAMP, diff --git a/data/storage/postgres/create_cdrs_tables.sql b/data/storage/postgres/create_cdrs_tables.sql index d4425fb89..2c710dc4e 100644 --- a/data/storage/postgres/create_cdrs_tables.sql +++ b/data/storage/postgres/create_cdrs_tables.sql @@ -45,6 +45,7 @@ CREATE TABLE sm_costs ( cgrid CHAR(40) NOT NULL, run_id VARCHAR(64) NOT NULL, cost_source VARCHAR(64) NOT NULL, + usage NUMERIC(30,9) NOT NULL, cost_details jsonb, created_at TIMESTAMP, deleted_at TIMESTAMP, diff --git a/engine/cdrs.go b/engine/cdrs.go index 27cd49dc6..e8b7de17d 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -38,6 +38,7 @@ type CallCostLog struct { CgrId string Source string RunId string + Usage float64 // real usage (not increment rounded) CallCost *CallCost CheckDuplicate bool } @@ -119,11 +120,11 @@ func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { if cc != nil { return nil, utils.ErrExists } - return nil, self.cdrDb.LogCallCost(ccl.CgrId, ccl.RunId, ccl.Source, ccl.CallCost) + return nil, self.cdrDb.LogCallCost(&SMCost{CGRID: ccl.CgrId, RunID: ccl.RunId, CostSource: ccl.Source, Usage: ccl.Usage, CostDetails: ccl.CallCost}) }, 0, ccl.CgrId) return err } - return self.cdrDb.LogCallCost(ccl.CgrId, ccl.RunId, ccl.Source, ccl.CallCost) + return self.cdrDb.LogCallCost(&SMCost{CGRID: ccl.CgrId, RunID: ccl.RunId, CostSource: ccl.Source, Usage: ccl.Usage, CostDetails: ccl.CallCost}) } // Called by rate/re-rate API @@ -332,12 +333,16 @@ func (self *CdrServer) rateCDR(cdr *CDR) error { if cdr.RequestType == utils.META_NONE { return nil } - if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, cdr.RequestType) && cdr.Usage != 0 { // ToDo: Get rid of PREPAID as soon as we don't want to support it backwards + _, hasLastUsed := cdr.ExtraFields["LastUsed"] + if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, cdr.RequestType) && (cdr.Usage != 0 || hasLastUsed) { // ToDo: Get rid of PREPAID as soon as we don't want to support it backwards // Should be previously calculated and stored in DB delay := utils.Fib() + var usage float64 for i := 0; i < 4; i++ { - qryCC, err = self.cdrDb.GetCallCostLog(cdr.CGRID, cdr.RunID) + qrySMC, err := self.cdrDb.GetCallCostLog(cdr.CGRID, cdr.RunID) if err == nil { + qryCC = qrySMC.CostDetails + usage = qrySMC.Usage break } time.Sleep(delay()) @@ -346,6 +351,9 @@ func (self *CdrServer) rateCDR(cdr *CDR) error { utils.Logger.Warning(fmt.Sprintf(" WARNING: Could not find CallCostLog for cgrid: %s, source: %s, runid: %s, will recalculate", cdr.CGRID, utils.SESSION_MANAGER_SOURCE, cdr.RunID)) qryCC, err = self.getCostFromRater(cdr) } + if cdr.Usage == 0 { + cdr.Usage = time.Duration(usage) + } } else { qryCC, err = self.getCostFromRater(cdr) diff --git a/engine/guardian_test.go b/engine/guardian_test.go index a8e3cc4f8..6e436e9d5 100644 --- a/engine/guardian_test.go +++ b/engine/guardian_test.go @@ -19,31 +19,33 @@ along with this program. If not, see package engine import ( - "log" "testing" "time" ) -func ATestAccountLock(t *testing.T) { +func BenchmarkGuard(b *testing.B) { for i := 0; i < 100; i++ { go Guardian.Guard(func() (interface{}, error) { - log.Print("first 1") time.Sleep(1 * time.Millisecond) - log.Print("end first 1") return 0, nil }, 0, "1") go Guardian.Guard(func() (interface{}, error) { - log.Print("first 2") time.Sleep(1 * time.Millisecond) - log.Print("end first 2") return 0, nil }, 0, "2") go Guardian.Guard(func() (interface{}, error) { - log.Print("second 1") time.Sleep(1 * time.Millisecond) - log.Print("end second 1") return 0, nil }, 0, "1") } - time.Sleep(10 * time.Second) + +} + +func BenchmarkGuardian(b *testing.B) { + for i := 0; i < 100; i++ { + go Guardian.Guard(func() (interface{}, error) { + time.Sleep(1 * time.Millisecond) + return 0, nil + }, 0, "1") + } } diff --git a/engine/models.go b/engine/models.go index 85ce94362..1da2db987 100644 --- a/engine/models.go +++ b/engine/models.go @@ -452,6 +452,7 @@ type TBLSMCosts struct { Cgrid string RunID string CostSource string + Usage float64 CostDetails string CreatedAt time.Time DeletedAt time.Time diff --git a/engine/pubsub.go b/engine/pubsub.go index 2ae544df0..0d4dd17c1 100644 --- a/engine/pubsub.go +++ b/engine/pubsub.go @@ -139,13 +139,13 @@ func (ps *PubSub) Publish(evt CgrEvent, reply *string) error { } transport := split[0] address := split[1] - + ttlVerify := ps.ttlVerify switch transport { case utils.META_HTTP_POST: go func() { delay := utils.Fib() for i := 0; i < 5; i++ { // Loop so we can increase the success rate on best effort - if _, err := ps.pubFunc(address, ps.ttlVerify, evt); err == nil { + if _, err := ps.pubFunc(address, ttlVerify, evt); err == nil { break // Success, no need to reinterate } else if i == 4 { // Last iteration, syslog the warning utils.Logger.Warning(fmt.Sprintf(" Failed calling url: [%s], error: [%s], event type: %s", address, err.Error(), evt["EventName"])) diff --git a/engine/storage_cdrs_it_test.go b/engine/storage_cdrs_it_test.go index f264ec237..4e934d4e4 100644 --- a/engine/storage_cdrs_it_test.go +++ b/engine/storage_cdrs_it_test.go @@ -222,13 +222,13 @@ func testSMCosts(cfg *config.CGRConfig) error { }, TOR: utils.VOICE, } - if err := cdrStorage.LogCallCost("164b0422fdc6a5117031b427439482c6a4f90e41", utils.META_DEFAULT, utils.UNIT_TEST, cc); err != nil { + if err := cdrStorage.LogCallCost(&SMCost{CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", RunID: utils.META_DEFAULT, CostSource: utils.UNIT_TEST, CostDetails: cc}); err != nil { return err } - if rcvCC, err := cdrStorage.GetCallCostLog("164b0422fdc6a5117031b427439482c6a4f90e41", utils.META_DEFAULT); err != nil { + if rcvSMC, err := cdrStorage.GetCallCostLog("164b0422fdc6a5117031b427439482c6a4f90e41", utils.META_DEFAULT); err != nil { return err - } else if len(cc.Timespans) != len(rcvCC.Timespans) { // cc.Timespans[0].RateInterval.Rating.Rates[0], rcvCC.Timespans[0].RateInterval.Rating.Rates[0]) - return fmt.Errorf("Expecting: %+v, received: %+v", cc, rcvCC) + } else if len(cc.Timespans) != len(rcvSMC.CostDetails.Timespans) { // cc.Timespans[0].RateInterval.Rating.Rates[0], rcvCC.Timespans[0].RateInterval.Rating.Rates[0]) + return fmt.Errorf("Expecting: %+v, received: %+s", cc, utils.ToIJSON(rcvSMC)) } return nil } diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 8d082ae14..95d2f8d0e 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -97,8 +97,8 @@ type AccountingStorage interface { type CdrStorage interface { Storage SetCDR(*CDR, bool) error - LogCallCost(cgrid, runid, source string, cc *CallCost) error - GetCallCostLog(cgrid, runid string) (*CallCost, error) + LogCallCost(smc *SMCost) error + GetCallCostLog(cgrid, runid string) (*SMCost, error) GetCDRs(*utils.CDRsFilter, bool) ([]*CDR, int64, error) } diff --git a/engine/storage_mongo_stordb.go b/engine/storage_mongo_stordb.go index 0e0090def..a0aaffe09 100644 --- a/engine/storage_mongo_stordb.go +++ b/engine/storage_mongo_stordb.go @@ -698,16 +698,16 @@ func (ms *MongoStorage) LogActionTiming(source string, at *ActionTiming, as Acti }{at, as, time.Now(), source}) } -func (ms *MongoStorage) LogCallCost(cgrid, runid, source string, cc *CallCost) error { - return ms.db.C(utils.TBLSMCosts).Insert(&SMCost{CGRID: cgrid, RunID: runid, CostSource: source, CostDetails: cc}) +func (ms *MongoStorage) LogCallCost(smc *SMCost) error { + return ms.db.C(utils.TBLSMCosts).Insert(smc) } -func (ms *MongoStorage) GetCallCostLog(cgrid, runid string) (cc *CallCost, err error) { +func (ms *MongoStorage) GetCallCostLog(cgrid, runid string) (smc *SMCost, err error) { var result SMCost if err = ms.db.C(utils.TBLSMCosts).Find(bson.M{CGRIDLow: cgrid, RunIDLow: runid}).One(&result); err != nil { return nil, err } - return result.CostDetails, nil + return &result, nil } func (ms *MongoStorage) SetCDR(cdr *CDR, allowUpdate bool) (err error) { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index e7796e34f..cac8c86fc 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -569,21 +569,22 @@ func (self *SQLStorage) SetTpAccountActions(aas []TpAccountAction) error { return nil } -func (self *SQLStorage) LogCallCost(cgrid, runid, source string, cc *CallCost) error { - if cc == nil { +func (self *SQLStorage) LogCallCost(smc *SMCost) error { + if smc.CostDetails == nil { return nil } - tss, err := json.Marshal(cc) + tss, err := json.Marshal(smc.CostDetails) if err != nil { utils.Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err)) return err } tx := self.db.Begin() cd := &TBLSMCosts{ - Cgrid: cgrid, - RunID: runid, - CostSource: source, + Cgrid: smc.CGRID, + RunID: smc.RunID, + CostSource: smc.CostSource, CostDetails: string(tss), + Usage: smc.Usage, CreatedAt: time.Now(), } if tx.Save(cd).Error != nil { // Check further since error does not properly reflect duplicates here (sql: no rows in result set) @@ -594,7 +595,7 @@ func (self *SQLStorage) LogCallCost(cgrid, runid, source string, cc *CallCost) e return nil } -func (self *SQLStorage) GetCallCostLog(cgrid, runid string) (*CallCost, error) { +func (self *SQLStorage) GetCallCostLog(cgrid, runid string) (*SMCost, error) { var tpCostDetail TBLSMCosts if err := self.db.Where(&TBLSMCosts{Cgrid: cgrid, RunID: runid}).First(&tpCostDetail).Error; err != nil { return nil, err @@ -602,11 +603,17 @@ func (self *SQLStorage) GetCallCostLog(cgrid, runid string) (*CallCost, error) { if len(tpCostDetail.CostDetails) == 0 { return nil, nil // No costs returned } - var cc CallCost - if err := json.Unmarshal([]byte(tpCostDetail.CostDetails), &cc); err != nil { + smc := &SMCost{ + CGRID: tpCostDetail.Cgrid, + RunID: tpCostDetail.RunID, + CostSource: tpCostDetail.CostSource, + Usage: tpCostDetail.Usage, + CostDetails: &CallCost{}, + } + if err := json.Unmarshal([]byte(tpCostDetail.CostDetails), smc.CostDetails); err != nil { return nil, err } - return &cc, nil + return smc, nil } func (self *SQLStorage) LogActionTrigger(ubId, source string, at *ActionTrigger, as Actions) (err error) { diff --git a/engine/storage_utils.go b/engine/storage_utils.go index 8994020fd..16012f2e1 100644 --- a/engine/storage_utils.go +++ b/engine/storage_utils.go @@ -157,5 +157,6 @@ type SMCost struct { CGRID string RunID string CostSource string + Usage float64 CostDetails *CallCost } diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 53a655db6..46ade6019 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -225,7 +225,6 @@ func (self *SMGSession) saveOperations() error { } firstCC := self.callCosts[0] // was merged in close method firstCC.Round() - self.totalUsage = time.Duration(firstCC.RatedUsage) // save final usage //utils.Logger.Debug("Saved CC: " + utils.ToJSON(firstCC)) roundIncrements := firstCC.GetRoundIncrements() if len(roundIncrements) != 0 { @@ -242,6 +241,7 @@ func (self *SMGSession) saveOperations() error { CgrId: self.eventStart.GetCgrId(self.timezone), Source: utils.SESSION_MANAGER_SOURCE, RunId: self.runId, + Usage: float64(self.totalUsage), CallCost: firstCC, CheckDuplicate: true, }, &reply) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index ff5931434..2f16b2f71 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -117,6 +117,7 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error { return nil, nil // Did not find the session so no need to close it anymore } for idx, s := range ss { + s.totalUsage = usage // save final usage as totalUsage //utils.Logger.Info(fmt.Sprintf(" Ending session: %s, runId: %s", sessionId, s.runId)) if idx == 0 && s.stopDebit != nil { close(s.stopDebit) // Stop automatic debits @@ -333,18 +334,8 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } func (self *SMGeneric) ProcessCdr(gev SMGenericEvent) error { - cdr := gev.AsStoredCdr(self.cgrCfg, self.timezone) - if cdr.Usage == 0 { - var s *SMGSession - for _, s = range self.getSession(gev.GetUUID()) { - break - } - if s != nil { - cdr.Usage = s.TotalUsage() - } - } var reply string - if err := self.cdrsrv.ProcessCdr(cdr, &reply); err != nil { + if err := self.cdrsrv.ProcessCdr(gev.AsStoredCdr(self.cgrCfg, self.timezone), &reply); err != nil { return err } return nil From 7a4b3f24af8b67f6f67664cab1d90bb2eed200e3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 24 Mar 2016 18:27:16 +0200 Subject: [PATCH 20/26] don't skip LastUsed in extra fields' --- sessionmanager/smg_event.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index 643c8693f..ced2b5e0c 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -228,7 +228,7 @@ func (self SMGenericEvent) GetCdrSource() string { func (self SMGenericEvent) GetExtraFields() map[string]string { extraFields := make(map[string]string) for key, val := range self { - primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME, utils.LastUsed) + primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME) if utils.IsSliceMember(primaryFields, key) { continue } From 29bd0c5dbb18488647db63ae92b2f39471fe4e10 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 24 Mar 2016 20:13:04 +0100 Subject: [PATCH 21/26] Diameter - fix metaSum to consider filter indexes --- agents/libdmt.go | 6 ++-- agents/libdmt_test.go | 81 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/agents/libdmt.go b/agents/libdmt.go index bb60b4f75..f3ee75dee 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -244,8 +244,8 @@ func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimal return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil } -func metaSum(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) { - valStr := composedFieldvalue(m, argsTpl, 0, nil) +func metaSum(m *diam.Message, argsTpl utils.RSRFields, passAtIndex, roundingDecimals int) (string, error) { + valStr := composedFieldvalue(m, argsTpl, passAtIndex, nil) handlerArgs := strings.Split(valStr, utils.HandlerArgSep) var summed float64 for _, arg := range handlerArgs { @@ -398,7 +398,7 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa case META_VALUE_EXPONENT: outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals case META_SUM: - outVal, err = metaSum(m, cfgFld.Value, 10) + outVal, err = metaSum(m, cfgFld.Value, passAtIndex, 10) default: outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) if err != nil { diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index 7d6924c42..5bb1d7c01 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -27,6 +27,7 @@ import ( "time" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/sessionmanager" "github.com/cgrates/cgrates/utils" "github.com/fiorix/go-diameter/diam" "github.com/fiorix/go-diameter/diam/avp" @@ -133,12 +134,12 @@ func TestMetaSum(t *testing.T) { }), }, }) - if val, err := metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil { + if val, err := metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 0, 10); err != nil { t.Error(err) } else if val != "9995" { t.Error("Received: ", val) } - if _, err = metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil { + if _, err = metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 0, 10); err == nil { t.Error("Should have received error") // Insufficient number arguments } } @@ -396,3 +397,79 @@ func TestCCASetProcessorAVPs(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eMessage, ccaMsg) } } + +func TestCCRAsSMGenericEvent(t *testing.T) { + ccr := &CCR{ // Bare information, just the one needed for answer + SessionId: "ccrasgen1", + AuthApplicationId: 4, + CCRequestType: 3, + } + ccr.diamMessage = ccr.AsBareDiameterMessage() + ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit + AVP: []*diam.AVP{ + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(17)), // CC-Time + diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1341)), // CC-Input-Octets + diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(3079)), // CC-Output-Octets + }, + }), + diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)), + }, + }) + ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit + AVP: []*diam.AVP{ + diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time + diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(8046)), // CC-Input-Octets + diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(46193)), // CC-Output-Octets + }, + }), + diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)), + }, + }) + ccr.diamMessage.NewAVP("FramedIPAddress", avp.Mbit, 0, datatype.OctetString("0AE40041")) + cfgFlds := make([]*config.CfgCdrField, 0) + eSMGEv := sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR"} + if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { + t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) + } + cfgFlds = []*config.CfgCdrField{ + &config.CfgCdrField{ + Tag: "LastUsed", + FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(1)", utils.INFIELD_SEP), + FieldId: "LastUsed", + Type: "*handler", + HandlerId: "*sum", + Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), + Mandatory: true, + }, + } + eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "54239"} + if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { + t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) + } + cfgFlds = []*config.CfgCdrField{ + &config.CfgCdrField{ + Tag: "LastUsed", + FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(99)", utils.INFIELD_SEP), + FieldId: "LastUsed", + Type: "*handler", + HandlerId: "*sum", + Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), + Mandatory: true, + }, + } + eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "4420"} + if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { + t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) + } +} From 069af112a6f5a4b543cc72be8c7801582d9077cf Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 24 Mar 2016 20:19:09 +0100 Subject: [PATCH 22/26] Fix SMGEventTest --- sessionmanager/smg_event_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index 4cc80781c..5cc65583b 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -127,7 +127,7 @@ func TestSMGenericEventParseFields(t *testing.T) { if smGev.GetOriginatorIP(utils.META_DEFAULT) != "127.0.0.1" { t.Error("Unexpected: ", smGev.GetOriginatorIP(utils.META_DEFAULT)) } - if extrFlds := smGev.GetExtraFields(); !reflect.DeepEqual(extrFlds, map[string]string{"Extra1": "Value1", "Extra2": "5"}) { + if extrFlds := smGev.GetExtraFields(); !reflect.DeepEqual(extrFlds, map[string]string{"Extra1": "Value1", "Extra2": "5", "LastUsed": "21s"}) { t.Error("Unexpected: ", extrFlds) } } From 393d893ef7505368d0fec5bdae3f21d68756008f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 25 Mar 2016 15:11:02 +0200 Subject: [PATCH 23/26] make tests use glide novendor --- .travis.yml | 2 +- local_test.sh | 31 ++----------------------------- test.sh | 50 ++------------------------------------------------ 3 files changed, 5 insertions(+), 78 deletions(-) diff --git a/.travis.yml b/.travis.yml index 899b7c359..9b4f37383 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ install: - go get github.com/Masterminds/glide - glide install -script: $TRAVIS_BUILD_DIR/test.sh +script: go test -v $(glide novendor) branches: only: master diff --git a/local_test.sh b/local_test.sh index 05577d629..4218bd3bd 100755 --- a/local_test.sh +++ b/local_test.sh @@ -1,32 +1,5 @@ #! /usr/bin/env sh ./test.sh gen=$? -echo 'go test github.com/cgrates/cgrates/apier/v1 -local' -go test github.com/cgrates/cgrates/apier/v1 -local -ap1=$? -echo 'go test github.com/cgrates/cgrates/apier/v2 -local' -go test github.com/cgrates/cgrates/apier/v2 -local -ap2=$? -echo 'go test github.com/cgrates/cgrates/engine -local -integration' -go test github.com/cgrates/cgrates/engine -local -integration -en=$? -echo 'go test github.com/cgrates/cgrates/cdrc -local' -go test github.com/cgrates/cgrates/cdrc -local -cdrc=$? -echo 'go test github.com/cgrates/cgrates/config -local' -go test github.com/cgrates/cgrates/config -local -cfg=$? -echo 'go test github.com/cgrates/cgrates/utils -local' -go test github.com/cgrates/cgrates/utils -local -utl=$? -echo 'go test github.com/cgrates/cgrates/general_tests -local -integration' -go test github.com/cgrates/cgrates/general_tests -local -integration -gnr=$? -echo 'go test github.com/cgrates/cgrates/agents -integration' -go test github.com/cgrates/cgrates/agents -integration -agts=$? -echo 'go test github.com/cgrates/cgrates/sessionmanager -integration' -go test github.com/cgrates/cgrates/sessionmanager -integration -smg=$? - -exit $gen && $ap1 && $ap2 && $en && $cdrc && $cfg && $utl && $gnr && $agts && $smg +go test -local -integration $(glide novendor) +exit $gen && $? diff --git a/test.sh b/test.sh index 3a4a57e95..8c6ab0558 100755 --- a/test.sh +++ b/test.sh @@ -1,50 +1,4 @@ #! /usr/bin/env sh ./build.sh - -go test -i github.com/cgrates/cgrates/apier/v1 -go test -i github.com/cgrates/cgrates/apier/v2 -go test -i github.com/cgrates/cgrates/engine -go test -i github.com/cgrates/cgrates/sessionmanager -go test -i github.com/cgrates/cgrates/config -go test -i github.com/cgrates/cgrates/cmd/cgr-engine -go test -i github.com/cgrates/cgrates/cache2go -go test -i github.com/cgrates/cgrates/cdrc -go test -i github.com/cgrates/cgrates/utils -go test -i github.com/cgrates/cgrates/history -go test -i github.com/cgrates/cgrates/cdre -go test -i github.com/cgrates/cgrates/agents -go test -i github.com/cgrates/cgrates/structmatcher - -go test github.com/cgrates/cgrates/apier/v1 -v1=$? -go test github.com/cgrates/cgrates/apier/v2 -v2=$? -go test github.com/cgrates/cgrates/engine -en=$? -go test github.com/cgrates/cgrates/general_tests -gt=$? -go test github.com/cgrates/cgrates/sessionmanager -sm=$? -go test github.com/cgrates/cgrates/config -cfg=$? -go test github.com/cgrates/cgrates/cmd/cgr-engine -cr=$? -go test github.com/cgrates/cgrates/console -con=$? -go test github.com/cgrates/cgrates/cdrc -cdrcs=$? -go test github.com/cgrates/cgrates/utils -ut=$? -go test github.com/cgrates/cgrates/history -hs=$? -go test github.com/cgrates/cgrates/cache2go -c2g=$? -go test github.com/cgrates/cgrates/cdre -cdre=$? -go test github.com/cgrates/cgrates/agents -ag=$? -go test github.com/cgrates/cgrates/structmatcher -sc=$? - - -exit $v1 && $v2 && $en && $gt && $sm && $cfg && $bl && $cr && $con && $cdrc && $ut && $hs && $c2g && $cdre && $ag && $sc +go test $(glide novendor) +exit $? From 7721a99edd5efa501ca1f186e7e3ebce7292ef4a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 25 Mar 2016 15:32:21 +0200 Subject: [PATCH 24/26] fix GetAccounts with mongo --- apier/v1/accounts.go | 2 +- apier/v2/accounts.go | 2 +- data/conf/samples/cgradmin/cgradmin.json | 26 +++++------ engine/storage_interface.go | 2 +- engine/storage_map.go | 15 +++--- engine/storage_mongo_datadb.go | 58 ++++++++++++++++++++++-- engine/storage_redis.go | 13 ++++-- engine/storage_sql.go | 2 +- engine/tp_reader.go | 2 +- 9 files changed, 90 insertions(+), 32 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 6d006da25..c6c161331 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -318,7 +318,7 @@ func (self *ApierV1) GetAccounts(attr utils.AttrGetAccounts, reply *[]interface{ var accountKeys []string var err error if len(attr.AccountIds) == 0 { - if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX + attr.Tenant); err != nil { + if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+attr.Tenant, true); err != nil { return err } } else { diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index c3c495edd..08f6832dd 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -33,7 +33,7 @@ func (self *ApierV2) GetAccounts(attr utils.AttrGetAccounts, reply *[]*engine.Ac var accountKeys []string var err error if len(attr.AccountIds) == 0 { - if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX + utils.ConcatenatedKey(attr.Tenant)); err != nil { + if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+utils.ConcatenatedKey(attr.Tenant), true); err != nil { return err } } else { diff --git a/data/conf/samples/cgradmin/cgradmin.json b/data/conf/samples/cgradmin/cgradmin.json index 2f6f9e4a1..9dd30abcf 100644 --- a/data/conf/samples/cgradmin/cgradmin.json +++ b/data/conf/samples/cgradmin/cgradmin.json @@ -10,19 +10,19 @@ "http": ":2080", // HTTP listening address }, -//"tariffplan_db": { // database used to store offline tariff plans and CDRs -// "db_type": "mongo", // stor database type to use: -// "db_host": "127.0.0.1", // the host to connect to -// "db_port": 27017, // the port to reach the stordb -// "db_name": "tpdb", -//}, -// -//"data_db": { // database used to store offline tariff plans and CDRs -// "db_type": "mongo", // stor database type to use: -// "db_host": "127.0.0.1", // the host to connect to -// "db_port": 27017, // the port to reach the stordb -// "db_name": "datadb", -//}, +"tariffplan_db": { // database used to store offline tariff plans and CDRs + "db_type": "mongo", // stor database type to use: + "db_host": "127.0.0.1", // the host to connect to + "db_port": 27017, // the port to reach the stordb + "db_name": "tpdb", +}, + +"data_db": { // database used to store offline tariff plans and CDRs + "db_type": "mongo", // stor database type to use: + "db_host": "127.0.0.1", // the host to connect to + "db_port": 27017, // the port to reach the stordb + "db_name": "datadb", +}, "stor_db": { // database used to store offline tariff plans and CDRs "db_type": "mongo", // stor database type to use: diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 95d2f8d0e..dbf8a8632 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -33,7 +33,7 @@ import ( type Storage interface { Close() Flush(string) error - GetKeysForPrefix(string) ([]string, error) + GetKeysForPrefix(string, bool) ([]string, error) } // Interface for storage providers. diff --git a/engine/storage_map.go b/engine/storage_map.go index 0e77e3ae1..5a898fa32 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -53,14 +53,17 @@ func (ms *MapStorage) Flush(ignore string) error { return nil } -func (ms *MapStorage) GetKeysForPrefix(prefix string) ([]string, error) { - keysForPrefix := make([]string, 0) - for key := range ms.dict { - if strings.HasPrefix(key, prefix) { - keysForPrefix = append(keysForPrefix, key) +func (ms *MapStorage) GetKeysForPrefix(prefix string, skipCache bool) ([]string, error) { + if skipCache { + keysForPrefix := make([]string, 0) + for key := range ms.dict { + if strings.HasPrefix(key, prefix) { + keysForPrefix = append(keysForPrefix, key) + } } + return keysForPrefix, nil } - return keysForPrefix, nil + return cache2go.GetEntriesKeys(prefix), nil } func (ms *MapStorage) CacheRatingAll() error { diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 4fb29e84b..a208ce6cb 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -244,8 +244,60 @@ func (ms *MongoStorage) Close() { ms.session.Close() } -func (ms *MongoStorage) GetKeysForPrefix(prefix string) ([]string, error) { - return nil, nil +func (ms *MongoStorage) GetKeysForPrefix(prefix string, skipCache bool) ([]string, error) { + var category, subject string + length := len(utils.DESTINATION_PREFIX) + if len(prefix) >= length { + category = prefix[:length] // prefix lenght + subject = fmt.Sprintf("^%s", prefix[length:]) + } else { + return nil, fmt.Errorf("unsupported prefix in GetKeysForPrefix: %s", prefix) + } + var result []string + if skipCache { + keyResult := struct{ Key string }{} + idResult := struct{ Id string }{} + switch category { + case utils.DESTINATION_PREFIX: + iter := ms.db.C(colDst).Find(bson.M{"key": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"key": 1}).Iter() + for iter.Next(&keyResult) { + result = append(result, utils.DESTINATION_PREFIX+keyResult.Key) + } + return result, nil + case utils.RATING_PLAN_PREFIX: + iter := ms.db.C(colRpl).Find(bson.M{"key": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"key": 1}).Iter() + for iter.Next(&keyResult) { + result = append(result, utils.RATING_PLAN_PREFIX+keyResult.Key) + } + return result, nil + case utils.RATING_PROFILE_PREFIX: + iter := ms.db.C(colRpf).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.RATING_PROFILE_PREFIX+idResult.Id) + } + return result, nil + case utils.ACTION_PREFIX: + iter := ms.db.C(colAct).Find(bson.M{"key": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"key": 1}).Iter() + for iter.Next(&keyResult) { + result = append(result, utils.ACTION_PREFIX+keyResult.Key) + } + return result, nil + case utils.ACTION_PLAN_PREFIX: + iter := ms.db.C(colApl).Find(bson.M{"key": bson.M{"$regex": bson.RegEx{Pattern: subject}}}).Select(bson.M{"key": 1}).Iter() + for iter.Next(&keyResult) { + result = append(result, utils.ACTION_PLAN_PREFIX+keyResult.Key) + } + return result, nil + case utils.ACCOUNT_PREFIX: + iter := ms.db.C(colAcc).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.ACCOUNT_PREFIX+idResult.Id) + } + return result, nil + } + return result, fmt.Errorf("unsupported prefix in GetKeysForPrefix: %s", prefix) + } + return cache2go.GetEntriesKeys(prefix), nil } func (ms *MongoStorage) Flush(ignore string) (err error) { @@ -627,7 +679,7 @@ func (ms *MongoStorage) HasData(category, subject string) (bool, error) { count, err := ms.db.C(colAcc).Find(bson.M{"id": subject}).Count() return count > 0, err } - return false, errors.New("Unsupported category in HasData") + return false, errors.New("unsupported category in HasData") } func (ms *MongoStorage) GetRatingPlan(key string, skipCache bool) (rp *RatingPlan, err error) { diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 52d4bb3a7..8d8b1660a 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -84,12 +84,15 @@ func (rs *RedisStorage) Flush(ignore string) error { return rs.db.Cmd("FLUSHDB").Err } -func (rs *RedisStorage) GetKeysForPrefix(prefix string) ([]string, error) { - r := rs.db.Cmd("KEYS", prefix+"*") - if r.Err != nil { - return nil, r.Err +func (rs *RedisStorage) GetKeysForPrefix(prefix string, skipCache bool) ([]string, error) { + if skipCache { + r := rs.db.Cmd("KEYS", prefix+"*") + if r.Err != nil { + return nil, r.Err + } + return r.List() } - return r.List() + return cache2go.GetEntriesKeys(prefix), nil } func (rs *RedisStorage) CacheRatingAll() error { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index cac8c86fc..1e0f5368a 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -55,7 +55,7 @@ func (self *SQLStorage) Flush(scriptsPath string) (err error) { return nil } -func (self *SQLStorage) GetKeysForPrefix(prefix string) ([]string, error) { +func (self *SQLStorage) GetKeysForPrefix(prefix string, skipCache bool) ([]string, error) { return nil, utils.ErrNotImplemented } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index bf662a335..e458e130e 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -433,7 +433,7 @@ func (tpr *TpReader) LoadLCRs() (err error) { } } if !found && tpr.ratingStorage != nil { - if keys, err := tpr.ratingStorage.GetKeysForPrefix(utils.RATING_PROFILE_PREFIX + ratingProfileSearchKey); err != nil { + if keys, err := tpr.ratingStorage.GetKeysForPrefix(utils.RATING_PROFILE_PREFIX+ratingProfileSearchKey, true); err != nil { return fmt.Errorf("[LCR] error querying ratingDb %s", err.Error()) } else if len(keys) != 0 { found = true From 4c6514bf4f55a15606068b5cf95fa5bf14279298 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 25 Mar 2016 15:54:57 +0200 Subject: [PATCH 25/26] revert local tests till we found a better solution --- local_test.sh | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/local_test.sh b/local_test.sh index 4218bd3bd..05577d629 100755 --- a/local_test.sh +++ b/local_test.sh @@ -1,5 +1,32 @@ #! /usr/bin/env sh ./test.sh gen=$? -go test -local -integration $(glide novendor) -exit $gen && $? +echo 'go test github.com/cgrates/cgrates/apier/v1 -local' +go test github.com/cgrates/cgrates/apier/v1 -local +ap1=$? +echo 'go test github.com/cgrates/cgrates/apier/v2 -local' +go test github.com/cgrates/cgrates/apier/v2 -local +ap2=$? +echo 'go test github.com/cgrates/cgrates/engine -local -integration' +go test github.com/cgrates/cgrates/engine -local -integration +en=$? +echo 'go test github.com/cgrates/cgrates/cdrc -local' +go test github.com/cgrates/cgrates/cdrc -local +cdrc=$? +echo 'go test github.com/cgrates/cgrates/config -local' +go test github.com/cgrates/cgrates/config -local +cfg=$? +echo 'go test github.com/cgrates/cgrates/utils -local' +go test github.com/cgrates/cgrates/utils -local +utl=$? +echo 'go test github.com/cgrates/cgrates/general_tests -local -integration' +go test github.com/cgrates/cgrates/general_tests -local -integration +gnr=$? +echo 'go test github.com/cgrates/cgrates/agents -integration' +go test github.com/cgrates/cgrates/agents -integration +agts=$? +echo 'go test github.com/cgrates/cgrates/sessionmanager -integration' +go test github.com/cgrates/cgrates/sessionmanager -integration +smg=$? + +exit $gen && $ap1 && $ap2 && $en && $cdrc && $cfg && $utl && $gnr && $agts && $smg From 8b94e0c8e3adfc1aceee7d508772ea344229fb85 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 25 Mar 2016 16:44:05 +0100 Subject: [PATCH 26/26] SMG - SessionRelocate through the use of InitialOriginID --- sessionmanager/smg_event.go | 12 ++++++++++++ sessionmanager/smg_event_test.go | 16 ++++++++++++++++ sessionmanager/smgeneric.go | 30 ++++++++++++++++++++++++++++++ utils/consts.go | 2 ++ 4 files changed, 60 insertions(+) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index ced2b5e0c..890e9a1c9 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -238,6 +238,18 @@ func (self SMGenericEvent) GetExtraFields() map[string]string { return extraFields } +func (self SMGenericEvent) GetFieldAsString(fieldName string) (string, error) { + valIf, hasVal := self[fieldName] + if !hasVal { + return "", utils.ErrNotFound + } + result, converted := utils.ConvertIfaceToString(valIf) + if !converted { + return "", utils.ErrNotConvertible + } + return result, nil +} + func (self SMGenericEvent) MissingParameter(timezone string) bool { switch self.GetName() { case utils.CGR_AUTHORIZATION: diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index 5cc65583b..336996943 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -191,3 +191,19 @@ func TestSMGenericEventAsLcrRequest(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eLcrReq, lcrReq) } } + +func TestSMGenericEventGetFieldAsString(t *testing.T) { + smGev := SMGenericEvent{} + smGev[utils.EVENT_NAME] = "TEST_EVENT" + smGev[utils.TOR] = utils.VOICE + smGev[utils.ACCID] = "12345" + smGev[utils.DIRECTION] = utils.OUT + smGev[utils.ACCOUNT] = "account1" + smGev[utils.SUBJECT] = "subject1" + eFldVal := utils.VOICE + if strVal, err := smGev.GetFieldAsString(utils.TOR); err != nil { + t.Error(err) + } else if strVal != eFldVal { + t.Errorf("Expecting: %s, received: %s", eFldVal, strVal) + } +} diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 2f16b2f71..a5b102a77 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -139,6 +139,27 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error { return err } +// Used when an update will relocate an initial session (eg multiple data streams) +func (self *SMGeneric) sessionRelocate(sessionID, initialID string) error { + _, err := self.guard.Guard(func() (interface{}, error) { // Lock it on initialID level + if utils.IsSliceMember([]string{sessionID, initialID}, "") { + return nil, utils.ErrMandatoryIeMissing + } + ss := self.getSession(initialID) + if len(ss) == 0 { // No need of relocation + return nil, utils.ErrNotFound + } + for i, s := range ss { + self.indexSession(sessionID, s) + if i == 0 { + self.unindexSession(initialID) + } + } + return nil, nil + }, time.Duration(2)*time.Second, initialID) + return err +} + // Methods to apply on sessions, mostly exported through RPC/Bi-RPC //Calculates maximum usage allowed for gevent func (self *SMGeneric) GetMaxUsage(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { @@ -170,6 +191,15 @@ func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([ // Execute debits for usage/maxUsage func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { + if initialID, err := gev.GetFieldAsString(utils.InitialOriginID); err == nil { + err := self.sessionRelocate(gev.GetUUID(), initialID) + if err == utils.ErrNotFound { // Session was already relocated, create a new session with this update + err = self.sessionStart(gev, getClientConnId(clnt)) + } + if err != nil { + return nilDuration, err + } + } evLastUsed, err := gev.GetLastUsed(utils.META_DEFAULT) if err != nil && err != utils.ErrNotFound { return nilDuration, err diff --git a/utils/consts.go b/utils/consts.go index 72c15b145..36001e3c9 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -31,6 +31,7 @@ var ( ErrAccountDisabled = errors.New("ACCOUNT_DISABLED") ErrUserNotFound = errors.New("USER_NOT_FOUND") ErrInsufficientCredit = errors.New("INSUFFICENT_CREDIT") + ErrNotConvertible = errors.New("NOT_CONVERTIBLE") ) const ( @@ -113,6 +114,7 @@ const ( TOR = "ToR" ORDERID = "OrderID" ACCID = "OriginID" + InitialOriginID = "InitialOriginID" CDRSOURCE = "Source" CDRHOST = "OriginHost" REQTYPE = "RequestType"