// Takes the record out of csv and turns it into storedCdr which can be processed by CDRS func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcId string) (*engine.CDR, error) { storedCdr := &engine.CDR{OriginHost: "0.0.0.0", Source: self.cdrcCfgs[cdrcId].CdrSourceId, ExtraFields: make(map[string]string), Cost: -1} var err error var lazyHttpFields []*config.CfgCdrField for _, cdrFldCfg := range self.cdrcCfgs[cdrcId].ContentFields { if utils.IsSliceMember([]string{utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE}, self.dfltCdrcCfg.CdrFormat) { // Hardcode some values in case of flatstore switch cdrFldCfg.FieldId { case utils.ACCID: cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile("3;1;2", utils.INFIELD_SEP) // in case of flatstore, accounting id is made up out of callid, from_tag and to_tag case utils.USAGE: cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile(strconv.Itoa(len(record)-1), utils.INFIELD_SEP) // in case of flatstore, last element will be the duration computed by us } } var fieldVal string if cdrFldCfg.Type == utils.META_COMPOSED { for _, cfgFieldRSR := range cdrFldCfg.Value { if cfgFieldRSR.IsStatic() { fieldVal += cfgFieldRSR.ParseValue("") } else { // Dynamic value extracted using index if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx { return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cdrFldCfg.Tag) } else { fieldVal += cfgFieldRSR.ParseValue(record[cfgFieldIdx]) } } } } else if cdrFldCfg.Type == utils.META_HTTP_POST { lazyHttpFields = append(lazyHttpFields, cdrFldCfg) // Will process later so we can send an estimation of storedCdr to http server } else { return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type) } if err := storedCdr.ParseFieldValue(cdrFldCfg.FieldId, fieldVal, self.timezone); err != nil { return nil, err } } storedCdr.CGRID = utils.Sha1(storedCdr.OriginID, storedCdr.SetupTime.UTC().String()) if storedCdr.ToR == utils.DATA && self.cdrcCfgs[cdrcId].DataUsageMultiplyFactor != 0 { storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * self.cdrcCfgs[cdrcId].DataUsageMultiplyFactor) } for _, httpFieldCfg := range lazyHttpFields { // Lazy process the http fields var outValByte []byte var fieldVal, httpAddr string for _, rsrFld := range httpFieldCfg.Value { httpAddr += rsrFld.ParseValue("") } if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, storedCdr); err != nil && httpFieldCfg.Mandatory { return nil, err } else { fieldVal = string(outValByte) if len(fieldVal) == 0 && httpFieldCfg.Mandatory { return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag) } if err := storedCdr.ParseFieldValue(httpFieldCfg.FieldId, fieldVal, self.timezone); err != nil { return nil, err } } } return storedCdr, nil }
func TestMetaValueExponent(t *testing.T) { m := diam.NewRequest(diam.CreditControl, 4, nil) m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{ diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{ diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{ diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), }, }), diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), }, }), }, }) if val, err := metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil { t.Error(err) } else if val != "0.1" { t.Error("Received: ", val) } if _, err = metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil { t.Error("Should have received error") // Insufficient number arguments } }
func TestDmtAgentPopulateCCTotalOctets(t *testing.T) { daRP := &config.DARequestProcessor{CCAFields: []*config.CfgCdrField{ &config.CfgCdrField{Tag: "GrantedUnit", FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Time", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "GrantedOctet", FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Total-Octets", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), Mandatory: true}, }} ccr := new(CCR) ccr.diamMessage = ccr.AsBareDiameterMessage() cca := NewBareCCAFromCCR(ccr, "cgr-da", "cgrates.org") if err := cca.SetProcessorAVPs(daRP, map[string]string{CGRError: "", CGRMaxUsage: "153600"}); err != nil { t.Error(err) } if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { t.Error(err) } else if len(avps) == 0 { t.Error("Not found") } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template t.Errorf("Expecting 153600, received: %s", strResult) } if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, dict.UndefinedVendorID); err != nil { t.Error(err) } else if len(avps) == 0 { t.Error("Not found") } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template t.Errorf("Expecting 153600, received: %s", strResult) } }
func TestCgrEventPassFilters(t *testing.T) { ev := CgrEvent{"EventName": "TEST_EVENT", "Header1": "Value1", "Header2": "Value2"} if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(TEST_EVENT)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(DUMMY_EVENT)", utils.INFIELD_SEP)) { t.Error("Passing filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("^EventName::TEST_EVENT(TEST_EVENT)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("^EventName::DUMMY", utils.INFIELD_SEP)) { // Should pass since we have no filter defined t.Error("Not passing no filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("~EventName:s/^(.*)_/$1/(TEST)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("~EventName:s/^(\\w*)_/$1/:s/^(\\w)(\\w)(\\w)(\\w)/$1$3$4/(TST)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(TEST_EVENT);Header1(Value1)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(TEST_EVENT);Header1(Value2)", utils.INFIELD_SEP)) { t.Error("Passing filter") } if !ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(TEST_EVENT);~Header1:s/(\\d)/$1/(1)", utils.INFIELD_SEP)) { t.Error("Not passing filter") } if ev.PassFilters(utils.ParseRSRFieldsMustCompile("EventName(TEST_EVENT);~Header1:s/(\\d)/$1/(2)", utils.INFIELD_SEP)) { t.Error("Passing filter") } }
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 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 := fieldOutVal(m, cfgFld, time.Duration(0)); 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 := fieldOutVal(m, cfgFld, time.Duration(0)); 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 := fieldOutVal(m, cfgFld, time.Duration(0)); 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 := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil { t.Error(err) } else if fldOut != eOut { t.Errorf("Expecting: %s, received: %s", eOut, fldOut) } }
func TestRLsV1ResourceLimitsForEvent(t *testing.T) { eLimits := []*ResourceLimit{ &ResourceLimit{ ID: "RL1", 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), }}, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 2, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL2", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(1 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, } var rcvLmts []*ResourceLimit if err := rLS.V1ResourceLimitsForEvent(map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"}, &rcvLmts); err != nil { t.Error(err) } else if len(eLimits) != len(rcvLmts) { t.Errorf("Expecting: %+v, received: %+v", eLimits, rcvLmts) } }
func TestAPItoResourceLimit(t *testing.T) { tpRL := &utils.TPResourceLimit{ TPID: testTPID, ID: "ResGroup1", Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}}, &utils.TPRequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"10", "20"}}, &utils.TPRequestFilter{Type: MetaCDRStats, Values: []string{"CDRST1:*min_ASR:34", "CDRST_1001:*min_ASR:20"}}, &utils.TPRequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"}}, }, ActivationTime: "2014-07-29T15:00:00Z", Weight: 10, Limit: "2", } eRL := &ResourceLimit{ID: tpRL.ID, Weight: tpRL.Weight, Filters: make([]*RequestFilter, len(tpRL.Filters)), Usage: make(map[string]*ResourceUsage)} eRL.Filters[0] = &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}} eRL.Filters[1] = &RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"10", "20"}} eRL.Filters[2] = &RequestFilter{Type: MetaCDRStats, Values: []string{"CDRST1:*min_ASR:34", "CDRST_1001:*min_ASR:20"}, cdrStatSThresholds: []*RFStatSThreshold{ &RFStatSThreshold{QueueID: "CDRST1", ThresholdType: "*MIN_ASR", ThresholdValue: 34}, &RFStatSThreshold{QueueID: "CDRST_1001", ThresholdType: "*MIN_ASR", ThresholdValue: 20}, }} eRL.Filters[3] = &RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"}, rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP), } eRL.ActivationTime, _ = utils.ParseTimeDetectLayout("2014-07-29T15:00:00Z", "UTC") eRL.Limit = 2 if rl, err := APItoResourceLimit(tpRL, "UTC"); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRL, rl) { t.Errorf("Expecting: %+v, received: %+v", eRL, rl) } }
func testOnStorITCacheResourceLimit(t *testing.T) { rL := &ResourceLimit{ ID: "RL_TEST", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, &RequestFilter{Type: MetaRSRFields, Values: []string{"Subject(~^1.*1$)", "Destination(1002)"}, rsrFields: utils.ParseRSRFieldsMustCompile("Subject(~^1.*1$);Destination(1002)", utils.INFIELD_SEP), }}, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 0, time.UTC).Local(), ExpiryTime: time.Date(2015, 7, 3, 13, 43, 0, 0, time.UTC).Local(), Limit: 1, ActionTriggers: make(ActionTriggers, 0), UsageTTL: time.Duration(1 * time.Millisecond), Usage: make(map[string]*ResourceUsage), } if err := onStor.SetResourceLimit(rL, utils.NonTransactional); err != nil { t.Error(err) } if _, hasIt := cache.Get(utils.ResourceLimitsPrefix + rL.ID); hasIt { t.Error("Already in cache") } if err := onStor.CacheDataFromDB(utils.ResourceLimitsPrefix, []string{rL.ID}, false); err != nil { t.Error(err) } if itm, hasIt := cache.Get(utils.ResourceLimitsPrefix + rL.ID); !hasIt { t.Error("Did not cache") } else if rcv := itm.(*ResourceLimit); !reflect.DeepEqual(rL, rcv) { t.Errorf("Expecting: %+v, received: %+v", rL.ActivationTime, rcv.ActivationTime) } }
func TestFwvRecordPassesCfgFilter(t *testing.T) { //record, configKey string) bool { cgrConfig, _ := config.NewDefaultCGRConfig() cdrcConfig := cgrConfig.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0] // We don't really care that is for .csv since all we want to test are the filters cdrcConfig.CdrFilter = utils.ParseRSRFieldsMustCompile(`~52:s/^0(\d{9})/+49${1}/(^+49123123120)`, utils.INFIELD_SEP) fwvRp := &FwvRecordsProcessor{cdrcCfgs: cgrConfig.CdrcProfiles["/var/spool/cgrates/cdrc/in"]} cdrLine := "CDR0000010 0 20120708181506000123451234 0040123123120 004 000018009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009" if passesFilter := fwvRp.recordPassesCfgFilter(cdrLine, cdrcConfig); !passesFilter { t.Error("Not passes filter") } }
func TestXMLHandlerSubstractUsage(t *testing.T) { xp := goxpath.MustParse(path.Join("/broadWorksCDR/cdrData/")) xmlTree := xmltree.MustParseXML(bytes.NewBufferString(cdrXmlBroadsoft), optsNotStrict) cdrs := goxpath.MustExec(xp, xmlTree, nil) cdrWithUsage := cdrs[1] if usage, err := handlerSubstractUsage(cdrWithUsage, utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), "UTC"); err != nil { t.Error(err) } else if usage != time.Duration(13483000000) { t.Errorf("Expected: 13.483s, received: %v", usage) } }
func TestFieldsAsString(t *testing.T) { cdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, Pdd: time.Duration(5) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", } eVal := "call_from_1001" if val := cdr.FieldsAsString(utils.ParseRSRFieldsMustCompile("Category;^_from_;Account", utils.INFIELD_SEP)); val != eVal { t.Errorf("Expecting : %s, received: %s", eVal, val) } }
func TestCCASetProcessorAVPs(t *testing.T) { ccr := &CCR{ // Bare information, just the one needed for answer SessionId: "routinga;1442095190;1476802709", AuthApplicationId: 4, CCRequestType: 1, CCRequestNumber: 0, } ccr.diamMessage = ccr.AsBareDiameterMessage() ccr.diamMessage.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 }}) ccr.debitInterval = time.Duration(300) * time.Second cca := NewBareCCAFromCCR(ccr, "CGR-DA", "cgrates.org") reqProcessor := &config.DARequestProcessor{Id: "UNIT_TEST", // Set template for tests CCAFields: []*config.CfgCdrField{ &config.CfgCdrField{Tag: "Subscription-Id/Subscription-Id-Type", Type: utils.META_COMPOSED, FieldId: "Subscription-Id>Subscription-Id-Type", Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Subscription-Id/Subscription-Id-Data", Type: utils.META_COMPOSED, FieldId: "Subscription-Id>Subscription-Id-Data", Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}, }, } eMessage := cca.AsDiameterMessage() eMessage.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 }}) if err := cca.SetProcessorAVPs(reqProcessor, map[string]string{}); err != nil { t.Error(err) } else if ccaMsg := cca.AsDiameterMessage(); !reflect.DeepEqual(eMessage, ccaMsg) { t.Errorf("Expecting: %+v, received: %+v", eMessage, ccaMsg) } }
func TestRLsMatchingResourceLimitsForEvent(t *testing.T) { rLS = &ResourceLimiterService{dataDB: accountingStorage, cdrStatS: nil} eResLimits := map[string]*ResourceLimit{ "RL1": &ResourceLimit{ ID: "RL1", 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), }}, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 2, Usage: make(map[string]*ResourceUsage), }, "RL2": &ResourceLimit{ ID: "RL2", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(1 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, } if resLimits, err := rLS.matchingResourceLimitsForEvent(map[string]interface{}{"Account": "1002", "Subject": "dan", "Destination": "1002"}); err != nil { t.Error(err) } else if len(eResLimits) != len(resLimits) { t.Errorf("Expecting: %+v, received: %+v", eResLimits, resLimits) } else { for rlID := range eResLimits { if _, hasID := resLimits[rlID]; !hasID { t.Errorf("Expecting: %+v, received: %+v", eResLimits, resLimits) } } // Make sure the filters are what we expect to be after retrieving from cache: fltr := resLimits["RL1"].Filters[1] if pass, _ := fltr.Pass(map[string]interface{}{"Subject": "10000001"}, "", nil); !pass { t.Errorf("Expecting RL: %+v, received: %+v", eResLimits["RL1"], resLimits["RL1"]) } if pass, _ := fltr.Pass(map[string]interface{}{"Account": "1002"}, "", nil); pass { t.Errorf("Expecting RL: %+v, received: %+v", eResLimits["RL1"], resLimits["RL1"]) } } }
func TestCDRAsExportMap(t *testing.T) { cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, } eCDRMp := map[string]string{ utils.CGRID: cdr.CGRID, utils.DESTINATION: "004986517174963", "FieldExtra1": "val_extr1", } expFlds := []*config.CfgCdrField{ &config.CfgCdrField{FieldId: utils.CGRID, Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, &config.CfgCdrField{FieldId: utils.DESTINATION, Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~Destination:s/^\\+(\\d+)$/00${1}/", utils.INFIELD_SEP)}, &config.CfgCdrField{FieldId: "FieldExtra1", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("field_extr1", utils.INFIELD_SEP)}, } if cdrMp, err := cdr.AsExportMap(expFlds, false, nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCDRMp, cdrMp) { t.Errorf("Expecting: %+v, received: %+v", eCDRMp, cdrMp) } }
func TestCsvRecordToCDR(t *testing.T) { cgrConfig, _ := config.NewDefaultCGRConfig() cdrcConfig := cgrConfig.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0] cdrcConfig.CdrSourceId = "TEST_CDRC" cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, FieldId: utils.MEDI_RUNID, Value: utils.ParseRSRFieldsMustCompile("^*default", utils.INFIELD_SEP)}) cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.META_COMPOSED, FieldId: utils.SUPPLIER, Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}}) cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "DisconnectCauseTest", Type: utils.META_COMPOSED, FieldId: utils.DISCONNECT_CAUSE, Value: []*utils.RSRField{&utils.RSRField{Id: "16"}}}) csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: []*config.CdrcConfig{cdrcConfig}} cdrRow := []string{"firstField", "secondField"} _, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig) if err == nil { t.Error("Failed to corectly detect missing fields from record") } cdrRow = []string{"ignored", "ignored", utils.VOICE, "acc1", utils.META_PREPAID, "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1", "NORMAL_DISCONNECT"} rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig) if err != nil { t.Error("Failed to parse CDR in rated cdr", err) } expectedCdr := &engine.CDR{ CGRID: utils.Sha1(cdrRow[3], time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC).String()), RunID: "*default", ToR: cdrRow[2], OriginID: cdrRow[3], OriginHost: "0.0.0.0", // Got it over internal interface Source: "TEST_CDRC", RequestType: cdrRow[4], Direction: cdrRow[5], Tenant: cdrRow[6], Category: cdrRow[7], Account: cdrRow[8], Subject: cdrRow[9], Destination: cdrRow[10], SetupTime: time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC), AnswerTime: time.Date(2013, 2, 3, 19, 54, 0, 0, time.UTC), Usage: time.Duration(62) * time.Second, Supplier: "supplier1", DisconnectCause: "NORMAL_DISCONNECT", ExtraFields: map[string]string{}, Cost: -1, } if !reflect.DeepEqual(expectedCdr, rtCdr) { t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr) } }
func TestPassesFieldFilter(t *testing.T) { m := diam.NewRequest(diam.CreditControl, 4, nil) // Multiple-Services-Credit-Control>Rating-Group if pass, _ := passesFieldFilter(m, utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Rating-Group(^$)", utils.INFIELD_SEP)[0], nil); !pass { t.Error("Does not pass") } }
func cdrLogAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { defaultTemplate := map[string]utils.RSRFields{ utils.TOR: utils.ParseRSRFieldsMustCompile("BalanceType", utils.INFIELD_SEP), utils.CDRHOST: utils.ParseRSRFieldsMustCompile("^127.0.0.1", utils.INFIELD_SEP), utils.DIRECTION: utils.ParseRSRFieldsMustCompile("Directions", utils.INFIELD_SEP), utils.REQTYPE: utils.ParseRSRFieldsMustCompile("^"+utils.META_PREPAID, utils.INFIELD_SEP), utils.TENANT: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP), utils.ACCOUNT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), utils.SUBJECT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), utils.COST: utils.ParseRSRFieldsMustCompile("ActionValue", utils.INFIELD_SEP), } template := make(map[string]string) // overwrite default template if a.ExtraParameters != "" { if err = json.Unmarshal([]byte(a.ExtraParameters), &template); err != nil { return } for field, rsr := range template { defaultTemplate[field], err = utils.ParseRSRFields(rsr, utils.INFIELD_SEP) if err != nil { return err } } } // set stored cdr values var cdrs []*CDR for _, action := range acs { if !utils.IsSliceMember([]string{DEBIT, DEBIT_RESET, TOPUP, TOPUP_RESET}, action.ActionType) || action.Balance == nil { continue // Only log specific actions } cdr := &CDR{RunID: action.ActionType, Source: CDRLOG, SetupTime: time.Now(), AnswerTime: time.Now(), OriginID: utils.GenUUID(), ExtraFields: make(map[string]string)} cdr.CGRID = utils.Sha1(cdr.OriginID, cdr.SetupTime.String()) cdr.Usage = time.Duration(1) * time.Second elem := reflect.ValueOf(cdr).Elem() for key, rsrFlds := range defaultTemplate { parsedValue := parseTemplateValue(rsrFlds, acc, action) field := elem.FieldByName(key) if field.IsValid() && field.CanSet() { switch field.Kind() { case reflect.Float64: value, err := strconv.ParseFloat(parsedValue, 64) if err != nil { continue } field.SetFloat(value) case reflect.String: field.SetString(parsedValue) } } else { // invalid fields go in extraFields of CDR cdr.ExtraFields[key] = parsedValue } } cdrs = append(cdrs, cdr) if cdrStorage == nil { // Only save if the cdrStorage is defined continue } if err := cdrStorage.SetCDR(cdr, true); err != nil { return err } } b, _ := json.Marshal(cdrs) a.ExpirationString = string(b) // testing purpose only return }
func TestLoadCdrcConfigMultipleFiles(t *testing.T) { cgrCfg, err := NewCGRConfigFromFolder(".") if err != nil { t.Error(err) } eCgrCfg, _ := NewDefaultCGRConfig() eCgrCfg.CdrcProfiles = make(map[string][]*CdrcConfig) // Default instance first eCgrCfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"] = []*CdrcConfig{ &CdrcConfig{ ID: utils.META_DEFAULT, Enabled: false, CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}, CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/var/spool/cgrates/cdrc/in", CdrOutDir: "/var/spool/cgrates/cdrc/out", FailedCallsPrefix: "missed_calls", CDRPath: utils.HierarchyPath([]string{""}), CdrSourceId: "freeswitch_csv", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), PartialRecordCache: time.Duration(10) * time.Second, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc1/in"] = []*CdrcConfig{ &CdrcConfig{ ID: "CDRC-CSV1", Enabled: true, CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}, CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc1/in", CdrOutDir: "/tmp/cgrates/cdrc1/out", CDRPath: nil, CdrSourceId: "csv1", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc2/in"] = []*CdrcConfig{ &CdrcConfig{ ID: "CDRC-CSV2", Enabled: true, CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}, CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 0.000976563, RunDelay: 1000000000, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc2/in", CdrOutDir: "/tmp/cgrates/cdrc2/out", CDRPath: nil, CdrSourceId: "csv2", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, &CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, &CfgCdrField{FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc3/in"] = []*CdrcConfig{ &CdrcConfig{ ID: "CDRC-CSV3", Enabled: true, CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}, CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc3/in", CdrOutDir: "/tmp/cgrates/cdrc3/out", CDRPath: nil, CdrSourceId: "csv3", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) { t.Errorf("Expected: \n%s\n, received: \n%s\n", utils.ToJSON(eCgrCfg.CdrcProfiles), utils.ToJSON(cgrCfg.CdrcProfiles)) } }
func cdrLogAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { defaultTemplate := map[string]utils.RSRFields{ "TOR": utils.ParseRSRFieldsMustCompile("balance_type", utils.INFIELD_SEP), "CdrHost": utils.ParseRSRFieldsMustCompile("^127.0.0.1", utils.INFIELD_SEP), "Direction": utils.ParseRSRFieldsMustCompile("direction", utils.INFIELD_SEP), "ReqType": utils.ParseRSRFieldsMustCompile("^"+utils.META_PREPAID, utils.INFIELD_SEP), "Tenant": utils.ParseRSRFieldsMustCompile("tenant", utils.INFIELD_SEP), "Account": utils.ParseRSRFieldsMustCompile("account", utils.INFIELD_SEP), "Subject": utils.ParseRSRFieldsMustCompile("account", utils.INFIELD_SEP), "Cost": utils.ParseRSRFieldsMustCompile("balance_value", utils.INFIELD_SEP), "MediationRunId": utils.ParseRSRFieldsMustCompile("^"+utils.META_DEFAULT, utils.INFIELD_SEP), } template := make(map[string]string) // overwrite default template if a.ExtraParameters != "" { if err = json.Unmarshal([]byte(a.ExtraParameters), &template); err != nil { return } for field, rsr := range template { defaultTemplate[field], err = utils.ParseRSRFields(rsr, utils.INFIELD_SEP) if err != nil { return err } } } // set stored cdr values var cdrs []*StoredCdr for _, action := range acs { if !utils.IsSliceMember([]string{DEBIT, DEBIT_RESET}, action.ActionType) || action.Balance == nil { continue // Only log specific actions } cdr := &StoredCdr{CdrSource: CDRLOG, SetupTime: time.Now(), AnswerTime: time.Now(), AccId: utils.GenUUID(), ExtraFields: make(map[string]string)} cdr.CgrId = utils.Sha1(cdr.AccId, cdr.SetupTime.String()) cdr.Usage = time.Duration(1) * time.Second elem := reflect.ValueOf(cdr).Elem() for key, rsrFlds := range defaultTemplate { parsedValue := parseTemplateValue(rsrFlds, acc, action) field := elem.FieldByName(key) if field.IsValid() && field.CanSet() { switch field.Kind() { case reflect.Float64: value, err := strconv.ParseFloat(parsedValue, 64) if err != nil { continue } field.SetFloat(value) case reflect.String: field.SetString(parsedValue) } } else { // invalid fields go in extraFields of CDR cdr.ExtraFields[key] = parsedValue } } //utils.Logger.Debug(fmt.Sprintf("account: %+v, action: %+v, balance: %+v", acc, action, action.Balance)) cdrs = append(cdrs, cdr) if cdrStorage == nil { // Only save if the cdrStorage is defined continue } if err := cdrStorage.SetCdr(cdr); err != nil { return err } if err := cdrStorage.SetRatedCdr(cdr); err != nil { return err } // FixMe //if err := cdrStorage.LogCallCost(); err != nil { // return err //} } b, _ := json.Marshal(cdrs) a.ExpirationString = string(b) // testing purpose only return }
func TestLoadCdrcConfigMultipleFiles(t *testing.T) { cgrCfg, err := NewCGRConfigFromFolder(".") if err != nil { t.Error(err) } eCgrCfg, _ := NewDefaultCGRConfig() eCgrCfg.CdrcProfiles = make(map[string]map[string]*CdrcConfig) // Default instance first eCgrCfg.CdrcProfiles["/var/log/cgrates/cdrc/in"] = map[string]*CdrcConfig{ "*default": &CdrcConfig{ Enabled: false, Cdrs: "internal", CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/var/log/cgrates/cdrc/in", CdrOutDir: "/var/log/cgrates/cdrc/out", FailedCallsPrefix: "missed_calls", CdrSourceId: "freeswitch_csv", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), PartialRecordCache: time.Duration(10) * time.Second, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "tor", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "accid", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "reqtype", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "setup_time", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "answer_time", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc1/in"] = map[string]*CdrcConfig{ "CDRC-CSV1": &CdrcConfig{ Enabled: true, Cdrs: "internal", CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc1/in", CdrOutDir: "/tmp/cgrates/cdrc1/out", CdrSourceId: "csv1", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "tor", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "accid", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "reqtype", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "setup_time", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "answer_time", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc2/in"] = map[string]*CdrcConfig{ "CDRC-CSV2": &CdrcConfig{ Enabled: true, Cdrs: "internal", CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 0.000976563, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc2/in", CdrOutDir: "/tmp/cgrates/cdrc2/out", CdrSourceId: "csv2", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "", Type: "", FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|generic)$/*$1/", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, &CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, }, TrailerFields: make([]*CfgCdrField, 0), }, } eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc3/in"] = map[string]*CdrcConfig{ "CDRC-CSV3": &CdrcConfig{ Enabled: true, Cdrs: "internal", CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1024, RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/tmp/cgrates/cdrc3/in", CdrOutDir: "/tmp/cgrates/cdrc3/out", CdrSourceId: "csv3", CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{Tag: "tor", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "accid", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "reqtype", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "setup_time", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "answer_time", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, &CfgCdrField{Tag: "usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), }, } if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) { t.Errorf("Expected: %+v, received: %+v", eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) } }
func TestRLsIndexStringFilters(t *testing.T) { rls := []*ResourceLimit{ &ResourceLimit{ ID: "RL1", 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), }}, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 2, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL2", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(1 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL4", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+49"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL5", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+40"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(10 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, } for _, rl := range rls { cache2go.Set(utils.ResourceLimitsPrefix+rl.ID, rl, true, "") } rLS = new(ResourceLimiterService) eIndexes := map[string]map[string]utils.StringMap{ "Account": map[string]utils.StringMap{ "1001": utils.StringMap{ "RL1": true, }, "1002": utils.StringMap{ "RL1": true, "RL2": true, }, "dan": utils.StringMap{ "RL2": true, }, }, "Subject": map[string]utils.StringMap{ "dan": utils.StringMap{ "RL2": true, }, }, utils.NOT_AVAILABLE: map[string]utils.StringMap{ utils.NOT_AVAILABLE: utils.StringMap{ "RL4": true, "RL5": true, }, }, } if err := rLS.indexStringFilters(nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(eIndexes, rLS.stringIndexes) { t.Errorf("Expecting: %+v, received: %+v", eIndexes, rLS.stringIndexes) } rl3 := &ResourceLimit{ ID: "RL3", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"1003"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), } cache2go.Set(utils.ResourceLimitsPrefix+rl3.ID, rl3, true, "") rl6 := &ResourceLimit{ // Add it so we can test expiryTime ID: "RL6", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), } cache2go.Set(utils.ResourceLimitsPrefix+rl6.ID, rl6, true, "") eIndexes = map[string]map[string]utils.StringMap{ "Account": map[string]utils.StringMap{ "1001": utils.StringMap{ "RL1": true, }, "1002": utils.StringMap{ "RL1": true, "RL2": true, }, "dan": utils.StringMap{ "RL2": true, }, }, "Subject": map[string]utils.StringMap{ "dan": utils.StringMap{ "RL2": true, "RL3": true, "RL6": true, }, "1003": utils.StringMap{ "RL3": true, }, }, utils.NOT_AVAILABLE: map[string]utils.StringMap{ utils.NOT_AVAILABLE: utils.StringMap{ "RL4": true, "RL5": true, }, }, } // Test index update if err := rLS.indexStringFilters([]string{rl3.ID, rl6.ID}); err != nil { t.Error(err) } else if !reflect.DeepEqual(eIndexes, rLS.stringIndexes) { t.Errorf("Expecting: %+v, received: %+v", eIndexes, rLS.stringIndexes) } }
func TestXMLRPProcess(t *testing.T) { cdrcCfgs := []*config.CdrcConfig{ &config.CdrcConfig{ ID: "TestXML", Enabled: true, CdrFormat: "xml", DataUsageMultiplyFactor: 1024, CDRPath: utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), CdrSourceId: "TestXML", CdrFilter: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>headerModule>type(Normal)", utils.INFIELD_SEP), ContentFields: []*config.CfgCdrField{ &config.CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>localCallId", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("^*rated", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("~broadWorksCDR>cdrData>basicModule>userId:s/.*@(.*)/${1}/", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>userNumber", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>calledNumber", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>startTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Usage", Type: utils.META_HANDLER, FieldId: utils.USAGE, HandlerId: utils.HandlerSubstractUsage, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, }, }, } xmlRP, err := NewXMLRecordsProcessor(bytes.NewBufferString(cdrXmlBroadsoft), utils.HierarchyPath([]string{"broadWorksCDR", "cdrData"}), "UTC", true, cdrcCfgs) if err != nil { t.Error(err) } var cdrs []*engine.CDR for i := 0; i < 4; i++ { cdrs, err = xmlRP.ProcessNextRecord() if i == 1 { // Take second CDR since the first one cannot be processed break } } if err != nil { t.Error(err) } expectedCDRs := []*engine.CDR{ &engine.CDR{CGRID: "1f045359a0784d15e051d7e41ae30132b139d714", OriginHost: "0.0.0.0", Source: "TestXML", OriginID: "25160047719:0", ToR: "*voice", RequestType: "*rated", Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Destination: "+4986517174963", SetupTime: time.Date(2016, 4, 19, 21, 0, 5, 247000000, time.UTC), AnswerTime: time.Date(2016, 4, 19, 21, 0, 6, 813000000, time.UTC), Usage: time.Duration(13483000000), ExtraFields: map[string]string{}, Cost: -1}, } if !reflect.DeepEqual(expectedCDRs, cdrs) { t.Errorf("Expecting: %+v\n, received: %+v\n", expectedCDRs, cdrs) } }
func TestCgrCfgJSONDefaultsCdreProfiles(t *testing.T) { eFields := []*CfgCdrField{} eContentFlds := []*CfgCdrField{ &CfgCdrField{Tag: "CGRID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("CGRID", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "RunID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("RunID", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "TOR", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "OriginID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "RequestType", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Direction", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Direction", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Tenant", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Category", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Account", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Subject", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Destination", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "SetupTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("SetupTime", utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, &CfgCdrField{Tag: "AnswerTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, &CfgCdrField{Tag: "Usage", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Usage", utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Cost", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), RoundingDecimals: 4}, } eCdreCfg := map[string]*CdreConfig{ "*default": { CdrFormat: "csv", FieldSeparator: ',', DataUsageMultiplyFactor: 1, SMSUsageMultiplyFactor: 1, MMSUsageMultiplyFactor: 1, GenericUsageMultiplyFactor: 1, CostMultiplyFactor: 1, ExportDirectory: "/var/spool/cgrates/cdre", HeaderFields: eFields, ContentFields: eContentFlds, TrailerFields: eFields, }, } if !reflect.DeepEqual(cgrCfg.CdreProfiles, eCdreCfg) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.CdreProfiles, eCdreCfg) } }
func TestCgrCfgJSONDefaultsDiameterAgentCfg(t *testing.T) { reqProc := []*DARequestProcessor{&DARequestProcessor{ Id: "*default", DryRun: false, PublishEvent: false, RequestFilter: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type(0)", utils.INFIELD_SEP), Flags: utils.StringMap{}, ContinueOnSuccess: false, AppendCCA: true, CCRFields: []*CfgCdrField{ &CfgCdrField{Tag: "TOR", FieldId: "ToR", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "OriginID", FieldId: "OriginID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Session-Id", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "RequestType", FieldId: "RequestType", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Direction", FieldId: "Direction", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Tenant", FieldId: "Tenant", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Category", FieldId: "Category", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Account", FieldId: "Account", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Subject", FieldId: "Subject", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Destination", FieldId: "Destination", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Service-Information>IN-Information>Real-Called-Number", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "SetupTime", FieldId: "SetupTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Event-Timestamp", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "AnswerTime", FieldId: "AnswerTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Event-Timestamp", utils.INFIELD_SEP), Mandatory: true}, &CfgCdrField{Tag: "Usage", FieldId: "Usage", Type: "*handler", HandlerId: "*ccr_usage", Mandatory: true}, &CfgCdrField{Tag: "SubscriberID", FieldId: "SubscriberId", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}, }, CCAFields: []*CfgCdrField{ &CfgCdrField{Tag: "GrantedUnits", FieldId: "Granted-Service-Unit>CC-Time", Type: "*handler", HandlerId: "*cca_usage", Mandatory: true}}, }, } testDA := &DiameterAgentCfg{ Enabled: false, Listen: "127.0.0.1:3868", DictionariesDir: "/usr/share/cgrates/diameter/dict/", SMGenericConns: []*HaPoolConfig{&HaPoolConfig{Address: "*internal"}}, PubSubConns: []*HaPoolConfig{}, CreateCDR: true, DebitInterval: 5 * time.Minute, Timezone: "", OriginHost: "CGR-DA", OriginRealm: "cgrates.org", VendorId: 0, ProductName: "CGRateS", RequestProcessors: reqProc, } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.Enabled, testDA.Enabled) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.Enabled, testDA.Enabled) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.Listen, testDA.Listen) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.Listen, testDA.Listen) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.DictionariesDir, testDA.DictionariesDir) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.DictionariesDir, testDA.DictionariesDir) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.SMGenericConns, testDA.SMGenericConns) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.SMGenericConns, testDA.SMGenericConns) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.PubSubConns, testDA.PubSubConns) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.PubSubConns, testDA.PubSubConns) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.CreateCDR, testDA.CreateCDR) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.CreateCDR, testDA.CreateCDR) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.DebitInterval, testDA.DebitInterval) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.DebitInterval, testDA.DebitInterval) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.Timezone, testDA.Timezone) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.diameterAgentCfg.Timezone, testDA.Timezone) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.OriginHost, testDA.OriginHost) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.diameterAgentCfg.OriginHost, testDA.OriginHost) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.OriginRealm, testDA.OriginRealm) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.diameterAgentCfg.OriginRealm, testDA.OriginRealm) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.VendorId, testDA.VendorId) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.diameterAgentCfg.VendorId, testDA.VendorId) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.ProductName, testDA.ProductName) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.diameterAgentCfg.ProductName, testDA.ProductName) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.RequestProcessors, testDA.RequestProcessors) { t.Errorf("expecting: %+v, received: %+v", testDA.RequestProcessors, cgrCfg.diameterAgentCfg.RequestProcessors) } }
func TestRLsLoadRLs(t *testing.T) { rls := []*ResourceLimit{ &ResourceLimit{ ID: "RL1", 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), }}, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 2, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL2", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"dan", "1002"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(1 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL3", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"1003"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL4", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+49"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ ID: "RL5", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaStringPrefix, FieldName: "Destination", Values: []string{"+40"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, UsageTTL: time.Duration(10 * time.Millisecond), Usage: make(map[string]*ResourceUsage), }, &ResourceLimit{ // Add it so we can test expiryTime ID: "RL6", Weight: 10, Filters: []*RequestFilter{ &RequestFilter{Type: MetaString, FieldName: "Subject", Values: []string{"dan"}}, }, ActivationTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), Limit: 1, Usage: make(map[string]*ResourceUsage), }, } rlIdxr, err := NewReqFilterIndexer(accountingStorage, utils.ResourceLimitsIndex) if err != nil { t.Error(err) } for _, rl := range rls { if err := accountingStorage.SetResourceLimit(rl, utils.NonTransactional); err != nil { t.Error(err) } rlIdxr.IndexFilters(rl.ID, rl.Filters) } if err := rlIdxr.StoreIndexes(); err != nil { t.Error(err) } }
func TestCCRAsSMGenericEvent(t *testing.T) { ccr := &CCR{ // Bare information, just the one needed for answer SessionId: "ccrasgen1", AuthApplicationId: 4, CCRequestType: 3, } ccr.diamMessage = ccr.AsBareDiameterMessage() ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{ diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit AVP: []*diam.AVP{ diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(17)), // CC-Time diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1341)), // CC-Input-Octets diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(3079)), // CC-Output-Octets }, }), diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)), }, }) ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{ diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit AVP: []*diam.AVP{ diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(8046)), // CC-Input-Octets diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(46193)), // CC-Output-Octets }, }), diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)), }, }) ccr.diamMessage.NewAVP("FramedIPAddress", avp.Mbit, 0, datatype.OctetString("0AE40041")) cfgFlds := make([]*config.CfgCdrField, 0) eSMGEv := sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR"} if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) } cfgFlds = []*config.CfgCdrField{ &config.CfgCdrField{ Tag: "LastUsed", FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(1)", utils.INFIELD_SEP), FieldId: "LastUsed", Type: "*handler", HandlerId: "*sum", Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), Mandatory: true, }, } eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "54239"} if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) } cfgFlds = []*config.CfgCdrField{ &config.CfgCdrField{ Tag: "LastUsed", FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(99)", utils.INFIELD_SEP), FieldId: "LastUsed", Type: "*handler", HandlerId: "*sum", Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), Mandatory: true, }, } eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "4420"} if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) } }
func TestCgrCfgCDRC(t *testing.T) { JSN_RAW_CFG := ` { "cdrc": [ { "id": "*default", "enabled": true, // enable CDR client functionality "content_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"field_id": "ToR", "type": "*composed", "value": "~7:s/^(voice|data|sms|mms|generic)$/*$1/"}, {"field_id": "AnswerTime", "type": "*composed", "value": "1"}, {"field_id": "Usage", "type": "*composed", "value": "~9:s/^(\\d+)$/${1}s/"}, ], }, ], }` eCgrCfg, _ := NewDefaultCGRConfig() eCgrCfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"] = []*CdrcConfig{ &CdrcConfig{ ID: utils.META_DEFAULT, Enabled: true, DryRun: false, CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}}, CdrFormat: "csv", FieldSeparator: rune(','), DataUsageMultiplyFactor: 1024, Timezone: "", RunDelay: 0, MaxOpenFiles: 1024, CdrInDir: "/var/spool/cgrates/cdrc/in", CdrOutDir: "/var/spool/cgrates/cdrc/out", FailedCallsPrefix: "missed_calls", CDRPath: utils.HierarchyPath([]string{""}), CdrSourceId: "freeswitch_csv", ContinueOnSuccess: false, PartialRecordCache: time.Duration(10 * time.Second), PartialCacheExpiryAction: "*dump_to_file", HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ &CfgCdrField{FieldId: "ToR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP)}, &CfgCdrField{FieldId: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP)}, &CfgCdrField{FieldId: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP)}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } if cgrCfg, err := NewCGRConfigFromJsonStringWithDefaults(JSN_RAW_CFG); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) { t.Errorf("Expected: %+v, received: %+v", eCgrCfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0], cgrCfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0]) } }
func TestCgrCfgJSONDefaultsSureTax(t *testing.T) { localt, err := time.LoadLocation("Local") if err != nil { t.Error("time parsing error", err) } eSureTaxCfg := &SureTaxCfg{ Url: "", ClientNumber: "", ValidationKey: "", BusinessUnit: "", Timezone: localt, IncludeLocalCost: false, ReturnFileCode: "0", ResponseGroup: "03", ResponseType: "D4", RegulatoryCode: "03", ClientTracking: utils.ParseRSRFieldsMustCompile("CGRID", utils.INFIELD_SEP), CustomerNumber: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP), OrigNumber: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP), TermNumber: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP), BillToNumber: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Zipcode: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), P2PZipcode: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), P2PPlus4: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Units: utils.ParseRSRFieldsMustCompile("^1", utils.INFIELD_SEP), UnitType: utils.ParseRSRFieldsMustCompile("^00", utils.INFIELD_SEP), TaxIncluded: utils.ParseRSRFieldsMustCompile("^0", utils.INFIELD_SEP), TaxSitusRule: utils.ParseRSRFieldsMustCompile("^04", utils.INFIELD_SEP), TransTypeCode: utils.ParseRSRFieldsMustCompile("^010101", utils.INFIELD_SEP), SalesTypeCode: utils.ParseRSRFieldsMustCompile("^R", utils.INFIELD_SEP), TaxExemptionCodeList: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), } if !reflect.DeepEqual(cgrCfg.sureTaxCfg, eSureTaxCfg) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.sureTaxCfg, eSureTaxCfg) } }
// Takes the record out of csv and turns it into storedCdr which can be processed by CDRS func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcCfg *config.CdrcConfig) (*engine.CDR, error) { storedCdr := &engine.CDR{OriginHost: "0.0.0.0", Source: cdrcCfg.CdrSourceId, ExtraFields: make(map[string]string), Cost: -1} var err error var lazyHttpFields []*config.CfgCdrField for _, cdrFldCfg := range cdrcCfg.ContentFields { filterBreak := false for _, rsrFilter := range cdrFldCfg.FieldFilter { if rsrFilter == nil { // Nil filter does not need to match anything continue } if cfgFieldIdx, _ := strconv.Atoi(rsrFilter.Id); len(record) <= cfgFieldIdx { return nil, fmt.Errorf("Ignoring record: %v - cannot compile field filter %+v", record, rsrFilter) } else if !rsrFilter.FilterPasses(record[cfgFieldIdx]) { filterBreak = true break } } if filterBreak { // Stop processing this field template since it's filters are not matching continue } if utils.IsSliceMember([]string{utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE}, self.dfltCdrcCfg.CdrFormat) { // Hardcode some values in case of flatstore switch cdrFldCfg.FieldId { case utils.ACCID: cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile("3;1;2", utils.INFIELD_SEP) // in case of flatstore, accounting id is made up out of callid, from_tag and to_tag case utils.USAGE: cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile(strconv.Itoa(len(record)-1), utils.INFIELD_SEP) // in case of flatstore, last element will be the duration computed by us } } var fieldVal string switch cdrFldCfg.Type { case utils.META_COMPOSED, utils.MetaUnixTimestamp: for _, cfgFieldRSR := range cdrFldCfg.Value { if cfgFieldRSR.IsStatic() { fieldVal += cfgFieldRSR.ParseValue("") } else { // Dynamic value extracted using index if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx { return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cdrFldCfg.Tag) } else { strVal := cfgFieldRSR.ParseValue(record[cfgFieldIdx]) if cdrFldCfg.Type == utils.MetaUnixTimestamp { t, _ := utils.ParseTimeDetectLayout(strVal, self.timezone) strVal = strconv.Itoa(int(t.Unix())) } fieldVal += strVal } } } case utils.META_HTTP_POST: lazyHttpFields = append(lazyHttpFields, cdrFldCfg) // Will process later so we can send an estimation of storedCdr to http server default: return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type) } if err := storedCdr.ParseFieldValue(cdrFldCfg.FieldId, fieldVal, self.timezone); err != nil { return nil, err } } storedCdr.CGRID = utils.Sha1(storedCdr.OriginID, storedCdr.SetupTime.UTC().String()) if storedCdr.ToR == utils.DATA && cdrcCfg.DataUsageMultiplyFactor != 0 { storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * cdrcCfg.DataUsageMultiplyFactor) } for _, httpFieldCfg := range lazyHttpFields { // Lazy process the http fields var outValByte []byte var fieldVal, httpAddr string for _, rsrFld := range httpFieldCfg.Value { httpAddr += rsrFld.ParseValue("") } var jsn []byte jsn, err = json.Marshal(storedCdr) if err != nil { return nil, err } if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, jsn); err != nil && httpFieldCfg.Mandatory { return nil, err } else { fieldVal = string(outValByte) if len(fieldVal) == 0 && httpFieldCfg.Mandatory { return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag) } if err := storedCdr.ParseFieldValue(httpFieldCfg.FieldId, fieldVal, self.timezone); err != nil { return nil, err } } } return storedCdr, nil }