mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
ResourceLimit with AllocationMessage
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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,,,,,,
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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("<ResourceLimits>, 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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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("<SM-Kamailio> Failed building auth reply %s", err.Error()))
|
||||
} else if err = self.conns[connId].Send(kar.String()); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SM-Kamailio> 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("<SM-Kamailio> Failed building auth reply %s", err.Error()))
|
||||
} else if err = self.conns[connId].Send(kar.String()); err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<SM-Kamailio> 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("<SM-Kamailio> Failed building LCR reply %s", errReply.Error()))
|
||||
@@ -158,7 +158,7 @@ func (self *KamailioSessionManager) onCgrRLReq(evData []byte, connId string) {
|
||||
utils.Logger.Err(fmt.Sprintf("<SM-Kamailio> 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("<SM-Kamailio> Failed building RL reply %s", errReply.Error()))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user