mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-14 12:49:54 +05:00
Diameter - *grouped field type, filters in FieldFilter, fixes #318
This commit is contained in:
@@ -68,7 +68,7 @@ func (self *DiameterAgent) handlers() diam.Handler {
|
||||
func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor) (*CCA, error) {
|
||||
passesAllFilters := true
|
||||
for _, fldFilter := range reqProcessor.RequestFilter {
|
||||
if !ccr.passesFieldFilter(fldFilter) {
|
||||
if passes, _ := ccr.passesFieldFilter(fldFilter); !passes {
|
||||
passesAllFilters = false
|
||||
}
|
||||
}
|
||||
|
||||
112
agents/libdmt.go
112
agents/libdmt.go
@@ -282,14 +282,6 @@ func avpValAsString(a *diam.AVP) string {
|
||||
return dataVal[startIdx+1 : endIdx]
|
||||
}
|
||||
|
||||
// Follows the implementation in the StorCdr
|
||||
func (self *CCR) passesFieldFilter(fieldFilter *utils.RSRField) bool {
|
||||
if fieldFilter == nil {
|
||||
return true
|
||||
}
|
||||
return fieldFilter.FilterPasses(self.eventFieldValue(utils.RSRFields{fieldFilter}))
|
||||
}
|
||||
|
||||
// Handler for meta functions
|
||||
func (self *CCR) metaHandler(tag, arg string) (string, error) {
|
||||
switch tag {
|
||||
@@ -300,56 +292,106 @@ func (self *CCR) metaHandler(tag, arg string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (self *CCR) eventFieldValue(fldTpl utils.RSRFields) string {
|
||||
func (self *CCR) avpsWithPath(rsrFld *utils.RSRField) ([]*diam.AVP, error) {
|
||||
hierarchyPath := strings.Split(rsrFld.Id, utils.HIERARCHY_SEP)
|
||||
hpIf := make([]interface{}, len(hierarchyPath))
|
||||
for i, val := range hierarchyPath {
|
||||
hpIf[i] = val
|
||||
}
|
||||
return self.diamMessage.FindAVPsWithPath(hpIf, dict.UndefinedVendorID)
|
||||
}
|
||||
|
||||
// Follows the implementation in the StorCdr
|
||||
func (self *CCR) passesFieldFilter(fieldFilter *utils.RSRField) (bool, int) {
|
||||
if fieldFilter == nil {
|
||||
return true, 0
|
||||
}
|
||||
avps, err := self.avpsWithPath(fieldFilter)
|
||||
if err != nil {
|
||||
return false, 0
|
||||
} else if len(avps) == 0 {
|
||||
return true, 0
|
||||
}
|
||||
for avpIdx, avpVal := range avps {
|
||||
if fieldFilter.FilterPasses(avpValAsString(avpVal)) {
|
||||
return true, avpIdx
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (self *CCR) eventFieldValue(fldTpl utils.RSRFields, avpIdx int) string {
|
||||
var outVal string
|
||||
for _, rsrTpl := range fldTpl {
|
||||
if rsrTpl.IsStatic() {
|
||||
outVal += rsrTpl.ParseValue("")
|
||||
} else {
|
||||
hierarchyPath := strings.Split(rsrTpl.Id, utils.HIERARCHY_SEP)
|
||||
hpIf := make([]interface{}, len(hierarchyPath))
|
||||
for i, val := range hierarchyPath {
|
||||
hpIf[i] = val
|
||||
}
|
||||
matchingAvps, err := self.diamMessage.FindAVPsWithPath(hpIf, dict.UndefinedVendorID)
|
||||
matchingAvps, err := self.avpsWithPath(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))
|
||||
continue // Filter not matching
|
||||
}
|
||||
if len(matchingAvps) <= avpIdx {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Cannot retrieve AVP with index %d for field template with id: %s", avpIdx, rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
if matchingAvps[0].Data.Type() == diam.GroupedAVPType {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Value for field template with id: %s is matching a group AVP, ignoring.", rsrTpl.Id))
|
||||
continue // Not convertible, ignore
|
||||
}
|
||||
outVal += avpValAsString(matchingAvps[0])
|
||||
outVal += avpValAsString(matchingAvps[avpIdx])
|
||||
}
|
||||
}
|
||||
return outVal
|
||||
}
|
||||
|
||||
func (self *CCR) fieldOutVal(cfgFld *config.CfgCdrField) (fmtValOut string, err error) {
|
||||
var outVal string
|
||||
switch cfgFld.Type {
|
||||
case utils.META_FILLER:
|
||||
outVal = cfgFld.Value.Id()
|
||||
cfgFld.Padding = "right"
|
||||
case utils.META_CONSTANT:
|
||||
outVal = cfgFld.Value.Id()
|
||||
case utils.META_HANDLER:
|
||||
outVal, err = self.metaHandler(cfgFld.HandlerId, cfgFld.Layout)
|
||||
if err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error()))
|
||||
}
|
||||
case utils.META_COMPOSED:
|
||||
outVal = self.eventFieldValue(cfgFld.Value, 0)
|
||||
case utils.MetaGrouped: // GroupedAVP
|
||||
passAtIndex := -1
|
||||
matchedAllFilters := true
|
||||
for _, fldFilter := range cfgFld.FieldFilter {
|
||||
var pass bool
|
||||
if pass, passAtIndex = self.passesFieldFilter(fldFilter); !pass {
|
||||
matchedAllFilters = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchedAllFilters {
|
||||
return "", nil // Not matching field filters, will have it empty
|
||||
}
|
||||
if passAtIndex == -1 {
|
||||
passAtIndex = 0 // No filter
|
||||
}
|
||||
outVal = self.eventFieldValue(cfgFld.Value, passAtIndex)
|
||||
}
|
||||
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()))
|
||||
return "", err
|
||||
}
|
||||
return fmtValOut, nil
|
||||
}
|
||||
|
||||
// Extracts data out of CCR into a SMGenericEvent based on the configured template
|
||||
func (self *CCR) AsSMGenericEvent(cfgFlds []*config.CfgCdrField) (sessionmanager.SMGenericEvent, error) {
|
||||
outMap := make(map[string]string) // work with it so we can append values to keys
|
||||
outMap[utils.EVENT_NAME] = DIAMETER_CCR
|
||||
for _, cfgFld := range cfgFlds {
|
||||
var outVal string
|
||||
var err error
|
||||
switch cfgFld.Type {
|
||||
case utils.META_FILLER:
|
||||
outVal = cfgFld.Value.Id()
|
||||
cfgFld.Padding = "right"
|
||||
case utils.META_CONSTANT:
|
||||
outVal = cfgFld.Value.Id()
|
||||
case utils.META_HANDLER:
|
||||
outVal, err = self.metaHandler(cfgFld.HandlerId, cfgFld.Layout)
|
||||
if err != nil {
|
||||
utils.Logger.Warning(fmt.Sprintf("<Diameter> Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error()))
|
||||
}
|
||||
case utils.META_COMPOSED:
|
||||
outVal = self.eventFieldValue(cfgFld.Value)
|
||||
}
|
||||
fmtOut := outVal
|
||||
if fmtOut, 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()))
|
||||
fmtOut, err := self.fieldOutVal(cfgFld)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, hasKey := outMap[cfgFld.FieldId]; !hasKey {
|
||||
|
||||
@@ -19,10 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package agents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/fiorix/go-diameter/diam"
|
||||
"github.com/fiorix/go-diameter/diam/avp"
|
||||
"github.com/fiorix/go-diameter/diam/datatype"
|
||||
@@ -70,8 +71,6 @@ func TestUsageFromCCR(t *testing.T) {
|
||||
}
|
||||
if usage := usageFromCCR(1, 0, 360, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second {
|
||||
t.Error(usage)
|
||||
} else {
|
||||
fmt.Printf("Usage: %v", usage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,3 +81,58 @@ func TestAvpValAsString(t *testing.T) {
|
||||
t.Errorf("Expected: %s, received: %s", originHostStr, avpValStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldOutVal(t *testing.T) {
|
||||
m := diam.NewRequest(diam.CreditControl, 4, nil)
|
||||
m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002"))
|
||||
m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Type
|
||||
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("33708000003")), // Subscription-Id-Data
|
||||
}})
|
||||
m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(1)), // Subscription-Id-Type
|
||||
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000003")), // Subscription-Id-Data
|
||||
}})
|
||||
m.NewAVP("Service-Identifier", avp.Mbit, 0, datatype.Unsigned32(0))
|
||||
m.NewAVP("Requested-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(360))}}) // CC-Time
|
||||
ccr := &CCR{diamMessage: m}
|
||||
cfgFld := &config.CfgCdrField{Tag: "StaticTest", Type: utils.META_COMPOSED, FieldId: utils.TOR,
|
||||
Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut := "*voice"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
}
|
||||
cfgFld = &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION,
|
||||
Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "360"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
}
|
||||
// Without filter, we shoud get always the first subscriptionId
|
||||
cfgFld = &config.CfgCdrField{Tag: "Grouped1", Type: utils.MetaGrouped, FieldId: "Account",
|
||||
Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "33708000003"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
}
|
||||
// Without groupedAVP, we shoud get the first subscriptionId
|
||||
cfgFld = &config.CfgCdrField{Tag: "Grouped2", Type: utils.MetaGrouped, FieldId: "Account",
|
||||
FieldFilter: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type(1)", utils.INFIELD_SEP),
|
||||
Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}
|
||||
eOut = "208708000003"
|
||||
if fldOut, err := ccr.fieldOutVal(cfgFld); err != nil {
|
||||
t.Error(err)
|
||||
} else if fldOut != eOut {
|
||||
t.Errorf("Expecting: %s, received: %s", eOut, fldOut)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ func (self SMGenericEvent) GetOriginatorIP(fieldName string) string {
|
||||
}
|
||||
|
||||
func (self SMGenericEvent) GetCdrSource() string {
|
||||
return utils.SessionManagerGeneric // Needs to match the one in the SMGEvent.saveOperations
|
||||
return utils.SMG // Needs to match the one in the SMGEvent.saveOperations
|
||||
}
|
||||
|
||||
func (self SMGenericEvent) GetExtraFields() map[string]string {
|
||||
|
||||
@@ -206,7 +206,7 @@ func (self *SMGSession) saveOperations() error {
|
||||
var reply string
|
||||
err := self.cdrsrv.LogCallCost(&engine.CallCostLog{
|
||||
CgrId: self.eventStart.GetCgrId(self.timezone),
|
||||
Source: utils.SessionManagerGeneric,
|
||||
Source: utils.SMG,
|
||||
RunId: self.runId,
|
||||
CallCost: firstCC,
|
||||
CheckDuplicate: true,
|
||||
|
||||
@@ -261,7 +261,8 @@ const (
|
||||
NegativePrefix = "!"
|
||||
MatchStartPrefix = "^"
|
||||
MatchEndPrefix = "$"
|
||||
SessionManagerGeneric = "SMG"
|
||||
SMG = "SMG"
|
||||
MetaGrouped = "*grouped"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user