diff --git a/agents/dmtagent.go b/agents/dmtagent.go index f8c6e4a67..3e6f4299f 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -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 } } diff --git a/agents/libdmt.go b/agents/libdmt.go index 52ea81003..510879f9e 100644 --- a/agents/libdmt.go +++ b/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(" 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(" 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(" 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(" 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(" 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(" 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(" 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 { diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index dd1b3c179..dc57d4704 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -19,10 +19,11 @@ along with this program. If not, see 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) + } +} diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index 74e65d9d9..943ad7dd5 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -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 { diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index a0f8cfbe3..c064afd8e 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -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, diff --git a/utils/consts.go b/utils/consts.go index 1fbdb4fa9..38e56d530 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -261,7 +261,8 @@ const ( NegativePrefix = "!" MatchStartPrefix = "^" MatchEndPrefix = "$" - SessionManagerGeneric = "SMG" + SMG = "SMG" + MetaGrouped = "*grouped" ) var (