mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-13 02:56:24 +05:00
Diameter - completing field templates for individual messages
This commit is contained in:
@@ -109,6 +109,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro
|
||||
var maxUsage float64
|
||||
processorVars := make(map[string]string)
|
||||
processorVars[CGRResultCode] = strconv.Itoa(diam.Success)
|
||||
processorVars[CGRError] = ""
|
||||
if reqProcessor.DryRun { // DryRun does not send over network
|
||||
utils.Logger.Info(fmt.Sprintf("<DiameterAgent> SMGenericEvent: %+v", smgEv))
|
||||
processorVars[CGRResultCode] = strconv.Itoa(diam.LimitedSuccess)
|
||||
|
||||
@@ -221,7 +221,7 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e
|
||||
// metaValueExponent will multiply the float value with the exponent provided.
|
||||
// Expects 2 arguments in template separated by |
|
||||
func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) {
|
||||
valStr := composedFieldvalue(m, argsTpl, 0)
|
||||
valStr := composedFieldvalue(m, argsTpl, 0, nil)
|
||||
handlerArgs := strings.Split(valStr, utils.HandlerArgSep)
|
||||
if len(handlerArgs) != 2 {
|
||||
return "", errors.New("Unexpected number of arguments")
|
||||
@@ -258,15 +258,14 @@ func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVa
|
||||
if fieldFilter == nil {
|
||||
return true, 0
|
||||
}
|
||||
if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority
|
||||
if fieldFilter.FilterPasses(val) {
|
||||
return true, 0
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
avps, err := avpsWithPath(m, fieldFilter)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "Could not find AVP") {
|
||||
if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // Could not find it in the message, try to match it in processorVars
|
||||
if fieldFilter.FilterPasses(val) {
|
||||
return true, -1
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
for avpIdx, avpVal := range avps { // First match wins due to index
|
||||
@@ -277,12 +276,16 @@ func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVa
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int) string {
|
||||
func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int, processorVars map[string]string) string {
|
||||
var outVal string
|
||||
for _, rsrTpl := range outTpl {
|
||||
if rsrTpl.IsStatic() {
|
||||
outVal += rsrTpl.ParseValue("")
|
||||
} else {
|
||||
if val, hasIt := processorVars[rsrTpl.Id]; hasIt { // ProcessorVars have priority
|
||||
outVal += rsrTpl.ParseValue(val)
|
||||
continue
|
||||
}
|
||||
matchingAvps, err := avpsWithPath(m, rsrTpl)
|
||||
if err != nil || len(matchingAvps) == 0 {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id))
|
||||
@@ -377,9 +380,9 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa
|
||||
}
|
||||
}
|
||||
case utils.META_COMPOSED:
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, 0)
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, 0, processorVars)
|
||||
case utils.MetaGrouped: // GroupedAVP
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex)
|
||||
outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex, processorVars)
|
||||
}
|
||||
if fmtValOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Error when processing field template with tag: %s, error: %s", cfgFld.Tag, err.Error()))
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
"diameter_agent": {
|
||||
"request_processors": [
|
||||
{
|
||||
"id": "*default", // formal identifier of this processor
|
||||
"request_filter": "Service-Context-Id(nonexistent)", // cancel matching of this processor
|
||||
},
|
||||
{
|
||||
"id": "dryrun1", // formal identifier of this processor
|
||||
"dry_run": true, // do not send the events to SMG, just log them
|
||||
@@ -60,6 +64,12 @@
|
||||
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
|
||||
{"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true},
|
||||
],
|
||||
"cca_fields":[
|
||||
{"tag": "ResultCode", "field_filter":"CGRError(ACCOUNT_NOT_FOUND)",
|
||||
"field_id": "Result-Code", "type": "*constant", "value": "5030"},
|
||||
{"tag": "ResultCode", "field_filter":"CGRError(USER_NOT_FOUND)",
|
||||
"field_id": "Result-Code", "type": "*constant", "value": "5030"},
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
@@ -4,9 +4,38 @@
|
||||
"diameter_agent": {
|
||||
"request_processors": [
|
||||
{
|
||||
"id": "*default", // formal identifier of this processor
|
||||
"id": "VoiceInit", // formal identifier of this processor
|
||||
"dry_run": false, // do not send the events to SMG, just log them
|
||||
"request_filter": "Service-Context-Id(^voice)", // filter requests processed by this processor
|
||||
"request_filter": "Service-Context-Id(^voice);CC-Request-Type(1)", // filter requests processed by this processor
|
||||
"continue_on_success": false, // continue to the next template if executed
|
||||
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
|
||||
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true},
|
||||
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
|
||||
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
|
||||
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true},
|
||||
{"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true},
|
||||
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
|
||||
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
|
||||
{"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true},
|
||||
{"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
|
||||
],
|
||||
"cca_fields":[
|
||||
{"tag": "ResultCode", "field_filter":"CGRError(ACCOUNT_NOT_FOUND)",
|
||||
"field_id": "Result-Code", "type": "*constant", "value": "5030"},
|
||||
{"tag": "ResultCode", "field_filter":"CGRError(USER_NOT_FOUND)",
|
||||
"field_id": "Result-Code", "type": "*constant", "value": "5030"},
|
||||
{"tag": "GrantedUnits", "field_filter":"CGRError(^$)",
|
||||
"field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "VoiceUpdate", // formal identifier of this processor
|
||||
"dry_run": false, // do not send the events to SMG, just log them
|
||||
"request_filter": "Service-Context-Id(^voice);CC-Request-Type(2)", // filter requests processed by this processor
|
||||
"continue_on_success": false, // continue to the next template if executed
|
||||
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
|
||||
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true},
|
||||
@@ -24,7 +53,33 @@
|
||||
{"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
|
||||
],
|
||||
"cca_fields":[ // fields returned in CCA
|
||||
{"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true},
|
||||
{"tag": "GrantedUnits", "field_filter":"CGRError(^$)",
|
||||
"field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "VoiceTerminate", // formal identifier of this processor
|
||||
"dry_run": false, // do not send the events to SMG, just log them
|
||||
"request_filter": "Service-Context-Id(^voice);CC-Request-Type(3)", // filter requests processed by this processor
|
||||
"continue_on_success": false, // continue to the next template if executed
|
||||
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
|
||||
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true},
|
||||
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
|
||||
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
|
||||
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true},
|
||||
{"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true},
|
||||
{"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true},
|
||||
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
|
||||
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
|
||||
{"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true},
|
||||
{"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
|
||||
],
|
||||
"cca_fields":[ // fields returned in CCA
|
||||
{"tag": "GrantedUnits", "field_filter":"CGRError(^$)",
|
||||
"field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -166,6 +166,9 @@ func (rsrFltr *RSRFilter) Pass(val string) bool {
|
||||
if rsrFltr.filterRule[:1] == REGEXP_PREFIX {
|
||||
return rsrFltr.fltrRgxp.MatchString(val) != rsrFltr.negative
|
||||
}
|
||||
if rsrFltr.filterRule == "^$" { // Special case to test empty value
|
||||
return len(val) == 0 != rsrFltr.negative
|
||||
}
|
||||
if rsrFltr.filterRule[:1] == MatchStartPrefix {
|
||||
return strings.HasPrefix(val, rsrFltr.filterRule[1:]) != rsrFltr.negative
|
||||
}
|
||||
|
||||
@@ -341,4 +341,14 @@ func TestRSRFilterPass(t *testing.T) {
|
||||
if fltr.Pass("CGRateS") {
|
||||
t.Error("Passing!")
|
||||
}
|
||||
fltr, err = NewRSRFilter("^$") // Empty value
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if fltr.Pass("CGRateS") {
|
||||
t.Error("Passing!")
|
||||
}
|
||||
if !fltr.Pass("") {
|
||||
t.Error("Not passing!")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user