From 4d41c6552c8c17bf315e77aa12afe164dc72737f Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 11 May 2017 21:20:56 +0200 Subject: [PATCH] ResourceLimit with AllocationMessage --- .../mysql/create_tariffplan_tables.sql | 1 + .../postgres/create_tariffplan_tables.sql | 3 +- data/tariffplans/tutorial/ResourceLimits.csv | 12 +- .../etc/kamailio/kamailio-cgrates.cfg | 6 +- .../kamailio/etc/kamailio/kamailio.cfg | 2 +- engine/loader_csv_test.go | 28 ++--- engine/model_helpers.go | 9 ++ engine/models.go | 5 +- engine/reslimiter.go | 103 ++++++++++++------ engine/reslimiter_test.go | 41 ++++++- engine/responder.go | 2 +- sessionmanager/kamailiosm.go | 8 +- sessionmanager/kamevent.go | 23 ++-- sessionmanager/kamevent_test.go | 4 +- utils/apitpdata.go | 3 +- 15 files changed, 168 insertions(+), 82 deletions(-) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 4f650505d..6179a8615 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -402,6 +402,7 @@ CREATE TABLE tp_resource_limits ( `activation_interval` varchar(64) NOT NULL, `usage_ttl` varchar(32) NOT NULL, `limit` varchar(64) NOT NULL, + `allocation_message` varchar(64) NOT NULL, `weight` decimal(8,2) NOT NULL, `action_trigger_ids` varchar(64) NOT NULL, `created_at` TIMESTAMP, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 48951f270..dab478d76 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -396,8 +396,9 @@ CREATE TABLE tp_resource_limits ( "filter_field_values" varchar(256) NOT NULL, "activation_interval" varchar(64) NOT NULL, "usage_ttl" varchar(32) NOT NULL, - "weight" decimal(8,2) NOT NULL, "limit" varchar(64) NOT NULL, + "allocation_message" varchar(64) NOT NULL, + "weight" decimal(8,2) NOT NULL, "action_trigger_ids" varchar(64) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE ); diff --git a/data/tariffplans/tutorial/ResourceLimits.csv b/data/tariffplans/tutorial/ResourceLimits.csv index a78c36730..a104d3070 100644 --- a/data/tariffplans/tutorial/ResourceLimits.csv +++ b/data/tariffplans/tutorial/ResourceLimits.csv @@ -1,6 +1,6 @@ -#Id,FilterType,FilterFieldName,FilterFieldValues,ActivationInterval,TTL,Limit,Weight,ActionTriggers -ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,2,10, -ResGroup1,*string_prefix,Destination,10;20,,,,, -ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,, -ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,2,10, -ResGroup2,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,, +#Id,FilterType,FilterFieldName,FilterFieldValues,ActivationInterval,TTL,Limit,AllocationReply,Weight,ActionTriggers +ResGroup1,*string,Account,1001;1002,2014-07-29T15:00:00Z,1s,2,,10, +ResGroup1,*string_prefix,Destination,10;20,,,,,, +ResGroup1,*rsr_fields,,Subject(~^1.*1$);Destination(1002),,,,,, +ResGroup2,*destinations,Destination,DST_FS,2014-07-29T15:00:00Z,3600s,2,,SPECIAL_1002,10, +ResGroup2,*cdr_stats,,CDRST1:*min_ASR:34;CDRST_1001:*min_ASR:20,,,,,, diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg index 67e596c9e..4858bd479 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg @@ -117,13 +117,13 @@ route[CGRATES_RL_REQUEST] { \"cgr_setuptime\":\"$TS\"}"); } -# Process LCR_REPLY from CGRateS +# Process RL_REPLY from CGRateS route[CGR_RL_REPLY] { - json_get_field("$evapi(msg)", "ResourceAllowed", "$var(CGRResourceAllowed)"); + json_get_field("$evapi(msg)", "ResourceAllocated", "$var(CGRResourceAllocated)"); json_get_field("$evapi(msg)", "Error", "$var(CgrError)"); $var(id_index) = $(var(TransactionIndex){s.int}); $var(id_label) = $(var(TransactionLabel){s.int}); - $var(CGRResourceAllowed) = $(var(CGRResourceAllowed){s.rm,"}); + $var(CGRResourceAllocated) = $(var(CGRResourceAllocated){s.rm,"}); $var(CgrError) = $(var(CgrError){s.rm,"}); t_continue("$var(id_index)", "$var(id_label)", "CGRATES_RL_REPLY"); # Unpark the transaction } diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg index 2d5dfb710..5662cf77d 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg @@ -220,7 +220,7 @@ route[CGRATES_RL_REPLY] { sl_send_reply("503","CGR_ERROR"); exit; } - if $var(CGRResourceAllowed) == "false" { + if $var(CGRResourceAllocated) == "false" { sl_send_reply("403","Resource not allowed"); exit; } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 56098f96d..ed7f9c826 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -267,11 +267,11 @@ cgrates.org,mas,true,another,value,10 ` resLimits = ` -#Id,FilterType,FilterFieldName,FilterFieldValues,ActivationInterval,TTL,Limit,Weight,ActionTriggers -ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,10, -ResGroup21,*string_prefix,HdrDestination,10;20,,,,, -ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,, -ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,10, +#Id,FilterType,FilterFieldName,FilterFieldValues,ActivationInterval,TTL,Limit,AllocationMessage,Weight,ActionTriggers +ResGroup21,*string,HdrAccount,1001;1002,2014-07-29T15:00:00Z,1s,2,call,10, +ResGroup21,*string_prefix,HdrDestination,10;20,,,,,, +ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,,,, +ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,10, ` ) @@ -329,7 +329,6 @@ func init() { log.Print("error in LoadAliases:", err) } if err := csvr.LoadResourceLimits(); err != nil { - log.Print("error in LoadResourceLimits:", err) } csvr.WriteToDatabase(false, false, false) cache.Flush() @@ -1387,9 +1386,10 @@ func TestLoadResourceLimits(t *testing.T) { ActivationInterval: &utils.TPActivationInterval{ ActivationTime: "2014-07-29T15:00:00Z", }, - UsageTTL: "1s", - Weight: 10, - Limit: "2", + UsageTTL: "1s", + AllocationMessage: "call", + Weight: 10, + Limit: "2", }, "ResGroup22": &utils.TPResourceLimit{ TPid: testTPID, @@ -1400,15 +1400,15 @@ func TestLoadResourceLimits(t *testing.T) { ActivationInterval: &utils.TPActivationInterval{ ActivationTime: "2014-07-29T15:00:00Z", }, - UsageTTL: "3600s", - Weight: 10, - Limit: "2", + UsageTTL: "3600s", + AllocationMessage: "premium_call", + Weight: 10, + Limit: "2", }, } if len(csvr.resLimits) != len(eResLimits) { t.Error("Failed to load resourcelimits: ", len(csvr.resLimits)) - } - if !reflect.DeepEqual(eResLimits["ResGroup22"], csvr.resLimits["ResGroup22"]) { + } else if !reflect.DeepEqual(eResLimits["ResGroup22"], csvr.resLimits["ResGroup22"]) { t.Errorf("Expecting: %+v, received: %+v", eResLimits["ResGroup22"], csvr.resLimits["ResGroup22"]) } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index d7e13c9a3..c7b4d1d3f 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1845,6 +1845,9 @@ func (tps TpResourceLimits) AsTPResourceLimits() (result []*utils.TPResourceLimi if tp.Limit != "" { rl.Limit = tp.Limit } + if tp.AllocationMessage != "" { + rl.AllocationMessage = tp.AllocationMessage + } if len(tp.ActivationInterval) != 0 { rl.ActivationInterval = new(utils.TPActivationInterval) aiSplt := strings.Split(tp.ActivationInterval, utils.INFIELD_SEP) @@ -1888,6 +1891,7 @@ func APItoModelResourceLimit(rl *utils.TPResourceLimit) (mdls TpResourceLimits) mdl.UsageTTL = rl.UsageTTL mdl.Weight = rl.Weight mdl.Limit = rl.Limit + mdl.AllocationMessage = rl.AllocationMessage if rl.ActivationInterval != nil { if rl.ActivationInterval.ActivationTime != "" { mdl.ActivationInterval = rl.ActivationInterval.ActivationTime @@ -1921,6 +1925,11 @@ func APItoModelResourceLimit(rl *utils.TPResourceLimit) (mdls TpResourceLimits) func APItoResourceLimit(tpRL *utils.TPResourceLimit, timezone string) (rl *ResourceLimit, err error) { rl = &ResourceLimit{ID: tpRL.ID, Weight: tpRL.Weight, Filters: make([]*RequestFilter, len(tpRL.Filters)), Usage: make(map[string]*ResourceUsage)} + if tpRL.UsageTTL != "" { + if rl.UsageTTL, err = utils.ParseDurationWithSecs(tpRL.UsageTTL); err != nil { + return nil, err + } + } for i, f := range tpRL.Filters { rf := &RequestFilter{Type: f.Type, FieldName: f.FieldName, Values: f.Values} if err := rf.CompileValues(); err != nil { diff --git a/engine/models.go b/engine/models.go index 5a6026154..8d0449492 100644 --- a/engine/models.go +++ b/engine/models.go @@ -460,8 +460,9 @@ type TpResourceLimit struct { ActivationInterval string `index:"4" re:""` UsageTTL string `index:"5" re:""` Limit string `index:"6" re:""` - Weight float64 `index:"7" re:"\d+\.?\d*"` - ActionTriggerIds string `index:"8" re:""` + AllocationMessage string `index:"7" re:""` + Weight float64 `index:"8" re:"\d+\.?\d*"` + ActionTriggerIds string `index:"9" re:""` CreatedAt time.Time } diff --git a/engine/reslimiter.go b/engine/reslimiter.go index 36b7d548b..7c5d8b115 100644 --- a/engine/reslimiter.go +++ b/engine/reslimiter.go @@ -30,9 +30,9 @@ import ( ) type ResourceUsage struct { - ID string // Unique identifier of this resourceUsage, Eg: FreeSWITCH UUID - UsageTime time.Time // So we can expire it later - UsageUnits float64 // Number of units used + ID string // Unique identifier of this ResourceUsage, Eg: FreeSWITCH UUID + Time time.Time // So we can expire it later + Units float64 // Number of units used } // ResourceLimit represents a limit imposed for accessing a resource (eg: new calls) @@ -46,23 +46,22 @@ type ResourceLimit struct { Limit float64 // Limit value ActionTriggers ActionTriggers // Thresholds to check after changing Limit UsageTTL time.Duration // Expire usage after this duration + AllocationMessage string // message returned by the winning resourceLimit on allocation Usage map[string]*ResourceUsage // Keep a record of usage, bounded with timestamps so we can expire too long records TotalUsage float64 // internal counter aggregating real usage of ResourceLimit } func (rl *ResourceLimit) removeExpiredUnits() { for ruID, rv := range rl.Usage { - if time.Now().Sub(rv.UsageTime) <= rl.UsageTTL { + if time.Now().Sub(rv.Time) <= rl.UsageTTL { continue // not expired } delete(rl.Usage, ruID) - rl.TotalUsage -= rv.UsageUnits + rl.TotalUsage -= rv.Units } } func (rl *ResourceLimit) UsedUnits() float64 { - rl.Lock() - defer rl.Unlock() if rl.UsageTTL != 0 { rl.removeExpiredUnits() } @@ -70,25 +69,21 @@ func (rl *ResourceLimit) UsedUnits() float64 { } func (rl *ResourceLimit) RecordUsage(ru *ResourceUsage) (err error) { - rl.Lock() - defer rl.Unlock() if _, hasID := rl.Usage[ru.ID]; hasID { return fmt.Errorf("Duplicate resource usage with id: %s", ru.ID) } rl.Usage[ru.ID] = ru - rl.TotalUsage += ru.UsageUnits + rl.TotalUsage += ru.Units return } func (rl *ResourceLimit) ClearUsage(ruID string) error { - rl.Lock() - defer rl.Unlock() ru, hasIt := rl.Usage[ruID] if !hasIt { return fmt.Errorf("Cannot find usage record with id: %s", ruID) } delete(rl.Usage, ru.ID) - rl.TotalUsage -= ru.UsageUnits + rl.TotalUsage -= ru.Units return nil } @@ -100,19 +95,7 @@ func (rls ResourceLimits) Sort() { sort.Slice(rls, func(i, j int) bool { return rls[i].Weight > rls[j].Weight }) } -// AllowUsage checks limits and decides whether the usage is allowed -func (rls ResourceLimits) AllowUsage(usage float64) (allowed bool) { - if len(rls) != 0 { // if rules defined, they need to allow usage - for _, rl := range rls { - if rl.Limit < rl.UsedUnits()+usage { - return false - } - } - } - return true -} - -// RecordUsage will record the usage in all the resource limits +// RecordUsage will record the usage in all the resource limits, failing back on errors func (rls ResourceLimits) RecordUsage(ru *ResourceUsage) (err error) { var failedAtIdx int for i, rl := range rls { @@ -124,6 +107,7 @@ func (rls ResourceLimits) RecordUsage(ru *ResourceUsage) (err error) { if err != nil { for _, rl := range rls[:failedAtIdx] { rl.ClearUsage(ru.ID) // best effort + rl.TotalUsage -= ru.Units } } return @@ -131,6 +115,10 @@ func (rls ResourceLimits) RecordUsage(ru *ResourceUsage) (err error) { // ClearUsage gives back the units to the pool func (rls ResourceLimits) ClearUsage(ruID string) { + for _, rl := range rls { + rl.Lock() + defer rl.Unlock() + } for _, rl := range rls { if err := rl.ClearUsage(ruID); err != nil { utils.Logger.Warning(fmt.Sprintf(", err: %s", err.Error())) @@ -139,6 +127,44 @@ func (rls ResourceLimits) ClearUsage(ruID string) { return } +// AllocateResource attempts allocating resources for a *ResourceUsage +// simulates on dryRun +// returns utils.ErrResourceUnavailable if allocation is not possible +func (rls ResourceLimits) AllocateResource(ru *ResourceUsage, dryRun bool) (alcMessage string, err error) { + if len(rls) == 0 { + return utils.META_NONE, nil + } + // lock resources so we can safely take decisions, need all to be locked before proceeding + for _, rl := range rls { + if dryRun { + rl.RLock() + defer rl.RUnlock() + } else { + rl.Lock() + defer rl.Unlock() + } + } + // Simulate resource usage + for _, rl := range rls { + if rl.Limit >= rl.UsedUnits()+ru.Units { + if alcMessage == "" { + alcMessage = rl.AllocationMessage + } + if alcMessage == "" { // rl.AllocationMessage is not populated + alcMessage = rl.ID + } + } + } + if alcMessage == "" { + return "", utils.ErrResourceUnavailable + } + if dryRun { + return + } + err = rls.RecordUsage(ru) + return +} + // Pas the config as a whole so we can ask access concurrently func NewResourceLimiterService(cfg *config.CGRConfig, dataDB DataDB, cdrStatS rpcclient.RpcClientConnection) (*ResourceLimiterService, error) { if cdrStatS != nil && reflect.ValueOf(cdrStatS).IsNil() { @@ -254,12 +280,19 @@ func (rls *ResourceLimiterService) V1ResourceLimitsForEvent(ev map[string]interf return nil } -func (rls *ResourceLimiterService) V1AllowUsage(attrs utils.AttrRLsResourceUsage, allow *bool) (err error) { - matchingRLForEv, err := rls.matchingResourceLimitsForEvent(attrs.Event) +func (rls *ResourceLimiterService) V1AllowUsage(args utils.AttrRLsResourceUsage, allow *bool) (err error) { + mtcRLs, err := rls.matchingResourceLimitsForEvent(args.Event) if err != nil { return utils.NewErrServerError(err) } - *allow = matchingRLForEv.AllowUsage(attrs.Units) + if _, err = mtcRLs.AllocateResource(&ResourceUsage{ID: args.UsageID, + Time: time.Now(), Units: args.Units}, false); err != nil { + if err == utils.ErrResourceUnavailable { + return // not error but still not allowed + } + return utils.NewErrServerError(err) + } + *allow = true return } @@ -269,14 +302,12 @@ func (rls *ResourceLimiterService) V1AllocateResource(args utils.AttrRLsResource if err != nil { return utils.NewErrServerError(err) } - if !mtcRLs.AllowUsage(args.Units) { - return utils.ErrResourceUnavailable + if alcMsg, err := mtcRLs.AllocateResource(&ResourceUsage{ID: args.UsageID, + Time: time.Now(), Units: args.Units}, false); err != nil { + return err + } else { + *reply = alcMsg } - if err = mtcRLs.RecordUsage(&ResourceUsage{ID: args.UsageID, - UsageTime: time.Now(), UsageUnits: args.Units}); err != nil { - return - } - *reply = utils.OK return } diff --git a/engine/reslimiter_test.go b/engine/reslimiter_test.go index 7cff51adf..dda5e3892 100644 --- a/engine/reslimiter_test.go +++ b/engine/reslimiter_test.go @@ -172,6 +172,45 @@ func TestRLsIndexStringFilters(t *testing.T) { } } */ +/* +func TestResourceLimitsAllowUsage(t *testing.T) { + rls := make(ResourceLimits, 0) + if !rls.AllowUsage(1.0) { + t.Error("Not allowed for empty limits") + } + rls = ResourceLimits{ + &ResourceLimit{ + ID: "RLAU1", + Weight: 20, + Filters: []*RequestFilter{ + &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}}, + &RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"}, + rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP), + }}, + ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)}, + Limit: 1, + Usage: map[string]*ResourceUsage{"call1": &ResourceUsage{ + ID: "call1", UsageTime: time.Now(), UsageUnits: 1}}, + TotalUsage: 1, + }, + &ResourceLimit{ + ID: "RLAU2", + Weight: 10, + Filters: []*RequestFilter{ + &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, + &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, + }, + ActivationInterval: &utils.ActivationInterval{ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)}, + Limit: 2, + UsageTTL: time.Duration(1 * time.Millisecond), + Usage: make(map[string]*ResourceUsage), + }, + } + if !rls.AllowUsage(2.0) { + t.Error("Not allowed") + } +} +*/ func TestRLsLoadRLs(t *testing.T) { rls := []*ResourceLimit{ @@ -347,7 +386,7 @@ func TestRLsV1InitiateResourceUsage(t *testing.T) { var reply string if err := rLS.V1AllocateResource(attrRU, &reply); err != nil { t.Error(err) - } else if reply != utils.OK { + } else if reply != "RL1" { t.Error("Received reply: ", reply) } resLimits, err := rLS.matchingResourceLimitsForEvent(attrRU.Event) diff --git a/engine/responder.go b/engine/responder.go index 42210bd42..4267846ac 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -380,8 +380,8 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { maxCallDuration = remainingDuration } } - rs.getCache().Cache(cacheKey, &cache.CacheItem{Value: maxCallDuration}) *reply = maxCallDuration + rs.getCache().Cache(cacheKey, &cache.CacheItem{Value: reply}) return nil } diff --git a/sessionmanager/kamailiosm.go b/sessionmanager/kamailiosm.go index ec5da2fdb..c743b9283 100644 --- a/sessionmanager/kamailiosm.go +++ b/sessionmanager/kamailiosm.go @@ -93,7 +93,7 @@ func (self *KamailioSessionManager) onCgrAuth(evData []byte, connId string) { return } if kev.MissingParameter(self.timezone) { - if kar, err := kev.AsKamAuthReply(0.0, "", false, utils.ErrMandatoryIeMissing); err != nil { + if kar, err := kev.AsKamAuthReply(0.0, "", false, "", utils.ErrMandatoryIeMissing); err != nil { utils.Logger.Err(fmt.Sprintf(" Failed building auth reply %s", err.Error())) } else if err = self.conns[connId].Send(kar.String()); err != nil { utils.Logger.Err(fmt.Sprintf(" Failed sending auth reply %s", err.Error())) @@ -123,7 +123,7 @@ func (self *KamailioSessionManager) onCgrAuth(evData []byte, connId string) { resourceAllowed = false } } - if kar, err := kev.AsKamAuthReply(remainingDuration, supplStr, resourceAllowed, errReply); err != nil { + if kar, err := kev.AsKamAuthReply(remainingDuration, supplStr, resourceAllowed, "", errReply); err != nil { utils.Logger.Err(fmt.Sprintf(" Failed building auth reply %s", err.Error())) } else if err = self.conns[connId].Send(kar.String()); err != nil { utils.Logger.Err(fmt.Sprintf(" Failed sending auth reply %s", err.Error())) @@ -137,7 +137,7 @@ func (self *KamailioSessionManager) onCgrLcrReq(evData []byte, connId string) { return } supplStr, err := self.getSuppliers(kev) - kamLcrReply, errReply := kev.AsKamAuthReply(0, supplStr, false, err) + kamLcrReply, errReply := kev.AsKamAuthReply(0, supplStr, false, "", err) kamLcrReply.Event = CGR_LCR_REPLY // Hit the CGR_LCR_REPLY event route on Kamailio side if errReply != nil { utils.Logger.Err(fmt.Sprintf(" Failed building LCR reply %s", errReply.Error())) @@ -158,7 +158,7 @@ func (self *KamailioSessionManager) onCgrRLReq(evData []byte, connId string) { utils.Logger.Err(fmt.Sprintf(" RLs error: %s", err.Error())) resourceAllowed = false } - kamRLReply, errReply := kev.AsKamAuthReply(0, "", resourceAllowed, err) + kamRLReply, errReply := kev.AsKamAuthReply(0, "", resourceAllowed, "", err) kamRLReply.Event = CGR_RL_REPLY // Hit the CGR_LCR_REPLY event route on Kamailio side if errReply != nil { utils.Logger.Err(fmt.Sprintf(" Failed building RL reply %s", errReply.Error())) diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 08eae2a38..b1b51ed62 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -55,13 +55,14 @@ var primaryFields = []string{EVENT, CALLID, FROM_TAG, HASH_ENTRY, HASH_ID, CGR_A CGR_CATEGORY, CGR_TENANT, CGR_REQTYPE, CGR_ANSWERTIME, CGR_SETUPTIME, CGR_STOPTIME, CGR_DURATION, CGR_PDD, utils.CGR_SUPPLIER, utils.CGR_DISCONNECT_CAUSE} type KamAuthReply struct { - Event string // Kamailio will use this to differentiate between requests and replies - TransactionIndex int // Original transaction index - TransactionLabel int // Original transaction label - MaxSessionTime int // Maximum session time in case of success, -1 for unlimited - Suppliers string // List of suppliers, comma separated - ResourceAllowed bool - Error string // Reply in case of error + Event string // Kamailio will use this to differentiate between requests and replies + TransactionIndex int // Original transaction index + TransactionLabel int // Original transaction label + MaxSessionTime int // Maximum session time in case of success, -1 for unlimited + Suppliers string // List of suppliers, comma separated + ResourceAllocated bool + AllocationMessage string + Error string // Reply in case of error } func (self *KamAuthReply) String() string { @@ -349,8 +350,10 @@ func (kev KamEvent) String() string { return string(mrsh) } -func (kev KamEvent) AsKamAuthReply(maxSessionTime float64, suppliers string, resAllowed bool, rplyErr error) (kar *KamAuthReply, err error) { - kar = &KamAuthReply{Event: CGR_AUTH_REPLY, Suppliers: suppliers} +func (kev KamEvent) AsKamAuthReply(maxSessionTime float64, suppliers string, + resAllocated bool, allocationMessage string, rplyErr error) (kar *KamAuthReply, err error) { + kar = &KamAuthReply{Event: CGR_AUTH_REPLY, Suppliers: suppliers, + ResourceAllocated: resAllocated, AllocationMessage: allocationMessage} if rplyErr != nil { kar.Error = rplyErr.Error() } @@ -371,7 +374,7 @@ func (kev KamEvent) AsKamAuthReply(maxSessionTime float64, suppliers string, res maxSessionTime = maxSessionDur.Seconds() } kar.MaxSessionTime = int(utils.Round(maxSessionTime, 0, utils.ROUNDING_MIDDLE)) - kar.ResourceAllowed = resAllowed + return kar, nil } diff --git a/sessionmanager/kamevent_test.go b/sessionmanager/kamevent_test.go index caea74588..7a068c94a 100644 --- a/sessionmanager/kamevent_test.go +++ b/sessionmanager/kamevent_test.go @@ -61,8 +61,8 @@ func TestNewKamEvent(t *testing.T) { func TestKevAsKamAuthReply(t *testing.T) { expectedKar := &KamAuthReply{Event: CGR_AUTH_REPLY, TransactionIndex: 29223, TransactionLabel: 698469260, - MaxSessionTime: 1200, ResourceAllowed: true, Suppliers: "supplier1,supplier2"} - if rcvKar, err := kamEv.AsKamAuthReply(1200000000000.0, "supplier1,supplier2", true, nil); err != nil { + MaxSessionTime: 1200, ResourceAllocated: true, Suppliers: "supplier1,supplier2"} + if rcvKar, err := kamEv.AsKamAuthReply(1200000000000.0, "supplier1,supplier2", true, "", nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedKar, rcvKar) { t.Error("Received KAR: ", rcvKar) diff --git a/utils/apitpdata.go b/utils/apitpdata.go index a1ec6a4ee..621917a5c 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1267,7 +1267,8 @@ type TPResourceLimit struct { Filters []*TPRequestFilter // Filters for the request ActivationInterval *TPActivationInterval // Time when this limit becomes active/expires UsageTTL string - Limit string // Limit value + Limit string // Limit value + AllocationMessage string Weight float64 // Weight to sort the ResourceLimits ActionTriggerIDs []string // Thresholds to check after changing Limit }