From d789cac0b768ef1690ed31aba6f82494234ae14b Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 2 Nov 2017 15:33:31 +0100 Subject: [PATCH] Responder MaxComputedUsage implementation --- apier/v1/debit_test.go | 2 +- cmd/cgr-engine/rater.go | 2 +- engine/responder.go | 50 ++++++++++++++++++++++++++++++++------ engine/responder_test.go | 2 +- general_tests/auth_test.go | 5 ++-- utils/errors.go | 1 + 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/apier/v1/debit_test.go b/apier/v1/debit_test.go index 772b7b06a..460d13751 100644 --- a/apier/v1/debit_test.go +++ b/apier/v1/debit_test.go @@ -37,7 +37,7 @@ var ( func init() { apierDebitStorage, _ = engine.NewMapStorage() cfg, _ := config.NewDefaultCGRConfig() - responder := new(engine.Responder) + responder := &engine.Responder{MaxComputedUsage: cfg.RALsMaxComputedUsage} dm = engine.NewDataManager(apierDebitStorage) engine.SetDataStorage(dm) apierDebit = &ApierV1{ diff --git a/cmd/cgr-engine/rater.go b/cmd/cgr-engine/rater.go index 7994eb181..4090ff227 100755 --- a/cmd/cgr-engine/rater.go +++ b/cmd/cgr-engine/rater.go @@ -229,7 +229,7 @@ func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneC for _, chn := range waitTasks { <-chn } - responder := &engine.Responder{ExitChan: exitChan} + responder := &engine.Responder{ExitChan: exitChan, MaxComputedUsage: cfg.RALsMaxComputedUsage} responder.SetTimeToLive(cfg.ResponseCacheTTL, nil) apierRpcV1 := &v1.ApierV1{StorDb: loadDb, DataManager: dm, CdrDb: cdrDb, Config: cfg, Responder: responder, ServManager: serviceManager, diff --git a/engine/responder.go b/engine/responder.go index bfb9d1de5..1057460c1 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -46,12 +46,13 @@ type AttrGetLcr struct { } type Responder struct { - ExitChan chan bool - Stats rpcclient.RpcClientConnection - Timeout time.Duration - Timezone string - cnt int64 - responseCache *cache.ResponseCache + ExitChan chan bool + Stats rpcclient.RpcClientConnection + Timeout time.Duration + Timezone string + MaxComputedUsage map[string]time.Duration + cnt int64 + responseCache *cache.ResponseCache } func (rs *Responder) SetTimeToLive(timeToLive time.Duration, out *int) error { @@ -66,6 +67,18 @@ func (rs *Responder) getCache() *cache.ResponseCache { return rs.responseCache } +// usageAllowed checks requested usage against configured MaxComputedUsage +func (rs *Responder) usageAllowed(tor string, reqUsage time.Duration) (allowed bool) { + mcu, has := rs.MaxComputedUsage[tor] + if !has { + mcu = rs.MaxComputedUsage[utils.ANY] + } + if reqUsage <= mcu { + allowed = true + } + return +} + /* RPC method thet provides the external RPC interface for getting the rating information. */ @@ -91,6 +104,9 @@ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := guardian.Guardian.Guard(func() (interface{}, error) { return arg.GetCost() }, 0, arg.GetAccountKey()) @@ -124,6 +140,9 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.Debit() if e != nil { return e @@ -161,6 +180,9 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.MaxDebit() if e != nil { rs.getCache().Cache(cacheKey, &cache.CacheItem{ @@ -208,6 +230,9 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err }) return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } err = arg.RefundIncrements() rs.getCache().Cache(cacheKey, &cache.CacheItem{ Value: reply, @@ -247,6 +272,9 @@ func (rs *Responder) RefundRounding(arg *CallDescriptor, reply *float64) (err er }) return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } err = arg.RefundRounding() rs.getCache().Cache(cacheKey, &cache.CacheItem{ Value: reply, @@ -276,6 +304,9 @@ func (rs *Responder) GetMaxSessionTime(arg *CallDescriptor, reply *float64) (err }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.GetMaxSessionDuration() *reply, err = float64(r), e return @@ -312,7 +343,9 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) return err } - + if !rs.usageAllowed(ev.ToR, ev.Usage) { + return utils.ErrMaxUsageExceeded + } maxCallDuration := -1.0 attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: utils.OUT, @@ -518,6 +551,9 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) return err } + if !rs.usageAllowed(cd.TOR, cd.GetDuration()) { + return utils.ErrMaxUsageExceeded + } lcrCost, err := attrs.CallDescriptor.GetLCR(rs.Stats, attrs.LCRFilter, attrs.Paginator) if err != nil { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) diff --git a/engine/responder_test.go b/engine/responder_test.go index 567f0f10e..e367761fe 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -33,6 +33,7 @@ var rsponder *Responder func init() { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) + rsponder = &Responder{MaxComputedUsage: cfg.RALsMaxComputedUsage} } // Test internal abilites of GetDerivedChargers @@ -40,7 +41,6 @@ func TestResponderGetDerivedChargers(t *testing.T) { cfgedDC := &utils.DerivedChargers{DestinationIDs: utils.StringMap{}, Chargers: []*utils.DerivedCharger{&utils.DerivedCharger{RunID: "responder1", RequestTypeField: utils.META_DEFAULT, DirectionField: "test", TenantField: "test", CategoryField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", UsageField: "test"}}} - rsponder = &Responder{} attrs := &utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "responder_test", Subject: "responder_test"} if err := dm.DataDB().SetDerivedChargers(utils.DerivedChargersKey(utils.OUT, utils.ANY, utils.ANY, utils.ANY, utils.ANY), cfgedDC, utils.NonTransactional); err != nil { t.Error(err) diff --git a/general_tests/auth_test.go b/general_tests/auth_test.go index 18a3a59dd..2924a89b7 100644 --- a/general_tests/auth_test.go +++ b/general_tests/auth_test.go @@ -35,7 +35,7 @@ func TestAuthSetStorage(t *testing.T) { data, _ := engine.NewMapStorageJson() dbAuth = engine.NewDataManager(data) engine.SetDataStorage(dbAuth) - rsponder = new(engine.Responder) + rsponder = &engine.Responder{MaxComputedUsage: config.CgrConfig().RALsMaxComputedUsage} } @@ -98,7 +98,8 @@ func TestAuthPostpaidNoAcnt(t *testing.T) { Category: "call", Account: "nonexistent", Subject: "testauthpostpaid1", Destination: "4986517174963", SetupTime: time.Date(2015, 8, 27, 11, 26, 0, 0, time.UTC)} var maxSessionTime float64 - if err := rsponder.GetDerivedMaxSessionTime(cdr, &maxSessionTime); err == nil || err != utils.ErrAccountNotFound { + if err := rsponder.GetDerivedMaxSessionTime(cdr, &maxSessionTime); err == nil || + err != utils.ErrAccountNotFound { t.Error(err) } } diff --git a/utils/errors.go b/utils/errors.go index 54471678e..a2b813d21 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -46,6 +46,7 @@ var ( ErrResourceUnavailable = errors.New("RESOURCE_UNAVAILABLE") ErrNoActiveSession = errors.New("NO_ACTIVE_SESSION") ErrPartiallyExecuted = errors.New("PARTIALLY_EXECUTED") + ErrMaxUsageExceeded = errors.New("MAX_USAGE_EXCEEDED") ) // NewCGRError initialises a new CGRError