Diameter - completing field templates for individual messages

This commit is contained in:
DanB
2016-02-24 14:07:08 +01:00
parent b039693102
commit f3e5f0bfde
6 changed files with 96 additions and 14 deletions

View File

@@ -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)

View File

@@ -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()))

View File

@@ -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"},
],
},
],

View File

@@ -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},
],
},
],

View File

@@ -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
}

View File

@@ -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!")
}
}