From 5e06cafd2fc45fde2cc1788501f205910dc39a39 Mon Sep 17 00:00:00 2001 From: DanB Date: Sat, 22 Mar 2014 16:50:25 +0100 Subject: [PATCH] Adding rating aliases, account aliases, shared groups to cache stats struct, improving fs_json tutorial tests and documentation --- apier/apier.go | 11 +++- apier/tutfsjson_local_test.go | 66 +++++++++++++++++-- cdre/fixedwidth.go | 43 +----------- .../fs_json/cgrates/tariffplans/Actions.csv | 4 +- docs/tut_freeswitch_json.rst | 58 ++++++++++++---- pkg/debian/changelog | 2 +- utils/apitpdata.go | 6 ++ utils/consts.go | 1 + 8 files changed, 128 insertions(+), 63 deletions(-) diff --git a/apier/apier.go b/apier/apier.go index 8a20ece25..46040a364 100644 --- a/apier/apier.go +++ b/apier/apier.go @@ -532,6 +532,9 @@ func (self *ApierV1) GetCacheStats(attrs utils.AttrCacheStats, reply *utils.Cach cs.RatingPlans = cache2go.CountEntries(engine.RATING_PLAN_PREFIX) cs.RatingProfiles = cache2go.CountEntries(engine.RATING_PROFILE_PREFIX) cs.Actions = cache2go.CountEntries(engine.ACTION_PREFIX) + cs.SharedGroups = cache2go.CountEntries(engine.SHARED_GROUP_PREFIX) + cs.RatingAliases = cache2go.CountEntries(engine.RP_ALIAS_PREFIX) + cs.AccountAliases = cache2go.CountEntries(engine.ACC_ALIAS_PREFIX) *reply = *cs return nil } @@ -543,7 +546,7 @@ func (self *ApierV1) GetCachedItemAge(itemId string, reply *utils.CachedItemAge) cachedItemAge := new(utils.CachedItemAge) var found bool for idx, cacheKey := range []string{engine.DESTINATION_PREFIX + itemId, engine.RATING_PLAN_PREFIX + itemId, engine.RATING_PROFILE_PREFIX + itemId, - engine.ACTION_PREFIX + itemId} { + engine.ACTION_PREFIX + itemId, engine.SHARED_GROUP_PREFIX + itemId, engine.RP_ALIAS_PREFIX + itemId, engine.ACC_ALIAS_PREFIX + itemId} { if age, err := cache2go.GetKeyAge(cacheKey); err == nil { found = true switch idx { @@ -555,6 +558,12 @@ func (self *ApierV1) GetCachedItemAge(itemId string, reply *utils.CachedItemAge) cachedItemAge.RatingProfile = age case 3: cachedItemAge.Action = age + case 4: + cachedItemAge.SharedGroup = age + case 5: + cachedItemAge.RatingAlias = age + case 6: + cachedItemAge.AccountAlias = age } } } diff --git a/apier/tutfsjson_local_test.go b/apier/tutfsjson_local_test.go index 3d280e83a..264ad5937 100644 --- a/apier/tutfsjson_local_test.go +++ b/apier/tutfsjson_local_test.go @@ -171,7 +171,7 @@ func TestFsJsonLoadTariffPlans(t *testing.T) { } time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 3, RatingPlans: 2, RatingProfiles: 2, Actions: 5} + expectedStats := &utils.CacheStats{Destinations: 3, RatingPlans: 2, RatingProfiles: 2, Actions: 5, SharedGroups: 1, RatingAliases: 1, AccountAliases: 1} var args utils.AttrCacheStats if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) @@ -291,7 +291,7 @@ func TestFsJsonGetAccount1007(t *testing.T) { if acnt.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 0 { t.Errorf("Calling ApierV1.GetBalance expected: 0, received: %f", acnt.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue()) } - if len(acnt.BalanceMap[attrs.BalanceType+attrs.Direction]) != 2 { + if len(acnt.BalanceMap[attrs.BalanceType+attrs.Direction]) != 1 { t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[attrs.BalanceType+attrs.Direction])) } blncLst := acnt.BalanceMap[attrs.BalanceType+attrs.Direction] @@ -378,13 +378,13 @@ func TestMaxCallDuration(t *testing.T) { t.Error(err) } else { remainingDuration := time.Duration(remainingDurationFloat) - if remainingDuration < time.Duration(90)*time.Minute { - t.Errorf("Expecting maxSessionTime around 1h30m, received as: %v", remainingDuration) + if remainingDuration < time.Duration(20)*time.Minute { + t.Errorf("Expecting maxSessionTime around 20m, received as: %v", remainingDuration) } } } -func TestMaxDebit(t *testing.T) { +func TestMaxDebit1001(t *testing.T) { cc := &engine.CallCost{} var acnt *engine.Account cd := engine.CallDescriptor{ @@ -413,13 +413,67 @@ func TestMaxDebit(t *testing.T) { for _, blnc := range blncLst { if blnc.SharedGroup == "SHARED_A" && blnc.Value != 5 { t.Errorf("Unexpected value for shared balance: %f", blnc.Value) - } else if blnc.SharedGroup == "" && blnc.Value != 4.6 { + } else if len(blnc.SharedGroup) == 0 && blnc.Value != 4.4 { t.Errorf("Unexpected value for general balance: %f", blnc.Value) } } } } +func TestMaxDebit1007(t *testing.T) { + cc := &engine.CallCost{} + var acnt *engine.Account + cd := engine.CallDescriptor{ + Direction: "*out", + Tenant: "cgrates.org", + TOR: "call", + Subject: "1007", + Account: "1007", + Destination: "1002", + TimeStart: time.Now(), + TimeEnd: time.Now().Add(time.Duration(10) * time.Second), + } + if err := rater.Call("Responder.MaxDebit", cd, cc); err != nil { + t.Error(err.Error()) + } else if cc.GetDuration() > time.Duration(1)*time.Minute { + t.Errorf("Unexpected call duration received: %v", cc.GetDuration()) + } + // Debit out of shared balance should reflect in the 1001 instead of 1007 + attrs := &AttrGetAccount{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } else { + if len(acnt.BalanceMap["*monetary*out"]) != 2 { + t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap["*monetary*out"])) + } + blncLst := acnt.BalanceMap["*monetary*out"] + for _, blnc := range blncLst { + if blnc.SharedGroup == "SHARED_A" && blnc.Value != 4 { + t.Errorf("Unexpected value for shared balance: %f", blnc.Value) + } else if len(blnc.SharedGroup) == 0 && blnc.Value != 4.4 { + t.Errorf("Unexpected value for general balance: %f", blnc.Value) + } + } + } + // Make sure 1007 remains the same + attrs = &AttrGetAccount{Tenant: "cgrates.org", Account: "1007", BalanceType: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil { + t.Error("Got error on ApierV1.GetAccount: ", err.Error()) + } + if acnt.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue() != 0 { + t.Errorf("Calling ApierV1.GetBalance expected: 0, received: %f", acnt.BalanceMap[attrs.BalanceType+attrs.Direction].GetTotalValue()) + } + if len(acnt.BalanceMap[attrs.BalanceType+attrs.Direction]) != 1 { + t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[attrs.BalanceType+attrs.Direction])) + } + blnc := acnt.BalanceMap[attrs.BalanceType+attrs.Direction][0] + if len(blnc.SharedGroup) == 0 { // General balance + t.Errorf("Unexpected general balance: %f", blnc.Value) + } else if blnc.SharedGroup == "SHARED_A" && blnc.Value != 0 { + t.Errorf("Unexpected value for shared balance: %f", blnc.Value) + } +} + // Simply kill the engine after we are done with tests within this file func TestFsJsonStopEngine(t *testing.T) { if !*testLocal { diff --git a/cdre/fixedwidth.go b/cdre/fixedwidth.go index baec41987..5f4172653 100644 --- a/cdre/fixedwidth.go +++ b/cdre/fixedwidth.go @@ -19,48 +19,11 @@ along with this program. If not, see package cdre import ( - "fmt" - "strconv" + "github.com/cgrates/cgrates/utils" ) -type DeanCdrWriter struct{} +type FixedWidthCdrWriter struct{} -func (dcw *DeanCdrWriter) Write(cdr []string) error { +func (fww *FixedWidthCdrWriter) Write(cdr *utils.StoredCdr) error { return nil } - -// Used as generic function logic for various fields - -// Attributes -// source - the base source -// maxLen - the maximum field lenght -// stripAllowed - whether we allow stripping of chars in case of source bigger than the maximum allowed -// lStrip - if true, strip from beginning of the string -// lPadding - if true, add chars at the beginning of the string -// paddingChar - the character wich will be used to fill the existing -func filterField(source string, maxLen int, stripAllowed, lStrip, lPadding, padWithZero bool) (string, error) { - if len(source) == maxLen { // the source is exactly the maximum length - return source, nil - } - if len(source) > maxLen { //the source is bigger than allowed - if !stripAllowed { - return "", fmt.Errorf("source %s is bigger than the maximum allowed length %d", source, maxLen) - } - if !lStrip { - return source[:maxLen], nil - } else { - diffIndx := len(source) - maxLen - return source[diffIndx:], nil - } - } else { //the source is smaller as the maximum allowed - paddingString := "%" - if padWithZero { - paddingString += "0" // it will not work for rPadding but this is not needed - } - if !lPadding { - paddingString += "-" - } - paddingString += strconv.Itoa(maxLen) + "s" - return fmt.Sprintf(paddingString, source), nil - } -} diff --git a/data/tutorials/fs_json/cgrates/tariffplans/Actions.csv b/data/tutorials/fs_json/cgrates/tariffplans/Actions.csv index cc9d0d3e5..97b926ac6 100644 --- a/data/tutorials/fs_json/cgrates/tariffplans/Actions.csv +++ b/data/tutorials/fs_json/cgrates/tariffplans/Actions.csv @@ -1,6 +1,6 @@ #ActionsTag,Action,BalanceType,Direction,Units,ExpiryTime,DestinationTag,RatingSubject,BalanceWeight,SharedGroup,ExtraParameters,Weight TOPUP_RST_10,*topup_reset,*monetary,*out,10,*unlimited,*any,,10,,,10 -TOPUP_RST_5,*topup_reset,*monetary,*out,5,*unlimited,*any,,10,,,10 -TOPUP_RST_SHARED_5,*topup,*monetary,*out,5,*unlimited,*any,,20,SHARED_A,,10 +TOPUP_RST_5,*topup_reset,*monetary,*out,5,*unlimited,*any,,20,,,10 +TOPUP_RST_SHARED_5,*topup,*monetary,*out,5,*unlimited,*any,,10,SHARED_A,,10 SHARED_A_0,*topup_reset,*monetary,*out,0,*unlimited,*any,,10,SHARED_A,,10 LOG_WARNING,*log,,,,,,,,,,10 diff --git a/docs/tut_freeswitch_json.rst b/docs/tut_freeswitch_json.rst index cdca3830a..8a24f8dfa 100644 --- a/docs/tut_freeswitch_json.rst +++ b/docs/tut_freeswitch_json.rst @@ -6,14 +6,14 @@ Scenario - FreeSWITCH with *vanilla* configuration, replacing *mod_cdr_csv* with *mod_json_cdr*. - - Modified following users (with configs in *etc/freeswitch/directory/default*): 1001-prepaid, 1002-postpaid, 1003-pseudoprepaid, 1004-rated. + - Modified following users (with configs in *etc/freeswitch/directory/default*): 1001-prepaid, 1002-postpaid, 1003-pseudoprepaid, 1004-rated, 1006-prepaid, 1007-rated. - Have added inside default dialplan CGR own extensions just before routing towards users (*etc/freeswitch/dialplan/default.xml*). - FreeSWITCH configured to generate default *http-json* CDRs. - **CGRateS** with following components: - CGR-SM started as prepaid controller, with debits taking place at 5s intervals. - - CGR-Mediator compoenent attaching costs to the raw CDRs from FreeSWITCH_ inside CGR StorDB. + - CGR-Mediator component attaching costs to the raw CDRs from FreeSWITCH_ inside CGR StorDB. - CGR-CDRE exporting mediated CDRs from CGR StorDB (export path: */tmp*). - CGR-History component keeping the archive of the rates modifications (path browsable with git client at */tmp/cgr_history*). @@ -61,8 +61,13 @@ For our tutorial we load again prepared data out of shared folder, containing fo - Will charge by default *RT_40CNT* for all FreeSWITCH_ destinations during peak times (Monday-Friday 08:00-19:00) and *RT_10CNT* during offpeatimes (rest). - Account 1001 will receive a special *deal* for 1002 and 1003 destinations during peak times with *RT_20CNT*, otherwise having default rating. -- Create 4 accounts (equivalent of 2 FreeSWITCH default test users - 1001, 1002, 1003, 1004). -- 1001, 1002, 1003, 1004 will receive 10units of *\*monetary* balance. +- Create 5 accounts (equivalent of FreeSWITCH default test users - 1001, 1002, 1003, 1004, 1007). +- Create 1 account alias (1006 - alias of account 1002). +- Create 1 rating profile alias (1006 - alias of rating profile 1001). +- 1002, 1003, 1004 will receive 10units of *\*monetary* balance. +- 1001 will receive 5 units of general *\*monetary* and 5 units of shared balance in the shared group "SHARED_A". +- 1007 will receive 0 units of shared balance in the shared group "SHARED_A". +- Define the shared balance "SHARED_A" with debit policy *\*highest*. - For each balance created, attach 3 triggers to control the balance: log on balance=2, log on balance=20, log on 5 mins talked towards 10xx destination. :: @@ -85,10 +90,11 @@ To verify that all actions successfully performed, we use following *cgr-console :: - cgr-console get_balance cgrates.org 1001 - cgr-console get_balance cgrates.org 1002 - cgr-console get_balance cgrates.org 1003 - cgr-console get_balance cgrates.org 1004 + cgr-console get_account cgrates.org 1001 + cgr-console get_account cgrates.org 1002 + cgr-console get_account cgrates.org 1003 + cgr-console get_account cgrates.org 1004 + cgr-console get_account cgrates.org 1007 - Query call costs so we can see our calls will have expected costs (final cost will result as sum of *ConnectFee* and *Cost* fields): @@ -109,15 +115,15 @@ Test calls 1001 -> 1002 ~~~~~~~~~~~~ -Since the user 1001 is marked as *prepaid* inside FreeSWITCH_ directory configuration, calling between 1001 and 1002 should generate pre-auth and prepaid debits which can be checked with *get_balance* command integrated within *cgr-console* tool. Charging will be done based on time of day as described above. +Since the user 1001 is marked as *prepaid* inside FreeSWITCH_ directory configuration, calling between 1001 and 1002 should generate pre-auth and prepaid debits which can be checked with *get_account* command integrated within *cgr-console* tool. Charging will be done based on time of day as described above. *Note*: An important particularity to note here is the ability of **CGRateS** SessionManager to refund units booked in advance (eg: if debit occurs every 10s and rate increments are set to 1s, the SessionManager will be smart enough to refund pre-booked credits for calls stoped in the middle of debit interval). -Check that 1001 balance is properly debitted, during the call: +Check that 1001 balance is properly debitted, during the call, and moreover considering that general balance has priority over the shared one debits for this call should take place at first out of general balance. :: - cgr-console get_balance cgrates.org 1001 + cgr-console get_account cgrates.org 1001 1002 -> 1001 @@ -129,7 +135,7 @@ To check that we had debits we use again console command, this time not during t :: - cgr-console get_balance cgrates.org 1002 + cgr-console get_account cgrates.org 1002 1003 -> 1001 @@ -141,7 +147,7 @@ To check that there are no debits during or by the end of the call, but when the :: - cgr-console get_balance cgrates.org 1003 + cgr-console get_account cgrates.org 1003 1004 -> 1001 @@ -150,6 +156,32 @@ To check that there are no debits during or by the end of the call, but when the The user 1004 is marked as *rated* inside FreeSWITCH_ hence his calls not interact in any way with accounting subsystem. The only action perfomed by **CGRateS** related to his calls wil be rating/mediation of his CDRs. +1006 -> 1002 +~~~~~~~~~~~~ + +Since the user 1006 is marked as *prepaid* inside FreeSWITCH_ directory configuration, calling between 1006 and 1002 should generate pre-auth and prepaid debits which can be checked with *get_account* command integrated within *cgr-console* tool. One thing to note here is that 1006 is not defined as an account inside CGR Accounting Subsystem but as an alias of another account, hence *get_account* ran on 1006 will return "not found" and the debits can be monitored on the real account which is 1001. + +Check that 1001 balance is properly debitted, during the call, and moreover considering that general balance has priority over the shared one debits for this call should take place at first out of general balance. + +:: + + cgr-console get_account cgrates.org 1006 + cgr-console get_account cgrates.org 1001 + + +1007 -> 1002 +~~~~~~~~~~~~ + +Since the user 1007 is marked as *prepaid* inside FreeSWITCH_ directory configuration, calling between 1007 and 1002 should generate pre-auth and prepaid debits which can be checked with *get_account* command integrated within *cgr-console* tool. Since 1007 has no units left into his accounts but he has one balance marked as shared, debits for this call should take place in accounts which are a part of the same shared balance as the one of *1007/SHARED_A*, which in our scenario corresponds to the one of the account 1001. + +Check that call can proceed even if 1007 has no units left into his own balances, and that the costs attached to the call towards 1002 are debited from the balance marked as shared within account 1001. + +:: + + cgr-console get_account cgrates.org 1007 + cgr-console get_account cgrates.org 1001 + + Fraud detection ~~~~~~~~~~~~~~~ diff --git a/pkg/debian/changelog b/pkg/debian/changelog index 3b29ae54e..7818fec05 100644 --- a/pkg/debian/changelog +++ b/pkg/debian/changelog @@ -2,7 +2,7 @@ cgrates (0.9.1-rc4) UNRELEASED; urgency=low * RC4. - -- DanB Fri, 28 Feb 2014 12:43:31 +0100 + -- DanB Thursday, 20 March 2014 13:52:25 +0100 cgrates (0.9.1-rc3) UNRELEASED; urgency=low diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 5e66b1766..bdfddcc88 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -295,6 +295,9 @@ type CacheStats struct { RatingPlans int RatingProfiles int Actions int + SharedGroups int + RatingAliases int + AccountAliases int } type AttrCachedItemAge struct { @@ -307,6 +310,9 @@ type CachedItemAge struct { RatingPlan time.Duration RatingProfile time.Duration Action time.Duration + SharedGroup time.Duration + RatingAlias time.Duration + AccountAlias time.Duration } type AttrExpFileCdrs struct { diff --git a/utils/consts.go b/utils/consts.go index e13abb31d..3405475d1 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -91,6 +91,7 @@ const ( INTERNAL = "internal" ZERO_RATING_SUBJECT_PREFIX = "*zero" OK = "OK" + CDR_FIXED_WIDTH = "cdr_fixed_width" ) var (