diff --git a/apier/v1/cdre.go b/apier/v1/cdre.go index c2bb3eb17..990bd8b30 100644 --- a/apier/v1/cdre.go +++ b/apier/v1/cdre.go @@ -34,7 +34,6 @@ import ( "unicode/utf8" "github.com/cgrates/cgrates/cdre" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -93,28 +92,11 @@ func (self *ApierV1) ExportCdrsToZipString(attr utils.AttrExpFileCdrs, reply *st // Export Cdrs to file func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.ExportedFileCdrs) error { var err error - exportTemplate := self.Config.CdreDefaultInstance - if attr.ExportTemplate != nil && len(*attr.ExportTemplate) != 0 { // XML Template defined, can be field names or xml reference - if strings.HasPrefix(*attr.ExportTemplate, utils.XML_PROFILE_PREFIX) { - if self.Config.XmlCfgDocument == nil { - return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, "XmlDocumentNotLoaded") - } - expTplStr := *attr.ExportTemplate - if xmlTemplates := self.Config.XmlCfgDocument.GetCdreCfgs(expTplStr[len(utils.XML_PROFILE_PREFIX):]); xmlTemplates == nil { - return fmt.Errorf("%s:ExportTemplate", utils.ERR_NOT_FOUND) - } else { - if exportTemplate, err = config.NewCdreConfigFromXmlCdreCfg(xmlTemplates[expTplStr[len(utils.XML_PROFILE_PREFIX):]]); err != nil { - return fmt.Errorf("%s:ExportTemplate:%s", utils.ERR_SERVER_ERROR, err.Error()) - } - } - } else { - exportTemplate = config.NewDefaultCdreConfig() - if contentFlds, err := config.NewCfgCdrFieldsFromIds(exportTemplate.CdrFormat == utils.CDRE_FIXED_WIDTH, - strings.Split(*attr.ExportTemplate, string(utils.CSV_SEP))...); err != nil { - return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) - } else { - exportTemplate.ContentFields = contentFlds - } + exportTemplate := self.Config.CdreProfiles[utils.META_DEFAULT] + if attr.ExportTemplate != nil && len(*attr.ExportTemplate) != 0 { // Export template prefered, use it + var hasIt bool + if exportTemplate, hasIt = self.Config.CdreProfiles[*attr.ExportTemplate]; !hasIt { + return fmt.Errorf("%s:ExportTemplate", utils.ERR_NOT_FOUND) } } if exportTemplate == nil { diff --git a/apier/v1/derivedcharging.go b/apier/v1/derivedcharging.go index f54d80400..25dfb7a81 100644 --- a/apier/v1/derivedcharging.go +++ b/apier/v1/derivedcharging.go @@ -29,7 +29,7 @@ func (self *ApierV1) GetDerivedChargers(attrs utils.AttrDerivedChargers, reply * if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Direction", "Account", "Subject"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - if hDc, err := engine.HandleGetDerivedChargers(self.AccountDb, self.Config, attrs); err != nil { + if hDc, err := engine.HandleGetDerivedChargers(self.AccountDb, attrs); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } else if hDc != nil { *reply = hDc diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index cd0eb162b..5c16dd8a8 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -29,7 +29,6 @@ import ( "path" "strconv" "time" - "unicode/utf8" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -83,12 +82,8 @@ func populateStoredCdrField(cdr *utils.StoredCdr, fieldId, fieldVal string) erro } func NewCdrc(cdrcCfg *config.CdrcConfig, httpSkipTlsCheck bool, cdrServer *engine.CDRS) (*Cdrc, error) { - if len(cdrcCfg.FieldSeparator) != 1 { - return nil, fmt.Errorf("Unsupported csv separator: %s", cdrcCfg.FieldSeparator) - } - csvSepRune, _ := utf8.DecodeRune([]byte(cdrcCfg.FieldSeparator)) cdrc := &Cdrc{cdrsAddress: cdrcCfg.CdrsAddress, CdrFormat: cdrcCfg.CdrFormat, cdrInDir: cdrcCfg.CdrInDir, cdrOutDir: cdrcCfg.CdrOutDir, - cdrSourceId: cdrcCfg.CdrSourceId, runDelay: cdrcCfg.RunDelay, csvSep: csvSepRune, duMultiplyFactor: cdrcCfg.DataUsageMultiplyFactor, cdrFields: cdrcCfg.CdrFields, httpSkipTlsCheck: httpSkipTlsCheck, cdrServer: cdrServer} + cdrSourceId: cdrcCfg.CdrSourceId, runDelay: cdrcCfg.RunDelay, csvSep: cdrcCfg.FieldSeparator, duMultiplyFactor: cdrcCfg.DataUsageMultiplyFactor, cdrFields: cdrcCfg.CdrFields, httpSkipTlsCheck: httpSkipTlsCheck, cdrServer: cdrServer} // Before processing, make sure in and out folders exist for _, dir := range []string{cdrc.cdrInDir, cdrc.cdrOutDir} { if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { diff --git a/cdrc/cdrc_local_test.go b/cdrc/cdrc_local_test.go index 39ab0ad0a..a9eba223f 100644 --- a/cdrc/cdrc_local_test.go +++ b/cdrc/cdrc_local_test.go @@ -96,10 +96,10 @@ func TestLoadConfigt(*testing.T) { if !*testLocal { return } - cfgPath = path.Join(*dataDir, "conf", "samples", "apier_local_test.cfg") - cfg, _ = config.NewCGRConfigFromFile(&cfgPath) - if len(cfg.CdrcInstances) > 0 { - cdrcCfg = cfg.CdrcInstances[0] + cfgPath = path.Join(*dataDir, "conf", "samples", "apier") + cfg, _ = config.NewCGRConfigFromFolder(cfgPath) + if len(cfg.CdrcProfiles) > 0 { + cdrcCfg = cfg.CdrcProfiles[utils.META_DEFAULT] } } diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go index 2b9b7e108..c9db55b69 100644 --- a/cdrc/cdrc_test.go +++ b/cdrc/cdrc_test.go @@ -29,7 +29,7 @@ import ( func TestRecordForkCdr(t *testing.T) { cgrConfig, _ := config.NewDefaultCGRConfig() - cdrcConfig := cgrConfig.CdrcInstances[0] + cdrcConfig := cgrConfig.CdrcProfiles[utils.META_DEFAULT] cdrcConfig.CdrFields = append(cdrcConfig.CdrFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.CDRFIELD, CdrFieldId: "supplier", Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}}) cdrc := &Cdrc{CdrFormat: CSV, cdrSourceId: "TEST_CDRC", cdrFields: cdrcConfig.CdrFields} cdrRow := []string{"firstField", "secondField"} diff --git a/cdre/cdrexporter_test.go b/cdre/cdrexporter_test.go index b147f312c..9c274bfb4 100644 --- a/cdre/cdrexporter_test.go +++ b/cdre/cdrexporter_test.go @@ -51,14 +51,14 @@ func TestCdreGetCombimedCdrFieldVal(t *testing.T) { Category: "call", Account: "1000", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(), Usage: time.Duration(10) * time.Second, MediationRunId: "RETAIL1", Cost: 5.01}, } - cdre, err := NewCdrExporter(cdrs, logDb, cfg.CdreDefaultInstance, cfg.CdreDefaultInstance.CdrFormat, cfg.CdreDefaultInstance.FieldSeparator, + cdre, err := NewCdrExporter(cdrs, logDb, cfg.CdreProfiles["*default"], cfg.CdreProfiles["*default"].CdrFormat, cfg.CdreProfiles["*default"].FieldSeparator, "firstexport", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) if err != nil { t.Error("Unexpected error received: ", err) } fltrRule, _ := utils.ParseRSRFields("~mediation_runid:s/default/RUN_RTL/", utils.INFIELD_SEP) val, _ := utils.ParseRSRFields("cost", utils.INFIELD_SEP) - cfgCdrFld, _ := config.NewCfgCdrFieldWithDefaults(false, val, fltrRule, nil, nil, nil, nil, nil, nil, nil, nil) + cfgCdrFld := &config.CfgCdrField{Tag: "cost", Type: "cdrfield", CdrFieldId: "cost", Value: val, Filter: fltrRule} if costVal, err := cdre.getCombimedCdrFieldVal(cdrs[3], cfgCdrFld); err != nil { t.Error(err) } else if costVal != "1.01" { @@ -66,7 +66,7 @@ func TestCdreGetCombimedCdrFieldVal(t *testing.T) { } fltrRule, _ = utils.ParseRSRFields("~mediation_runid:s/default/RETAIL1/", utils.INFIELD_SEP) val, _ = utils.ParseRSRFields("account", utils.INFIELD_SEP) - cfgCdrFld, _ = config.NewCfgCdrFieldWithDefaults(false, val, fltrRule, nil, nil, nil, nil, nil, nil, nil, nil) + cfgCdrFld = &config.CfgCdrField{Tag: "account", Type: "cdrfield", CdrFieldId: "account", Value: val, Filter: fltrRule} if acntVal, err := cdre.getCombimedCdrFieldVal(cdrs[3], cfgCdrFld); err != nil { t.Error(err) } else if acntVal != "1000" { @@ -83,7 +83,7 @@ func TestGetDateTimeFieldVal(t *testing.T) { ExtraFields: map[string]string{"stop_time": "2014-06-11 19:19:00 +0000 UTC", "fieldextr2": "valextr2"}} val, _ := utils.ParseRSRFields("stop_time", utils.INFIELD_SEP) layout := "2006-01-02 15:04:05" - cfgCdrFld, _ := config.NewCfgCdrFieldWithDefaults(false, val, nil, nil, nil, nil, nil, &layout, nil, nil, nil) + cfgCdrFld := &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, Layout: layout} if cdrVal, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err != nil { t.Error(err) } else if cdrVal != "2014-06-11 19:19:00" { @@ -91,12 +91,12 @@ func TestGetDateTimeFieldVal(t *testing.T) { } // Test filter fltr, _ := utils.ParseRSRFields("~tenant:s/(.+)/itsyscom.com/", utils.INFIELD_SEP) - cfgCdrFld, _ = config.NewCfgCdrFieldWithDefaults(false, val, fltr, nil, nil, nil, nil, &layout, nil, nil, nil) + cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, Filter: fltr, Layout: layout} if _, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err == nil { t.Error(err) } val, _ = utils.ParseRSRFields("fieldextr2", utils.INFIELD_SEP) - cfgCdrFld, _ = config.NewCfgCdrFieldWithDefaults(false, val, nil, nil, nil, nil, nil, &layout, nil, nil, nil) + cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, Layout: layout} // Test time parse error if _, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err == nil { t.Error("Should give error here, got none.") @@ -110,14 +110,14 @@ func TestCdreCdrFieldValue(t *testing.T) { Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(), Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 1.01} val, _ := utils.ParseRSRFields("destination", utils.INFIELD_SEP) - cfgCdrFld, _ := config.NewCfgCdrFieldWithDefaults(false, val, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cfgCdrFld := &config.CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: val} if val, err := cdre.cdrFieldValue(cdr, cfgCdrFld); err != nil { t.Error(err) } else if val != cdr.Destination { t.Errorf("Expecting: %s, received: %s", cdr.Destination, val) } fltr, _ := utils.ParseRSRFields("~tenant:s/(.+)/itsyscom.com/", utils.INFIELD_SEP) - cfgCdrFld, _ = config.NewCfgCdrFieldWithDefaults(false, val, fltr, nil, nil, nil, nil, nil, nil, nil, nil) + cfgCdrFld = &config.CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: val, Filter: fltr} if _, err := cdre.cdrFieldValue(cdr, cfgCdrFld); err == nil { t.Error("Failed to use filter") } diff --git a/cdre/csv_test.go b/cdre/csv_test.go index 14c4732f8..e5e78cb72 100644 --- a/cdre/csv_test.go +++ b/cdre/csv_test.go @@ -39,7 +39,8 @@ func TestCsvCdrWriter(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, } - cdre, err := NewCdrExporter([]*utils.StoredCdr{storedCdr1}, logDb, cfg.CdreDefaultInstance, utils.CSV, ',', "firstexport", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) + cdre, err := NewCdrExporter([]*utils.StoredCdr{storedCdr1}, logDb, cfg.CdreProfiles["*default"], utils.CSV, ',', "firstexport", 0.0, 0.0, 0, 4, + cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) if err != nil { t.Error("Unexpected error received: ", err) } @@ -67,7 +68,7 @@ func TestAlternativeFieldSeparator(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, } - cdre, err := NewCdrExporter([]*utils.StoredCdr{storedCdr1}, logDb, cfg.CdreDefaultInstance, utils.CSV, '|', "firstexport", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) + cdre, err := NewCdrExporter([]*utils.StoredCdr{storedCdr1}, logDb, cfg.CdreProfiles["*default"], utils.CSV, '|', "firstexport", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify) if err != nil { t.Error("Unexpected error received: ", err) } diff --git a/cdre/fixedwidth_test.go b/cdre/fixedwidth_test.go index 91fc8f29a..6f661137b 100644 --- a/cdre/fixedwidth_test.go +++ b/cdre/fixedwidth_test.go @@ -29,65 +29,95 @@ import ( "github.com/cgrates/cgrates/utils" ) -var hdrCfgFlds = []*config.XmlCfgCdrField{ - &config.XmlCfgCdrField{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("10"), Width: utils.IntPointer(2)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("VOI"), Width: utils.IntPointer(3)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer("export_id"), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("LastCdr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_LASTCDRATIME), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("FileCreationfTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer("time_now"), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("FileVersion"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("01"), Width: utils.IntPointer(2)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(105)}, +var hdrJsnCfgFlds = []*config.CdrFieldJsonCfg{ + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^10"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^VOI"), Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer("export_id"), + Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdr"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_LASTCDRATIME), + Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileCreationfTime"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer("time_now"), + Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileVersion"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^01"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(105)}, } -var contentCfgFlds = []*config.XmlCfgCdrField{ - &config.XmlCfgCdrField{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("20"), Width: utils.IntPointer(2)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Account"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCOUNT), Width: utils.IntPointer(12), Strip: utils.StringPointer("left"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SUBJECT), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("CLI"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("cli"), Width: utils.IntPointer(15), Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.DESTINATION), Width: utils.IntPointer(24), Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("02"), Width: utils.IntPointer(2)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("SubtypeTOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("11"), Width: utils.IntPointer(4), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("SetupTime"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SETUP_TIME), Width: utils.IntPointer(12), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), - Layout: utils.StringPointer("020106150400")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Duration"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.USAGE), Width: utils.IntPointer(6), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), - Layout: utils.StringPointer(utils.SECONDS)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("DataVolume"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(6)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("TaxCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("1"), Width: utils.IntPointer(1)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("OperatorCode"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("opercode"), Width: utils.IntPointer(2), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("ProductId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("productid"), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("NetworkId"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("3"), Width: utils.IntPointer(1)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("CallId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCID), Width: utils.IntPointer(16), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("TerminationCode"), Type: utils.StringPointer(CONCATENATED_CDRFIELD), Value: utils.StringPointer("operator,product"), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Cost"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.COST), Width: utils.IntPointer(9), Padding: utils.StringPointer("zeroleft")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("DestinationPrivacy"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_MASKDESTINATION), Width: utils.IntPointer(1)}, +var contentJsnCfgFlds = []*config.CdrFieldJsonCfg{ + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^20"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCOUNT), Width: utils.IntPointer(12), + Strip: utils.StringPointer("left"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SUBJECT), Width: utils.IntPointer(5), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("CLI"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("cli"), Width: utils.IntPointer(15), + Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.DESTINATION), Width: utils.IntPointer(24), + Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^02"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("SubtypeTOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^11"), Width: utils.IntPointer(4), + Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SETUP_TIME), Width: utils.IntPointer(12), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), Layout: utils.StringPointer("020106150400")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Duration"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.USAGE), Width: utils.IntPointer(6), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), Layout: utils.StringPointer(utils.SECONDS)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DataVolume"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(6)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TaxCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^1"), Width: utils.IntPointer(1)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("OperatorCode"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("opercode"), Width: utils.IntPointer(2), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("ProductId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("productid"), Width: utils.IntPointer(5), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("NetworkId"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^3"), Width: utils.IntPointer(1)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("CallId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCID), Width: utils.IntPointer(16), + Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TerminationCode"), Type: utils.StringPointer(CONCATENATED_CDRFIELD), Value: utils.StringPointer("operator,product"), + Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.COST), Width: utils.IntPointer(9), + Padding: utils.StringPointer("zeroleft")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DestinationPrivacy"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_MASKDESTINATION), + Width: utils.IntPointer(1)}, } -var trailerCfgFlds = []*config.XmlCfgCdrField{ - &config.XmlCfgCdrField{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("90"), Width: utils.IntPointer(2)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("VOI"), Width: utils.IntPointer(3)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_EXPORTID), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("NumberOfRecords"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_NRCDRS), Width: utils.IntPointer(6), Padding: utils.StringPointer("zeroleft")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("CdrsDuration"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_DURCDRS), Width: utils.IntPointer(8), Padding: utils.StringPointer("zeroleft"), Layout: utils.StringPointer(utils.SECONDS)}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("FirstCdrTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_FIRSTCDRATIME), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("LastCdrTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_LASTCDRATIME), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.XmlCfgCdrField{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(93)}, +var trailerJsnCfgFlds = []*config.CdrFieldJsonCfg{ + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^90"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("^VOI"), Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_EXPORTID), Width: utils.IntPointer(5), + Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("NumberOfRecords"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_NRCDRS), + Width: utils.IntPointer(6), Padding: utils.StringPointer("zeroleft")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("CdrsDuration"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_DURCDRS), + Width: utils.IntPointer(8), Padding: utils.StringPointer("zeroleft"), Layout: utils.StringPointer(utils.SECONDS)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FirstCdrTime"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_FIRSTCDRATIME), + Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdrTime"), Type: utils.StringPointer(utils.METATAG), Cdr_field_id: utils.StringPointer(META_LASTCDRATIME), + Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(93)}, } +var hdrCfgFlds, contentCfgFlds, trailerCfgFlds []*config.CfgCdrField + // Write one CDR and test it's results only for content buffer func TestWriteCdr(t *testing.T) { + var err error wrBuf := &bytes.Buffer{} logDb, _ := engine.NewMapStorage() cfg, _ := config.NewDefaultCGRConfig() - fixedWidth := utils.CDRE_FIXED_WIDTH - exportTpl := &config.CgrXmlCdreCfg{ - CdrFormat: &fixedWidth, - Header: &config.CgrXmlCfgCdrHeader{Fields: hdrCfgFlds}, - Content: &config.CgrXmlCfgCdrContent{Fields: contentCfgFlds}, - Trailer: &config.CgrXmlCfgCdrTrailer{Fields: trailerCfgFlds}, + if hdrCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(hdrJsnCfgFlds); err != nil { + t.Error(err) + } + if contentCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(contentJsnCfgFlds); err != nil { + t.Error(err) + } + if trailerCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(trailerJsnCfgFlds); err != nil { + t.Error(err) + } + cdreCfg := &config.CdreConfig{ + CdrFormat: utils.CDRE_FIXED_WIDTH, + HeaderFields: hdrCfgFlds, + ContentFields: contentCfgFlds, + TrailerFields: trailerCfgFlds, } cdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), TOR: utils.VOICE, OrderId: 1, AccId: "dsafdsaf", CdrHost: "192.168.1.1", @@ -98,17 +128,13 @@ func TestWriteCdr(t *testing.T) { Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } - cdreCfg, err := config.NewCdreConfigFromXmlCdreCfg(exportTpl) - if err != nil { - t.Error(err) - } cdre, err := NewCdrExporter([]*utils.StoredCdr{cdr}, logDb, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify) if err != nil { t.Error(err) } - eHeader := "10 VOI0000007111308420024031415390001 \n" + eHeader := "10 VOIfwv_107111308420018011511340001 \n" eContentOut := "201001 1001 1002 0211 07111308420010 1 3dsafdsaf 0002.34570\n" - eTrailer := "90 VOI0000000000100000010071113084260071113084200 \n" + eTrailer := "90 VOIfwv_100000100000010071113084200071113084200 \n" if err := cdre.writeOut(wrBuf); err != nil { t.Error(err) } @@ -145,12 +171,11 @@ func TestWriteCdr(t *testing.T) { func TestWriteCdrs(t *testing.T) { wrBuf := &bytes.Buffer{} logDb, _ := engine.NewMapStorage() - fixedWidth := utils.CDRE_FIXED_WIDTH - exportTpl := &config.CgrXmlCdreCfg{ - CdrFormat: &fixedWidth, - Header: &config.CgrXmlCfgCdrHeader{Fields: hdrCfgFlds}, - Content: &config.CgrXmlCfgCdrContent{Fields: contentCfgFlds}, - Trailer: &config.CgrXmlCfgCdrTrailer{Fields: trailerCfgFlds}, + cdreCfg := &config.CdreConfig{ + CdrFormat: utils.CDRE_FIXED_WIDTH, + HeaderFields: hdrCfgFlds, + ContentFields: contentCfgFlds, + TrailerFields: trailerCfgFlds, } cdr1 := &utils.StoredCdr{CgrId: utils.Sha1("aaa1", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), TOR: utils.VOICE, OrderId: 2, AccId: "aaa1", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org", @@ -178,10 +203,6 @@ func TestWriteCdrs(t *testing.T) { ExtraFields: map[string]string{"productnumber": "12344", "fieldextr2": "valextr2"}, } cfg, _ := config.NewDefaultCGRConfig() - cdreCfg, err := config.NewCdreConfigFromXmlCdreCfg(exportTpl) - if err != nil { - t.Error(err) - } cdre, err := NewCdrExporter([]*utils.StoredCdr{cdr1, cdr2, cdr3, cdr4}, logDb, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify) if err != nil { diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 0fb3954ce..51e56a156 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -55,7 +55,7 @@ const ( ) var ( - cfgPath = flag.String("config", "/etc/cgrates/cgrates.cfg", "Configuration file location.") + cfgDir = flag.String("config_dir", "/etc/cgrates", "Configuration directory path.") version = flag.Bool("version", false, "Prints the application version.") raterEnabled = flag.Bool("rater", false, "Enforce starting of the rater daemon overwriting config") schedEnabled = flag.Bool("scheduler", false, "Enforce starting of the scheduler daemon .overwriting config") @@ -325,7 +325,7 @@ func main() { } // runtime.GOMAXPROCS(runtime.NumCPU()) // For now it slows down computing due to CPU management, to be reviewed in future Go releases - cfg, err = config.NewCGRConfigFromFile(cfgPath) + cfg, err = config.NewCGRConfigFromFolder(*cfgDir) if err != nil { engine.Logger.Crit(fmt.Sprintf("Could not parse config: %s exiting!", err)) return @@ -502,7 +502,7 @@ func main() { go shutdownSessionmanagerSingnalHandler() } var cdrcEnabled bool - for _, cdrcConfig := range cfg.CdrcInstances { + for _, cdrcConfig := range cfg.CdrcProfiles { if cdrcConfig.Enabled == false { continue // Ignore not enabled } else if !cdrcEnabled { diff --git a/config/cdrcconfig.go b/config/cdrcconfig.go index bec3d904a..829834144 100644 --- a/config/cdrcconfig.go +++ b/config/cdrcconfig.go @@ -19,186 +19,14 @@ along with this program. If not, see package config import ( - "code.google.com/p/goconf/conf" - "fmt" - "github.com/cgrates/cgrates/utils" - "strings" "time" ) -func NewCdrcConfigFromCgrXmlCdrcCfg(id string, xmlCdrcCfg *CgrXmlCdrcCfg) (*CdrcConfig, error) { - cdrcCfg := NewDefaultCdrcConfig() - cdrcCfg.Id = id - if xmlCdrcCfg.Enabled != nil { - cdrcCfg.Enabled = *xmlCdrcCfg.Enabled - } - if xmlCdrcCfg.CdrsAddress != nil { - cdrcCfg.CdrsAddress = *xmlCdrcCfg.CdrsAddress - } - if xmlCdrcCfg.CdrFormat != nil { - cdrcCfg.CdrFormat = *xmlCdrcCfg.CdrFormat - } - if xmlCdrcCfg.FieldSeparator != nil { - cdrcCfg.FieldSeparator = *xmlCdrcCfg.FieldSeparator - } - if xmlCdrcCfg.DataUsageMultiplyFactor != nil { - cdrcCfg.DataUsageMultiplyFactor = *xmlCdrcCfg.DataUsageMultiplyFactor - } - if xmlCdrcCfg.RunDelay != nil { - cdrcCfg.RunDelay = time.Duration(*xmlCdrcCfg.RunDelay) * time.Second - } - if xmlCdrcCfg.CdrInDir != nil { - cdrcCfg.CdrInDir = *xmlCdrcCfg.CdrInDir - } - if xmlCdrcCfg.CdrOutDir != nil { - cdrcCfg.CdrOutDir = *xmlCdrcCfg.CdrOutDir - } - if xmlCdrcCfg.CdrSourceId != nil { - cdrcCfg.CdrSourceId = *xmlCdrcCfg.CdrSourceId - } - if len(xmlCdrcCfg.CdrFields) != 0 { - cdrcCfg.CdrFields = nil // Reinit the fields, so we do not inherit from defaults here - } - for _, xmlCdrField := range xmlCdrcCfg.CdrFields { - if cdrFld, err := NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlCdrField, cdrcCfg.CdrFormat == utils.CDRE_FIXED_WIDTH); err != nil { - return nil, err - } else { - cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, cdrFld) - } - } - return cdrcCfg, nil -} - -func NewDefaultCdrcConfig() *CdrcConfig { - torTag, accIdTag, reqTypeTag, dirTag, tenantTag, categTag, acntTag, subjTag, dstTag, sTimeTag, aTimeTag, usageTag := utils.TOR, - utils.ACCID, utils.REQTYPE, utils.DIRECTION, utils.TENANT, utils.CATEGORY, utils.ACCOUNT, utils.SUBJECT, utils.DESTINATION, utils.SETUP_TIME, utils.ANSWER_TIME, utils.USAGE - torFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "2"}}, nil, nil, &torTag, nil, nil, nil, nil, nil, nil) - accIdFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "3"}}, nil, nil, &accIdTag, nil, nil, nil, nil, nil, nil) - reqTypeFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "4"}}, nil, nil, &reqTypeTag, nil, nil, nil, nil, nil, nil) - directionFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "5"}}, nil, nil, &dirTag, nil, nil, nil, nil, nil, nil) - tenantFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "6"}}, nil, nil, &tenantTag, nil, nil, nil, nil, nil, nil) - categoryFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "7"}}, nil, nil, &categTag, nil, nil, nil, nil, nil, nil) - acntFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "8"}}, nil, nil, &acntTag, nil, nil, nil, nil, nil, nil) - subjFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "9"}}, nil, nil, &subjTag, nil, nil, nil, nil, nil, nil) - dstFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "10"}}, nil, nil, &dstTag, nil, nil, nil, nil, nil, nil) - setupTimeFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "11"}}, nil, nil, &sTimeTag, nil, nil, nil, nil, nil, nil) - answerTimeFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "12"}}, nil, nil, &aTimeTag, nil, nil, nil, nil, nil, nil) - usageFld, _ := NewCfgCdrFieldWithDefaults(false, []*utils.RSRField{&utils.RSRField{Id: "13"}}, nil, nil, &usageTag, nil, nil, nil, nil, nil, nil) - cdrcCfg := &CdrcConfig{ - Id: utils.META_DEFAULT, - Enabled: false, - CdrsAddress: "", - CdrFormat: utils.CSV, - FieldSeparator: utils.FIELDS_SEP, - DataUsageMultiplyFactor: 1.0, - RunDelay: time.Duration(0), - CdrInDir: "/var/log/cgrates/cdrc/in", - CdrOutDir: "/var/log/cgrates/cdrc/out", - CdrSourceId: utils.CSV, - CdrFields: []*CfgCdrField{torFld, accIdFld, reqTypeFld, directionFld, tenantFld, categoryFld, acntFld, subjFld, dstFld, setupTimeFld, answerTimeFld, usageFld}, - } - return cdrcCfg -} - -func NewCdrcConfigFromFileParams(c *conf.ConfigFile) (*CdrcConfig, error) { - var err error - cdrcCfg := NewDefaultCdrcConfig() - if hasOpt := c.HasOption("cdrc", "enabled"); hasOpt { - cdrcCfg.Enabled, _ = c.GetBool("cdrc", "enabled") - } - if hasOpt := c.HasOption("cdrc", "cdrs"); hasOpt { - cdrcCfg.CdrsAddress, _ = c.GetString("cdrc", "cdrs") - } - if hasOpt := c.HasOption("cdrc", "cdr_format"); hasOpt { - cdrcCfg.CdrFormat, _ = c.GetString("cdrc", "cdr_format") - } - if hasOpt := c.HasOption("cdrc", "field_separator"); hasOpt { - cdrcCfg.FieldSeparator, _ = c.GetString("cdrc", "field_separator") - } - if hasOpt := c.HasOption("cdrc", "data_usage_multiply_factor"); hasOpt { - cdrcCfg.DataUsageMultiplyFactor, _ = c.GetFloat64("cdrc", "data_usage_multiply_factor") - } - if hasOpt := c.HasOption("cdrc", "run_delay"); hasOpt { - durStr, _ := c.GetString("cdrc", "run_delay") - if cdrcCfg.RunDelay, err = utils.ParseDurationWithSecs(durStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrc", "cdr_in_dir"); hasOpt { - cdrcCfg.CdrInDir, _ = c.GetString("cdrc", "cdr_in_dir") - } - if hasOpt := c.HasOption("cdrc", "cdr_out_dir"); hasOpt { - cdrcCfg.CdrOutDir, _ = c.GetString("cdrc", "cdr_out_dir") - } - if hasOpt := c.HasOption("cdrc", "cdr_source_id"); hasOpt { - cdrcCfg.CdrSourceId, _ = c.GetString("cdrc", "cdr_source_id") - } - // Parse CdrFields - torFld, _ := c.GetString("cdrc", "tor_field") - accIdFld, _ := c.GetString("cdrc", "accid_field") - reqtypeFld, _ := c.GetString("cdrc", "reqtype_field") - directionFld, _ := c.GetString("cdrc", "direction_field") - tenantFld, _ := c.GetString("cdrc", "tenant_field") - categoryFld, _ := c.GetString("cdrc", "category_field") - acntFld, _ := c.GetString("cdrc", "account_field") - subjectFld, _ := c.GetString("cdrc", "subject_field") - destFld, _ := c.GetString("cdrc", "destination_field") - setupTimeFld, _ := c.GetString("cdrc", "setup_time_field") - answerTimeFld, _ := c.GetString("cdrc", "answer_time_field") - durFld, _ := c.GetString("cdrc", "usage_field") - newVals := false - for _, fldData := range [][]string{ // Need to keep fields order - []string{utils.TOR, torFld}, []string{utils.ACCID, accIdFld}, []string{utils.REQTYPE, reqtypeFld}, []string{utils.DIRECTION, directionFld}, - []string{utils.TENANT, tenantFld}, []string{utils.CATEGORY, categoryFld}, []string{utils.ACCOUNT, acntFld}, []string{utils.SUBJECT, subjectFld}, - []string{utils.DESTINATION, destFld}, []string{utils.SETUP_TIME, setupTimeFld}, []string{utils.ANSWER_TIME, answerTimeFld}, []string{utils.USAGE, durFld}} { - if len(fldData[1]) != 0 { - if rsrFlds, err := utils.ParseRSRFields(fldData[1], utils.INFIELD_SEP); err != nil { - return nil, err - } else if len(rsrFlds) > 0 { - if !newVals { // Default values there, reset them since we have at least one new - cdrcCfg.CdrFields = nil - newVals = true - } - if cdrcFld, err := NewCfgCdrFieldWithDefaults(false, rsrFlds, nil, nil, &fldData[0], nil, nil, nil, nil, nil, nil); err != nil { - return nil, err - } else { - cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, cdrcFld) - } - } - } - } - extraFlds, _ := c.GetString("cdrc", "extra_fields") - if len(extraFlds) != 0 { - if sepExtraFlds, err := ConfigSlice(extraFlds); err != nil { - return nil, err - } else { - for _, fldStr := range sepExtraFlds { - // extra fields defined as: : - if spltLbl := strings.Split(fldStr, utils.CONCATENATED_KEY_SEP); len(spltLbl) != 2 { - return nil, fmt.Errorf("Wrong format for cdrc.extra_fields: %s", fldStr) - } else { - if rsrFlds, err := utils.ParseRSRFields(spltLbl[1], utils.INFIELD_SEP); err != nil { - return nil, err - } else if len(rsrFlds) > 0 { - if cdrcFld, err := NewCfgCdrFieldWithDefaults(false, rsrFlds, nil, nil, &spltLbl[0], nil, nil, nil, nil, nil, nil); err != nil { - return nil, err - } else { - cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, cdrcFld) - } - } - } - } - } - } - return cdrcCfg, nil -} - type CdrcConfig struct { - Id string // Configuration label Enabled bool // Enable/Disable the profile CdrsAddress string // The address where CDRs can be reached CdrFormat string // The type of CDR file to process - FieldSeparator string // The separator to use when reading csvs + FieldSeparator rune // The separator to use when reading csvs DataUsageMultiplyFactor float64 // Conversion factor for data usage RunDelay time.Duration // Delay between runs, 0 for inotify driven requests CdrInDir string // Folder to process CDRs from @@ -206,3 +34,44 @@ type CdrcConfig struct { CdrSourceId string // Source identifier for the processed CDRs CdrFields []*CfgCdrField // List of fields to be processed } + +func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { + if jsnCfg == nil { + return nil + } + var err error + if jsnCfg.Enabled != nil { + self.Enabled = *jsnCfg.Enabled + } + if jsnCfg.Cdrs_address != nil { + self.CdrsAddress = *jsnCfg.Cdrs_address + } + if jsnCfg.Cdrs_address != nil { + self.CdrsAddress = *jsnCfg.Cdrs_address + } + if jsnCfg.Cdr_format != nil { + self.CdrFormat = *jsnCfg.Cdr_format + } + if jsnCfg.Field_separator != nil && len(*jsnCfg.Field_separator) > 0 { + sepStr := *jsnCfg.Field_separator + self.FieldSeparator = rune(sepStr[0]) + } + if jsnCfg.Data_usage_multiply_factor != nil { + self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor + } + if jsnCfg.Run_delay != nil { + self.RunDelay = time.Duration(*jsnCfg.Run_delay) * time.Second + } + if jsnCfg.Cdr_in_dir != nil { + self.CdrInDir = *jsnCfg.Cdr_in_dir + } + if jsnCfg.Cdr_out_dir != nil { + self.CdrOutDir = *jsnCfg.Cdr_out_dir + } + if jsnCfg.Cdr_fields != nil { + if self.CdrFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Cdr_fields); err != nil { + return err + } + } + return nil +} diff --git a/config/cdrcconfig_test.go b/config/cdrcconfig_test.go index 40c7b4f22..f49ee8e69 100644 --- a/config/cdrcconfig_test.go +++ b/config/cdrcconfig_test.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,128 +17,3 @@ along with this program. If not, see */ package config - -import ( - "reflect" - "testing" - "time" - - "github.com/cgrates/cgrates/utils" -) - -func TestNewDefaultCdrcConfig(t *testing.T) { - eDfCdrcConfig := &CdrcConfig{ - Id: utils.META_DEFAULT, - Enabled: false, - CdrsAddress: "", - CdrFormat: utils.CSV, - FieldSeparator: utils.FIELDS_SEP, - DataUsageMultiplyFactor: 1.0, - RunDelay: time.Duration(0), - CdrInDir: "/var/log/cgrates/cdrc/in", - CdrOutDir: "/var/log/cgrates/cdrc/out", - CdrSourceId: utils.CSV, - CdrFields: []*CfgCdrField{ - &CfgCdrField{ - Tag: utils.TOR, - Type: utils.CDRFIELD, - CdrFieldId: utils.TOR, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "2"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ACCID, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "3"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.REQTYPE, - Type: utils.CDRFIELD, - CdrFieldId: utils.REQTYPE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "4"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.DIRECTION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DIRECTION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "5"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.TENANT, - Type: utils.CDRFIELD, - CdrFieldId: utils.TENANT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "6"}}, - Mandatory: true}, - &CfgCdrField{ - Tag: utils.CATEGORY, - Type: utils.CDRFIELD, - CdrFieldId: utils.CATEGORY, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "7"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ACCOUNT, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCOUNT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "8"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.SUBJECT, - Type: utils.CDRFIELD, - CdrFieldId: utils.SUBJECT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "9"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.DESTINATION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DESTINATION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "10"}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.SETUP_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.SETUP_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "11"}}, - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ANSWER_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.ANSWER_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "12"}}, - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.USAGE, - Type: utils.CDRFIELD, - CdrFieldId: utils.USAGE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: "13"}}, - Mandatory: true, - }, - }, - } - if dfCdrcCfg := NewDefaultCdrcConfig(); !reflect.DeepEqual(eDfCdrcConfig, dfCdrcCfg) { - t.Errorf("Expected: %+v, received: %+v", eDfCdrcConfig, dfCdrcCfg) - } -} diff --git a/config/cdreconfig.go b/config/cdreconfig.go index e83336aca..8e05a23d0 100644 --- a/config/cdreconfig.go +++ b/config/cdreconfig.go @@ -18,77 +18,6 @@ along with this program. If not, see package config -import ( - "github.com/cgrates/cgrates/utils" -) - -func NewDefaultCdreConfig() *CdreConfig { - cdreCfg := new(CdreConfig) - cdreCfg.setDefaults() - return cdreCfg -} - -func NewCdreConfigFromXmlCdreCfg(xmlCdreCfg *CgrXmlCdreCfg) (*CdreConfig, error) { - var err error - cdreCfg := NewDefaultCdreConfig() - if xmlCdreCfg.CdrFormat != nil { - cdreCfg.CdrFormat = *xmlCdreCfg.CdrFormat - } - if xmlCdreCfg.FieldSeparator != nil && len(*xmlCdreCfg.FieldSeparator) == 1 { - sepStr := *xmlCdreCfg.FieldSeparator - cdreCfg.FieldSeparator = rune(sepStr[0]) - } - if xmlCdreCfg.DataUsageMultiplyFactor != nil { - cdreCfg.DataUsageMultiplyFactor = *xmlCdreCfg.DataUsageMultiplyFactor - } - if xmlCdreCfg.CostMultiplyFactor != nil { - cdreCfg.CostMultiplyFactor = *xmlCdreCfg.CostMultiplyFactor - } - if xmlCdreCfg.CostRoundingDecimals != nil { - cdreCfg.CostRoundingDecimals = *xmlCdreCfg.CostRoundingDecimals - } - if xmlCdreCfg.CostShiftDigits != nil { - cdreCfg.CostShiftDigits = *xmlCdreCfg.CostShiftDigits - } - if xmlCdreCfg.MaskDestId != nil { - cdreCfg.MaskDestId = *xmlCdreCfg.MaskDestId - } - if xmlCdreCfg.MaskLength != nil { - cdreCfg.MaskLength = *xmlCdreCfg.MaskLength - } - if xmlCdreCfg.ExportDir != nil { - cdreCfg.ExportDir = *xmlCdreCfg.ExportDir - } - if xmlCdreCfg.Header != nil { - cdreCfg.HeaderFields = make([]*CfgCdrField, len(xmlCdreCfg.Header.Fields)) - for idx, xmlFld := range xmlCdreCfg.Header.Fields { - cdreCfg.HeaderFields[idx], err = NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlFld, cdreCfg.CdrFormat == utils.CDRE_FIXED_WIDTH) - if err != nil { - return nil, err - } - } - } - if xmlCdreCfg.Content != nil { - cdreCfg.ContentFields = make([]*CfgCdrField, len(xmlCdreCfg.Content.Fields)) - for idx, xmlFld := range xmlCdreCfg.Content.Fields { - cdreCfg.ContentFields[idx], err = NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlFld, cdreCfg.CdrFormat == utils.CDRE_FIXED_WIDTH) - if err != nil { - return nil, err - } - } - } - if xmlCdreCfg.Trailer != nil { - cdreCfg.TrailerFields = make([]*CfgCdrField, len(xmlCdreCfg.Trailer.Fields)) - for idx, xmlFld := range xmlCdreCfg.Trailer.Fields { - cdreCfg.TrailerFields[idx], err = NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlFld, cdreCfg.CdrFormat == utils.CDRE_FIXED_WIDTH) - if err != nil { - return nil, err - } - } - } - return cdreCfg, nil -} - // One instance of CdrExporter type CdreConfig struct { CdrFormat string @@ -105,22 +34,53 @@ type CdreConfig struct { TrailerFields []*CfgCdrField } -// Set here defaults -func (cdreCfg *CdreConfig) setDefaults() error { - cdreCfg.CdrFormat = utils.CSV - cdreCfg.FieldSeparator = utils.CSV_SEP - cdreCfg.DataUsageMultiplyFactor = 0.0 - cdreCfg.CostMultiplyFactor = 0.0 - cdreCfg.CostRoundingDecimals = -1 - cdreCfg.CostShiftDigits = 0 - cdreCfg.MaskDestId = "" - cdreCfg.MaskLength = 0 - cdreCfg.ExportDir = "/var/log/cgrates/cdre" - if flds, err := NewCfgCdrFieldsFromIds(false, utils.CGRID, utils.MEDI_RUNID, utils.TOR, utils.ACCID, utils.REQTYPE, utils.DIRECTION, utils.TENANT, - utils.CATEGORY, utils.ACCOUNT, utils.SUBJECT, utils.DESTINATION, utils.SETUP_TIME, utils.ANSWER_TIME, utils.USAGE, utils.COST); err != nil { - return err - } else { - cdreCfg.ContentFields = flds +func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { + if jsnCfg == nil { + return nil + } + var err error + if jsnCfg.Cdr_format != nil { + self.CdrFormat = *jsnCfg.Cdr_format + } + if jsnCfg.Field_separator != nil && len(*jsnCfg.Field_separator) > 0 { // Make sure we got at least one character so we don't get panic here + sepStr := *jsnCfg.Field_separator + self.FieldSeparator = rune(sepStr[0]) + } + if jsnCfg.Data_usage_multiply_factor != nil { + self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor + } + if jsnCfg.Cost_multiply_factor != nil { + self.CostMultiplyFactor = *jsnCfg.Cost_multiply_factor + } + if jsnCfg.Cost_rounding_decimals != nil { + self.CostRoundingDecimals = *jsnCfg.Cost_rounding_decimals + } + if jsnCfg.Cost_shift_digits != nil { + self.CostShiftDigits = *jsnCfg.Cost_shift_digits + } + if jsnCfg.Mask_destination_id != nil { + self.MaskDestId = *jsnCfg.Mask_destination_id + } + if jsnCfg.Mask_length != nil { + self.MaskLength = *jsnCfg.Mask_length + } + if jsnCfg.Export_dir != nil { + self.ExportDir = *jsnCfg.Export_dir + } + if jsnCfg.Header_fields != nil { + if self.HeaderFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Header_fields); err != nil { + return err + } + } + if jsnCfg.Content_fields != nil { + if self.ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Content_fields); err != nil { + return err + } + } + if jsnCfg.Trailer_fields != nil { + if self.TrailerFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Trailer_fields); err != nil { + return err + } } return nil } diff --git a/config/cdreconfig_test.go b/config/cdreconfig_test.go index 455bdf296..819e5259e 100644 --- a/config/cdreconfig_test.go +++ b/config/cdreconfig_test.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,184 +15,4 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ - package config - -import ( - "fmt" - "github.com/cgrates/cgrates/utils" - "reflect" - "testing" -) - -func TestNewCfgCdrFieldsFromIds(t *testing.T) { - expectedFlds := []*CfgCdrField{ - &CfgCdrField{ - Tag: utils.CGRID, - Type: utils.CDRFIELD, - CdrFieldId: utils.CGRID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CGRID}}, - Width: 40, - Mandatory: true, - }, - &CfgCdrField{ - Tag: "extra1", - Type: utils.CDRFIELD, - CdrFieldId: "extra1", - Value: []*utils.RSRField{ - &utils.RSRField{Id: "extra1"}}, - Width: 30, - Strip: "xright", - Padding: "left", - Mandatory: false, - }, - } - if cdreFlds, err := NewCfgCdrFieldsFromIds(true, utils.CGRID, "extra1"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(expectedFlds, cdreFlds) { - t.Errorf("Expected: %v, received: %v", expectedFlds, cdreFlds) - } -} - -func TestCdreCfgNewDefaultCdreConfig(t *testing.T) { - eCdreCfg := new(CdreConfig) - eCdreCfg.CdrFormat = utils.CSV - eCdreCfg.FieldSeparator = utils.CSV_SEP - eCdreCfg.DataUsageMultiplyFactor = 0.0 - eCdreCfg.CostMultiplyFactor = 0.0 - eCdreCfg.CostRoundingDecimals = -1 - eCdreCfg.CostShiftDigits = 0 - eCdreCfg.MaskDestId = "" - eCdreCfg.MaskLength = 0 - eCdreCfg.ExportDir = "/var/log/cgrates/cdre" - eCdreCfg.ContentFields = []*CfgCdrField{ - &CfgCdrField{ - Tag: utils.CGRID, - Type: utils.CDRFIELD, - CdrFieldId: utils.CGRID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CGRID}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.MEDI_RUNID, - Type: utils.CDRFIELD, - CdrFieldId: utils.MEDI_RUNID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.MEDI_RUNID}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.TOR, - Type: utils.CDRFIELD, - CdrFieldId: utils.TOR, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.TOR}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ACCID, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ACCID}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.REQTYPE, - Type: utils.CDRFIELD, - CdrFieldId: utils.REQTYPE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.REQTYPE}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.DIRECTION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DIRECTION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.DIRECTION}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.TENANT, - Type: utils.CDRFIELD, - CdrFieldId: utils.TENANT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.TENANT}}, - Mandatory: true}, - &CfgCdrField{ - Tag: utils.CATEGORY, - Type: utils.CDRFIELD, - CdrFieldId: utils.CATEGORY, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CATEGORY}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ACCOUNT, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCOUNT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ACCOUNT}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.SUBJECT, - Type: utils.CDRFIELD, - CdrFieldId: utils.SUBJECT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.SUBJECT}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.DESTINATION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DESTINATION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.DESTINATION}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.SETUP_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.SETUP_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.SETUP_TIME}}, - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.ANSWER_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.ANSWER_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ANSWER_TIME}}, - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.USAGE, - Type: utils.CDRFIELD, - CdrFieldId: utils.USAGE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.USAGE}}, - Mandatory: true, - }, - &CfgCdrField{ - Tag: utils.COST, - Type: utils.CDRFIELD, - CdrFieldId: utils.COST, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.COST}}, - Mandatory: true, - }, - } - if cdreCfg := NewDefaultCdreConfig(); !reflect.DeepEqual(eCdreCfg, cdreCfg) { - for _, fld := range cdreCfg.ContentFields { - fmt.Printf("Have field: %+v\n", fld) - } - t.Errorf("Expecting: %v, received: %v", eCdreCfg, cdreCfg) - } -} diff --git a/config/cdrstatsconfig.go b/config/cdrstatsconfig.go index ab09674b8..d8fcc2238 100644 --- a/config/cdrstatsconfig.go +++ b/config/cdrstatsconfig.go @@ -19,163 +19,10 @@ along with this program. If not, see package config import ( - "code.google.com/p/goconf/conf" "github.com/cgrates/cgrates/utils" - "strconv" "time" ) -// Parse the configuration file for CDRStatConfigs -func ParseCfgDefaultCDRStatsConfig(c *conf.ConfigFile) (*CdrStatsConfig, error) { - var err error - csCfg := NewCdrStatsConfigWithDefaults() - if hasOpt := c.HasOption("cdrstats", "queue_length"); hasOpt { - csCfg.QueueLength, _ = c.GetInt("cdrstats", "queue_length") - } - if hasOpt := c.HasOption("cdrstats", "time_window"); hasOpt { - durStr, _ := c.GetString("cdrstats", "time_window") - if csCfg.TimeWindow, err = utils.ParseDurationWithSecs(durStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "metrics"); hasOpt { - metricsStr, _ := c.GetString("cdrstats", "metrics") - if csCfg.Metrics, err = ConfigSlice(metricsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "setup_interval"); hasOpt { - setupIntervalStr, _ := c.GetString("cdrstats", "setup_interval") - if len(setupIntervalStr) != 0 { // If we parse empty, will get empty time, we prefer nil - if setupIntervalSlc, err := ConfigSlice(setupIntervalStr); err != nil { - return nil, err - } else { - for _, setupTimeStr := range setupIntervalSlc { - if setupTime, err := utils.ParseTimeDetectLayout(setupTimeStr); err != nil { - return nil, err - } else { - csCfg.SetupInterval = append(csCfg.SetupInterval, setupTime) - } - } - } - } - } - if hasOpt := c.HasOption("cdrstats", "tors"); hasOpt { - torsStr, _ := c.GetString("cdrstats", "tors") - if csCfg.TORs, err = ConfigSlice(torsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "cdr_hosts"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "cdr_hosts") - if csCfg.CdrHosts, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "cdr_sources"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "cdr_sources") - if csCfg.CdrSources, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "req_types"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "req_types") - if csCfg.ReqTypes, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "directions"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "directions") - if csCfg.Directions, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "tenants"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "tenants") - if csCfg.Tenants, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "categories"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "categories") - if csCfg.Categories, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "accounts"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "accounts") - if csCfg.Accounts, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "subjects"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "subjects") - if csCfg.Subjects, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "destination_prefixes"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "destination_prefixes") - if csCfg.DestinationPrefixes, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "usage_interval"); hasOpt { - usageIntervalStr, _ := c.GetString("cdrstats", "usage_interval") - if usageIntervalSlc, err := ConfigSlice(usageIntervalStr); err != nil { - return nil, err - } else { - for _, usageDurStr := range usageIntervalSlc { - if usageDur, err := utils.ParseDurationWithSecs(usageDurStr); err != nil { - return nil, err - } else { - csCfg.UsageInterval = append(csCfg.UsageInterval, usageDur) - } - } - } - } - if hasOpt := c.HasOption("cdrstats", "mediation_run_ids"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "mediation_run_ids") - if csCfg.MediationRunIds, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "rated_accounts"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "rated_accounts") - if csCfg.RatedAccounts, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "rated_subjects"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "rated_subjects") - if csCfg.RatedSubjects, err = ConfigSlice(valsStr); err != nil { - return nil, err - } - } - if hasOpt := c.HasOption("cdrstats", "cost_intervals"); hasOpt { - valsStr, _ := c.GetString("cdrstats", "cost_intervals") - if costSlc, err := ConfigSlice(valsStr); err != nil { - return nil, err - } else { - for _, costStr := range costSlc { - if cost, err := strconv.ParseFloat(costStr, 64); err != nil { - return nil, err - } else { - csCfg.CostInterval = append(csCfg.CostInterval, cost) - } - } - } - } - - return csCfg, nil -} - -func NewCdrStatsConfigWithDefaults() *CdrStatsConfig { - csCfg := new(CdrStatsConfig) - csCfg.setDefaults() - return csCfg -} - type CdrStatsConfig struct { Id string // Config id, unique per config instance QueueLength int // Number of items in the stats buffer @@ -199,9 +46,74 @@ type CdrStatsConfig struct { CostInterval []float64 // 2 or less items, (>=Cost, +}, + +"cdrc": { + "CDRC-CSV1": { + "enabled": true, // enable CDR client functionality + "cdr_in_dir": "/tmp/cgrates/cdrc1/in", // absolute path towards the directory where the CDRs are stored + "cdr_out_dir": "/tmp/cgrates/cdrc1/out", // absolute path towards the directory where processed CDRs will be moved + "cdr_source_id": "csv1", // free form field, tag identifying the source of the CDRs within CDRS database + }, + "CDRC-CSV2": { + "enabled": true, // enable CDR client functionality + "cdr_in_dir": "/tmp/cgrates/cdrc2/in", // absolute path towards the directory where the CDRs are stored + "cdr_out_dir": "/tmp/cgrates/cdrc2/out", // absolute path towards the directory where processed CDRs will be moved + "cdr_source_id": "csv2", // free form field, tag identifying the source of the CDRs within CDRS database + "cdr_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"cdr_field_id": "tor", "value": "~7:s/^(voice|data|sms)$/*$1/"}, + {"cdr_field_id": "answer_time", "value": "1"}, + {"cdr_field_id": "usage", "value": "~9:s/^(\\d+)$/${1}s/"}, + ], + }, +}, + +} \ No newline at end of file diff --git a/config/cfgcdrfield.go b/config/cfgcdrfield.go index c471674bf..6ca1e2c53 100644 --- a/config/cfgcdrfield.go +++ b/config/cfgcdrfield.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,128 +20,46 @@ package config import ( "github.com/cgrates/cgrates/utils" - "strings" ) -func NewCfgCdrFieldFromCgrXmlCfgCdrField(xmlCdrFld *XmlCfgCdrField, fixedWidth bool) (*CfgCdrField, error) { +func NewCfgCdrFieldFromCdrFieldJsonCfg(jsnCfgFld *CdrFieldJsonCfg) (*CfgCdrField, error) { var err error - var val, fltr utils.RSRFields - if xmlCdrFld.Value != nil { - if xmlCdrFld.Type != nil && utils.IsSliceMember([]string{utils.CONSTANT, utils.FILLER, utils.METATAG, utils.HTTP_POST}, *xmlCdrFld.Type) && !strings.HasPrefix(*xmlCdrFld.Value, utils.STATIC_VALUE_PREFIX) { // Enforce static values for fields which do not support RSR rules - *xmlCdrFld.Value = utils.STATIC_VALUE_PREFIX + *xmlCdrFld.Value - } - if val, err = utils.ParseRSRFields(*xmlCdrFld.Value, utils.INFIELD_SEP); err != nil { + cfgFld := new(CfgCdrField) + if jsnCfgFld.Tag != nil { + cfgFld.Tag = *jsnCfgFld.Tag + } + if jsnCfgFld.Type != nil { + cfgFld.Type = *jsnCfgFld.Type + } + if jsnCfgFld.Cdr_field_id != nil { + cfgFld.CdrFieldId = *jsnCfgFld.Cdr_field_id + } + if jsnCfgFld.Value != nil { + if cfgFld.Value, err = utils.ParseRSRFields(*jsnCfgFld.Value, utils.INFIELD_SEP); err != nil { return nil, err } } - if xmlCdrFld.Filter != nil { - if fltr, err = utils.ParseRSRFields(*xmlCdrFld.Filter, utils.INFIELD_SEP); err != nil { + if jsnCfgFld.Filter != nil { + if cfgFld.Filter, err = utils.ParseRSRFields(*jsnCfgFld.Filter, utils.INFIELD_SEP); err != nil { return nil, err } } - if cdrFld, err := NewCfgCdrFieldWithDefaults(fixedWidth, val, fltr, xmlCdrFld.Type, xmlCdrFld.CdrFieldId, xmlCdrFld.Tag, - xmlCdrFld.Mandatory, xmlCdrFld.Layout, xmlCdrFld.Width, xmlCdrFld.Strip, xmlCdrFld.Padding); err != nil { - return nil, err - } else { - return cdrFld, nil + if jsnCfgFld.Width != nil { + cfgFld.Width = *jsnCfgFld.Width } -} - -func NewCfgCdrFieldWithDefaults(fixedWidth bool, val, filter utils.RSRFields, typ, cdrFieldId, tag *string, mandatory *bool, layout *string, width *int, strip, padding *string) (*CfgCdrField, error) { - cdrField := &CfgCdrField{Value: val, Filter: filter} - if typ != nil { - cdrField.Type = *typ - } else { - cdrField.Type = utils.CDRFIELD + if jsnCfgFld.Strip != nil { + cfgFld.Strip = *jsnCfgFld.Strip } - if cdrFieldId != nil { - cdrField.CdrFieldId = *cdrFieldId - } else if utils.IsSliceMember([]string{utils.CDRFIELD, utils.COMBIMED, utils.METATAG}, cdrField.Type) && len(cdrField.Value) != 0 { - cdrField.CdrFieldId = cdrField.Value[0].Id + if jsnCfgFld.Padding != nil { + cfgFld.Padding = *jsnCfgFld.Padding } - if tag != nil { - cdrField.Tag = *tag - } else { - cdrField.Tag = cdrField.CdrFieldId + if jsnCfgFld.Layout != nil { + cfgFld.Layout = *jsnCfgFld.Layout } - mandatoryFields := append(utils.PrimaryCdrFields, utils.CGRID, utils.COST, utils.MEDI_RUNID, utils.ORDERID) - if mandatory != nil { - cdrField.Mandatory = *mandatory - } else if utils.IsSliceMember(mandatoryFields, cdrField.CdrFieldId) { - cdrField.Mandatory = true + if jsnCfgFld.Mandatory != nil { + cfgFld.Mandatory = *jsnCfgFld.Mandatory } - if layout != nil { - cdrField.Layout = *layout - } else if utils.IsSliceMember([]string{utils.SETUP_TIME, utils.ANSWER_TIME}, cdrField.CdrFieldId) { - cdrField.Layout = "2006-01-02T15:04:05Z07:00" - } - if width != nil { - cdrField.Width = *width - } else if fixedWidth && cdrField.Type != utils.CONSTANT { - switch cdrField.CdrFieldId { // First value element is used as field reference, giving the default properties out, good enough for default configs which do not have more than one value anyway - case utils.CGRID: - cdrField.Width = 40 - case utils.ORDERID: - cdrField.Width = 11 - case utils.TOR: - cdrField.Width = 6 - case utils.ACCID: - cdrField.Width = 36 - case utils.CDRHOST: - cdrField.Width = 15 - case utils.CDRSOURCE: - cdrField.Width = 15 - case utils.REQTYPE: - cdrField.Width = 13 - case utils.DIRECTION: - cdrField.Width = 4 - case utils.TENANT: - cdrField.Width = 24 - case utils.CATEGORY: - cdrField.Width = 10 - case utils.ACCOUNT: - cdrField.Width = 24 - case utils.SUBJECT: - cdrField.Width = 24 - case utils.DESTINATION: - cdrField.Width = 24 - case utils.SETUP_TIME: - cdrField.Width = 30 - case utils.ANSWER_TIME: - cdrField.Width = 30 - case utils.USAGE: - cdrField.Width = 30 - case utils.MEDI_RUNID: - cdrField.Width = 20 - case utils.COST: - cdrField.Width = 24 - default: - cdrField.Width = 30 - } - } - if strip != nil { - cdrField.Strip = *strip - } else if fixedWidth && cdrField.Type != utils.CONSTANT { - switch cdrField.CdrFieldId { // First value element is used as field reference, giving the default properties out, good enough for default configs which do not have more than one value anyway - case utils.CGRID, utils.ORDERID, utils.TOR: - case utils.ACCID, utils.CDRHOST: - cdrField.Strip = "left" - case utils.CDRSOURCE, utils.REQTYPE, utils.DIRECTION, utils.TENANT, utils.CATEGORY, utils.ACCOUNT, utils.SUBJECT, utils.DESTINATION, utils.SETUP_TIME, utils.ANSWER_TIME, utils.USAGE, utils.MEDI_RUNID, utils.COST: - cdrField.Strip = "xright" - default: - cdrField.Strip = "xright" - } - } - if padding != nil { - cdrField.Padding = *padding - } else if fixedWidth && cdrField.Type != utils.CONSTANT { - switch cdrField.CdrFieldId { // First value element is used as field reference, giving the default properties out, good enough for default configs which do not have more than one value anyway - case utils.CGRID: - default: - cdrField.Padding = "left" - } - } - return cdrField, nil + return cfgFld, nil } type CfgCdrField struct { @@ -157,19 +75,14 @@ type CfgCdrField struct { Mandatory bool } -// Converts a list of field identifiers into proper CDR field content -func NewCfgCdrFieldsFromIds(withFixedWith bool, fldsIds ...string) ([]*CfgCdrField, error) { - cdrFields := make([]*CfgCdrField, len(fldsIds)) - for idx, fldId := range fldsIds { - if parsedRsr, err := utils.NewRSRField(fldId); err != nil { +func CfgCdrFieldsFromCdrFieldsJsonCfg(jsnCfgFldss []*CdrFieldJsonCfg) ([]*CfgCdrField, error) { + retFields := make([]*CfgCdrField, len(jsnCfgFldss)) + for idx, jsnFld := range jsnCfgFldss { + if cfgFld, err := NewCfgCdrFieldFromCdrFieldJsonCfg(jsnFld); err != nil { return nil, err } else { - if cdrFld, err := NewCfgCdrFieldWithDefaults(withFixedWith, utils.RSRFields{parsedRsr}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - return nil, err - } else { - cdrFields[idx] = cdrFld - } + retFields[idx] = cfgFld } } - return cdrFields, nil + return retFields, nil } diff --git a/config/cfgcdrfield_test.go b/config/cfgcdrfield_test.go index 0ad8c91c7..f49ee8e69 100644 --- a/config/cfgcdrfield_test.go +++ b/config/cfgcdrfield_test.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,315 +17,3 @@ along with this program. If not, see */ package config - -import ( - //"fmt" - "github.com/cgrates/cgrates/utils" - "reflect" - "testing" -) - -func TestNewCfgCdrFieldWithDefaults(t *testing.T) { - eCdreCdrFld := &CfgCdrField{ - Tag: utils.CGRID, - Type: utils.CDRFIELD, - CdrFieldId: utils.CGRID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CGRID}}, - Width: 40, - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.CGRID}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.ORDERID, - Type: utils.CDRFIELD, - CdrFieldId: utils.ORDERID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ORDERID}}, - Width: 11, - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.ORDERID}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.TOR, - Type: utils.CDRFIELD, - CdrFieldId: utils.TOR, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.TOR}}, - Width: 6, - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.TOR}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.ACCID, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ACCID}}, - Width: 36, - Strip: "left", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.ACCID}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.CDRHOST, - Type: utils.CDRFIELD, - CdrFieldId: utils.CDRHOST, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CDRHOST}}, - Width: 15, - Strip: "left", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.CDRHOST}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.CDRSOURCE, - Type: utils.CDRFIELD, - CdrFieldId: utils.CDRSOURCE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CDRSOURCE}}, - Width: 15, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.CDRSOURCE}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.REQTYPE, - Type: utils.CDRFIELD, - CdrFieldId: utils.REQTYPE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.REQTYPE}}, - Width: 13, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.REQTYPE}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.DIRECTION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DIRECTION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.DIRECTION}}, - Width: 4, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.DIRECTION}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.TENANT, - Type: utils.CDRFIELD, - CdrFieldId: utils.TENANT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.TENANT}}, - Width: 24, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.TENANT}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.CATEGORY, - Type: utils.CDRFIELD, - CdrFieldId: utils.CATEGORY, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.CATEGORY}}, - Width: 10, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.CATEGORY}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.ACCOUNT, - Type: utils.CDRFIELD, - CdrFieldId: utils.ACCOUNT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ACCOUNT}}, - Width: 24, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.ACCOUNT}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.SUBJECT, - Type: utils.CDRFIELD, - CdrFieldId: utils.SUBJECT, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.SUBJECT}}, - Width: 24, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.SUBJECT}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.DESTINATION, - Type: utils.CDRFIELD, - CdrFieldId: utils.DESTINATION, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.DESTINATION}}, - Width: 24, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.DESTINATION}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.SETUP_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.SETUP_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.SETUP_TIME}}, - Width: 30, - Strip: "xright", - Padding: "left", - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.SETUP_TIME}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.ANSWER_TIME, - Type: utils.CDRFIELD, - CdrFieldId: utils.ANSWER_TIME, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.ANSWER_TIME}}, - Width: 30, - Strip: "xright", - Padding: "left", - Layout: "2006-01-02T15:04:05Z07:00", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.ANSWER_TIME}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.USAGE, - Type: utils.CDRFIELD, - CdrFieldId: utils.USAGE, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.USAGE}}, - Width: 30, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.USAGE}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.MEDI_RUNID, - Type: utils.CDRFIELD, - CdrFieldId: utils.MEDI_RUNID, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.MEDI_RUNID}}, - Width: 20, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.MEDI_RUNID}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: utils.COST, - Type: utils.CDRFIELD, - CdrFieldId: utils.COST, - Value: []*utils.RSRField{ - &utils.RSRField{Id: utils.COST}}, - Width: 24, - Strip: "xright", - Padding: "left", - Mandatory: true, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: utils.COST}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } - eCdreCdrFld = &CfgCdrField{ - Tag: "extra_1", - Type: utils.CDRFIELD, - CdrFieldId: "extra_1", - Value: []*utils.RSRField{ - &utils.RSRField{Id: "extra_1"}}, - Width: 30, - Strip: "xright", - Padding: "left", - Mandatory: false, - } - if cfgCdrField, err := NewCfgCdrFieldWithDefaults(true, []*utils.RSRField{&utils.RSRField{Id: "extra_1"}}, nil, nil, nil, nil, nil, nil, nil, nil, nil); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eCdreCdrFld, cfgCdrField) { - t.Errorf("Expecting: %+v, received: %+v", eCdreCdrFld, cfgCdrField) - } -} diff --git a/config/config.go b/config/config.go index dbabd198d..14bc2cb23 100644 --- a/config/config.go +++ b/config/config.go @@ -22,11 +22,11 @@ import ( "errors" "fmt" "os" + "path/filepath" "strconv" "strings" "time" - "code.google.com/p/goconf/conf" "github.com/cgrates/cgrates/utils" ) @@ -53,185 +53,165 @@ func SetCgrConfig(cfg *CGRConfig) { cgrCfg = cfg } -// Holds system configuration, defaults are overwritten with values from config file if found -type CGRConfig struct { - RatingDBType string - RatingDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - RatingDBPort string // The port to bind to. - RatingDBName string // The name of the database to connect to. - RatingDBUser string // The user to sign in as. - RatingDBPass string // The user's password. - AccountDBType string - AccountDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - AccountDBPort string // The port to bind to. - AccountDBName string // The name of the database to connect to. - AccountDBUser string // The user to sign in as. - AccountDBPass string // The user's password. - StorDBType string // Should reflect the database type used to store logs - StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - StorDBPort string // Th e port to bind to. - StorDBName string // The name of the database to connect to. - StorDBUser string // The user to sign in as. - StorDBPass string // The user's password. - StorDBMaxOpenConns int // Maximum database connections opened - StorDBMaxIdleConns int // Maximum idle connections to keep opened - DBDataEncoding string // The encoding used to store object data in strings: - RPCJSONListen string // RPC JSON listening address - RPCGOBListen string // RPC GOB listening address - HTTPListen string // HTTP listening address - DefaultReqType string // Use this request type if not defined on top - DefaultCategory string // set default type of record - DefaultTenant string // set default tenant - DefaultSubject string // set default rating subject, useful in case of fallback - RoundingDecimals int // Number of decimals to round end prices at - HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate - TpExportPath string // Path towards export folder for offline Tariff Plans - XmlCfgDocument *CgrXmlCfgDocument // Load additional configuration inside xml document - RaterEnabled bool // start standalone server (no balancer) - RaterBalancer string // balancer address host:port - BalancerEnabled bool - SchedulerEnabled bool - CDRSEnabled bool // Enable CDR Server service - CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs - CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal> - CDRSStats string // Address where to reach the Mediator. <""|intenal> - CDRSStoreDisable bool // When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - CDRStatsEnabled bool // Enable CDR Stats service - CDRStatConfig *CdrStatsConfig // Active cdr stats configuration instances - CdreDefaultInstance *CdreConfig // Will be used in the case no specific one selected by API - CdrcInstances []*CdrcConfig // Number of CDRC instances running imports - SMEnabled bool - SMSwitchType string - SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer - SMCdrS string // Connection towards CDR server - SMReconnects int // Number of reconnect attempts to rater - SMDebitInterval int // the period to be debited in advanced during a call (in seconds) - SMMaxCallDuration time.Duration // The maximum duration of a call - SMMinCallDuration time.Duration // Only authorize calls with allowed duration bigger than this - MediatorEnabled bool // Starts Mediator service: . - MediatorReconnects int // Number of reconnects to rater before giving up. - MediatorRater string - MediatorStats string // Address where to reach the Rater: - MediatorStoreDisable bool // When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - DerivedChargers utils.DerivedChargers // System wide derived chargers, added to the account level ones - CombinedDerivedChargers bool // Combine accounts specific derived_chargers with server configured - FreeswitchServer string // freeswitch address host:port - FreeswitchPass string // FS socket password - FreeswitchReconnects int // number of times to attempt reconnect after connect fails - FSMinDurLowBalance time.Duration // Threshold which will trigger low balance warnings - FSLowBalanceAnnFile string // File to be played when low balance is reached - FSEmptyBalanceContext string // If defined, call will be transfered to this context on empty balance - FSEmptyBalanceAnnFile string // File to be played before disconnecting prepaid calls (applies only if no context defined) - FSCdrExtraFields []*utils.RSRField // Extra fields to store in CDRs in case of processing them - OsipsListenUdp string // Address where to listen for event datagrams coming from OpenSIPS - OsipsMiAddr string // Adress where to reach OpenSIPS mi_datagram module - OsipsEvSubscInterval time.Duration // Refresh event subscription at this interval - OsipsReconnects int // Number of attempts on connect failure. - KamailioEvApiAddr string // Address of the kamailio evapi server - KamailioReconnects int // Number of reconnect attempts on connection lost - HistoryAgentEnabled bool // Starts History as an agent: . - HistoryServer string // Address where to reach the master history server: - HistoryServerEnabled bool // Starts History as server: . - HistoryDir string // Location on disk where to store history files. - HistorySaveInterval time.Duration // The timout duration between history writes - MailerServer string // The server to use when sending emails out - MailerAuthUser string // Authenticate to email server using this user - MailerAuthPass string // Authenticate to email server with this password - MailerFromAddr string // From address used when sending emails out - DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .cfg options +func NewDefaultCGRConfig() (*CGRConfig, error) { + cfg := new(CGRConfig) + cgrJsonCfg, err := NewCgrJsonCfgFromReader(strings.NewReader(CGRATES_CFG_JSON)) + if err != nil { + return nil, err + } + if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil { + return nil, err + } + if err := cfg.checkConfigSanity(); err != nil { + return nil, err + } + return cfg, nil } -func (self *CGRConfig) setDefaults() error { - self.RatingDBType = REDIS - self.RatingDBHost = "127.0.0.1" - self.RatingDBPort = "6379" - self.RatingDBName = "10" - self.RatingDBUser = "" - self.RatingDBPass = "" - self.AccountDBType = REDIS - self.AccountDBHost = "127.0.0.1" - self.AccountDBPort = "6379" - self.AccountDBName = "11" - self.AccountDBUser = "" - self.AccountDBPass = "" - self.StorDBType = utils.MYSQL - self.StorDBHost = "localhost" - self.StorDBPort = "3306" - self.StorDBName = "cgrates" - self.StorDBUser = "cgrates" - self.StorDBPass = "CGRateS.org" - self.StorDBMaxOpenConns = 100 - self.StorDBMaxIdleConns = 10 - self.DBDataEncoding = utils.MSGPACK - self.RPCJSONListen = "127.0.0.1:2012" - self.RPCGOBListen = "127.0.0.1:2013" - self.HTTPListen = "127.0.0.1:2080" - self.DefaultReqType = utils.RATED - self.DefaultCategory = "call" - self.DefaultTenant = "cgrates.org" - self.DefaultSubject = "cgrates" - self.RoundingDecimals = 10 - self.HttpSkipTlsVerify = false - self.TpExportPath = "/var/log/cgrates/tpe" - self.XmlCfgDocument = nil - self.RaterEnabled = false - self.RaterBalancer = "" - self.BalancerEnabled = false - self.SchedulerEnabled = false - self.CDRSEnabled = false - self.CDRSExtraFields = []*utils.RSRField{} - self.CDRSMediator = "" - self.CDRSStats = "" - self.CDRSStoreDisable = false - self.CDRStatsEnabled = false - self.CDRStatConfig = NewCdrStatsConfigWithDefaults() - self.CdreDefaultInstance = NewDefaultCdreConfig() - self.CdrcInstances = []*CdrcConfig{NewDefaultCdrcConfig()} // This instance is just for the sake of defaults, it will be replaced when the file is loaded with the one resulted from there - self.MediatorEnabled = false - self.MediatorRater = utils.INTERNAL - self.MediatorReconnects = 3 - self.MediatorStats = "" - self.MediatorStoreDisable = false - self.DerivedChargers = make(utils.DerivedChargers, 0) - self.CombinedDerivedChargers = true - self.SMEnabled = false - self.SMSwitchType = FS - self.SMRater = utils.INTERNAL - self.SMCdrS = "" - self.SMReconnects = 3 - self.SMDebitInterval = 10 - self.SMMaxCallDuration = time.Duration(3) * time.Hour - self.SMMinCallDuration = time.Duration(0) - self.FreeswitchServer = "127.0.0.1:8021" - self.FreeswitchPass = "ClueCon" - self.FreeswitchReconnects = 5 - self.FSMinDurLowBalance = time.Duration(5) * time.Second - self.FSLowBalanceAnnFile = "" - self.FSEmptyBalanceContext = "" - self.FSEmptyBalanceAnnFile = "" - self.FSCdrExtraFields = []*utils.RSRField{} - self.OsipsListenUdp = "127.0.0.1:2020" - self.OsipsMiAddr = "127.0.0.1:8020" - self.OsipsEvSubscInterval = time.Duration(60) * time.Second - self.OsipsReconnects = 3 - self.KamailioEvApiAddr = "127.0.0.1:8448" - self.KamailioReconnects = 3 - self.HistoryAgentEnabled = false - self.HistoryServerEnabled = false - self.HistoryServer = utils.INTERNAL - self.HistoryDir = "/var/log/cgrates/history" - self.HistorySaveInterval = time.Duration(1) * time.Second - self.MailerServer = "localhost:25" - self.MailerAuthUser = "cgrates" - self.MailerAuthPass = "CGRateS.org" - self.MailerFromAddr = "cgr-mailer@localhost.localdomain" - self.DataFolderPath = "/usr/share/cgrates/" - return nil +func NewCGRConfigFromJsonString(cfgJsonStr string) (*CGRConfig, error) { + cfg := new(CGRConfig) + if jsnCfg, err := NewCgrJsonCfgFromReader(strings.NewReader(cfgJsonStr)); err != nil { + return nil, err + } else if err := cfg.loadFromJsonCfg(jsnCfg); err != nil { + return nil, err + } + return cfg, nil +} + +// Reads all .json files out of a folder/subfolders and loads them up in lexical order +func NewCGRConfigFromFolder(cfgDir string) (*CGRConfig, error) { + if fi, err := os.Stat(cfgDir); err != nil { + return nil, err + } else if !fi.IsDir() { + return nil, fmt.Errorf("Path: %s not a directory.", cfgDir) + } + cfg, err := NewDefaultCGRConfig() + if err != nil { + return nil, err + } + jsonFilesFound := false + err = filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + return nil + } + cfgFiles, err := filepath.Glob(filepath.Join(path, "*.json")) + if err != nil { + return err + } + if cfgFiles == nil { // No need of processing further since there are no config files in the folder + return nil + } + if !jsonFilesFound { + jsonFilesFound = true + } + for _, jsonFilePath := range cfgFiles { + if cgrJsonCfg, err := NewCgrJsonCfgFromFile(jsonFilePath); err != nil { + return err + } else if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil { + return err + } + } + return nil + }) + if err != nil { + return nil, err + } else if !jsonFilesFound { + return nil, fmt.Errorf("No config file found on path %s", cfgDir) + } + if err := cfg.checkConfigSanity(); err != nil { + return nil, err + } + return cfg, nil +} + +// Holds system configuration, defaults are overwritten with values from config file if found +type CGRConfig struct { + RatingDBType string + RatingDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + RatingDBPort string // The port to bind to. + RatingDBName string // The name of the database to connect to. + RatingDBUser string // The user to sign in as. + RatingDBPass string // The user's password. + AccountDBType string + AccountDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + AccountDBPort string // The port to bind to. + AccountDBName string // The name of the database to connect to. + AccountDBUser string // The user to sign in as. + AccountDBPass string // The user's password. + StorDBType string // Should reflect the database type used to store logs + StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + StorDBPort string // Th e port to bind to. + StorDBName string // The name of the database to connect to. + StorDBUser string // The user to sign in as. + StorDBPass string // The user's password. + StorDBMaxOpenConns int // Maximum database connections opened + StorDBMaxIdleConns int // Maximum idle connections to keep opened + DBDataEncoding string // The encoding used to store object data in strings: + RPCJSONListen string // RPC JSON listening address + RPCGOBListen string // RPC GOB listening address + HTTPListen string // HTTP listening address + DefaultReqType string // Use this request type if not defined on top + DefaultCategory string // set default type of record + DefaultTenant string // set default tenant + DefaultSubject string // set default rating subject, useful in case of fallback + RoundingDecimals int // Number of decimals to round end prices at + HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate + TpExportPath string // Path towards export folder for offline Tariff Plans + RaterEnabled bool // start standalone server (no balancer) + RaterBalancer string // balancer address host:port + BalancerEnabled bool + SchedulerEnabled bool + CDRSEnabled bool // Enable CDR Server service + CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs + CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal> + CDRSStats string // Address where to reach the Mediator. <""|intenal> + CDRSStoreDisable bool // When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario + CDRStatsEnabled bool // Enable CDR Stats service + CDRStatConfig *CdrStatsConfig // Active cdr stats configuration instances, platform level + CdreProfiles map[string]*CdreConfig + CdrcProfiles map[string]*CdrcConfig // Number of CDRC instances running imports + SMEnabled bool + SMSwitchType string + SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer + SMCdrS string // Connection towards CDR server + SMReconnects int // Number of reconnect attempts to rater + SMDebitInterval int // the period to be debited in advanced during a call (in seconds) + SMMaxCallDuration time.Duration // The maximum duration of a call + SMMinCallDuration time.Duration // Only authorize calls with allowed duration bigger than this + MediatorEnabled bool // Starts Mediator service: . + MediatorReconnects int // Number of reconnects to rater before giving up. + MediatorRater string + MediatorStats string // Address where to reach the Rater: + MediatorStoreDisable bool // When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario + FreeswitchServer string // freeswitch address host:port + FreeswitchPass string // FS socket password + FreeswitchReconnects int // number of times to attempt reconnect after connect fails + FSMinDurLowBalance time.Duration // Threshold which will trigger low balance warnings + FSLowBalanceAnnFile string // File to be played when low balance is reached + FSEmptyBalanceContext string // If defined, call will be transfered to this context on empty balance + FSEmptyBalanceAnnFile string // File to be played before disconnecting prepaid calls (applies only if no context defined) + FSCdrExtraFields []*utils.RSRField // Extra fields to store in CDRs in case of processing them + OsipsListenUdp string // Address where to listen for event datagrams coming from OpenSIPS + OsipsMiAddr string // Adress where to reach OpenSIPS mi_datagram module + OsipsEvSubscInterval time.Duration // Refresh event subscription at this interval + OsipsReconnects int // Number of attempts on connect failure. + KamailioEvApiAddr string // Address of the kamailio evapi server + KamailioReconnects int // Number of reconnect attempts on connection lost + HistoryAgentEnabled bool // Starts History as an agent: . + HistoryServer string // Address where to reach the master history server: + HistoryServerEnabled bool // Starts History as server: . + HistoryDir string // Location on disk where to store history files. + HistorySaveInterval time.Duration // The timout duration between history writes + MailerServer string // The server to use when sending emails out + MailerAuthUser string // Authenticate to email server using this user + MailerAuthPass string // Authenticate to email server with this password + MailerFromAddr string // From address used when sending emails out + DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options } func (self *CGRConfig) checkConfigSanity() error { // CDRC sanity checks - for _, cdrcInst := range self.CdrcInstances { + for _, cdrcInst := range self.CdrcProfiles { if cdrcInst.Enabled == true { if len(cdrcInst.CdrFields) == 0 { return errors.New("CdrC enabled but no fields to be processed defined!") @@ -259,382 +239,406 @@ func (self *CGRConfig) checkConfigSanity() error { return nil } -func NewDefaultCGRConfig() (*CGRConfig, error) { - cfg := &CGRConfig{} - cfg.setDefaults() - if err := cfg.checkConfigSanity(); err != nil { - return nil, err - } - return cfg, nil -} - -// Unifies the config handling for both tests and real path -func NewCGRConfig(c *conf.ConfigFile) (*CGRConfig, error) { - cfg, err := loadConfig(c) +// Loads from json configuration object, will be used for defaults, config from file and reload, might need lock +func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { + // Load sections out of JSON config, stop on error + jsnGeneralCfg, err := jsnCfg.GeneralJsonCfg() if err != nil { - return nil, err + return err } - if err := cfg.checkConfigSanity(); err != nil { - return nil, err - } - return cfg, nil -} - -// Instantiate a new CGRConfig setting defaults or reading from file -func NewCGRConfigFromFile(cfgPath *string) (*CGRConfig, error) { - c, err := conf.ReadConfigFile(*cfgPath) + jsnListenCfg, err := jsnCfg.ListenJsonCfg() if err != nil { - return nil, errors.New(fmt.Sprintf("Could not open the configuration file: %s", err)) + return err } - return NewCGRConfig(c) -} - -func NewCGRConfigFromBytes(data []byte) (*CGRConfig, error) { - c, err := conf.ReadConfigBytes(data) + jsnRatingDbCfg, err := jsnCfg.DbJsonCfg(RATINGDB_JSN) if err != nil { - return nil, errors.New(fmt.Sprintf("Could not open the configuration file: %s", err)) + return err } - return NewCGRConfig(c) -} - -func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { - cfg := &CGRConfig{} - cfg.setDefaults() - var hasOpt bool - var err error - if hasOpt = c.HasOption("global", "ratingdb_type"); hasOpt { - cfg.RatingDBType, _ = c.GetString("global", "ratingdb_type") + jsnAccountingDbCfg, err := jsnCfg.DbJsonCfg(ACCOUNTINGDB_JSN) + if err != nil { + return err } - if hasOpt = c.HasOption("global", "ratingdb_host"); hasOpt { - cfg.RatingDBHost, _ = c.GetString("global", "ratingdb_host") + jsnStorDbCfg, err := jsnCfg.DbJsonCfg(STORDB_JSN) + if err != nil { + return err } - if hasOpt = c.HasOption("global", "ratingdb_port"); hasOpt { - cfg.RatingDBPort, _ = c.GetString("global", "ratingdb_port") + jsnBalancerCfg, err := jsnCfg.BalancerJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "ratingdb_name"); hasOpt { - cfg.RatingDBName, _ = c.GetString("global", "ratingdb_name") + jsnRaterCfg, err := jsnCfg.RaterJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "ratingdb_user"); hasOpt { - cfg.RatingDBUser, _ = c.GetString("global", "ratingdb_user") + jsnSchedCfg, err := jsnCfg.SchedulerJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "ratingdb_passwd"); hasOpt { - cfg.RatingDBPass, _ = c.GetString("global", "ratingdb_passwd") + jsnCdrsCfg, err := jsnCfg.CdrsJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_type"); hasOpt { - cfg.AccountDBType, _ = c.GetString("global", "accountdb_type") + jsnMediatorCfg, err := jsnCfg.MediatorJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_host"); hasOpt { - cfg.AccountDBHost, _ = c.GetString("global", "accountdb_host") + jsnCdrstatsCfg, err := jsnCfg.CdrStatsJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_port"); hasOpt { - cfg.AccountDBPort, _ = c.GetString("global", "accountdb_port") + jsnCdreCfg, err := jsnCfg.CdreJsonCfgs() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_name"); hasOpt { - cfg.AccountDBName, _ = c.GetString("global", "accountdb_name") + jsnCdrcCfg, err := jsnCfg.CdrcJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_user"); hasOpt { - cfg.AccountDBUser, _ = c.GetString("global", "accountdb_user") + jsnSMCfg, err := jsnCfg.SessionManagerJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "accountdb_passwd"); hasOpt { - cfg.AccountDBPass, _ = c.GetString("global", "accountdb_passwd") + jsnFSCfg, err := jsnCfg.FSJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_type"); hasOpt { - cfg.StorDBType, _ = c.GetString("global", "stordb_type") + jsnKamCfg, err := jsnCfg.KamailioJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_host"); hasOpt { - cfg.StorDBHost, _ = c.GetString("global", "stordb_host") + jsnOsipsCfg, err := jsnCfg.OsipsJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_port"); hasOpt { - cfg.StorDBPort, _ = c.GetString("global", "stordb_port") + jsnHistServCfg, err := jsnCfg.HistServJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_name"); hasOpt { - cfg.StorDBName, _ = c.GetString("global", "stordb_name") + jsnHistAgentCfg, err := jsnCfg.HistAgentJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_user"); hasOpt { - cfg.StorDBUser, _ = c.GetString("global", "stordb_user") + jsnMailerCfg, err := jsnCfg.MailerJsonCfg() + if err != nil { + return err } - if hasOpt = c.HasOption("global", "stordb_passwd"); hasOpt { - cfg.StorDBPass, _ = c.GetString("global", "stordb_passwd") - } - if hasOpt = c.HasOption("global", "stordb_max_open_conns"); hasOpt { - cfg.StorDBMaxOpenConns, _ = c.GetInt("global", "stordb_max_open_conns") - } - if hasOpt = c.HasOption("global", "stordb_max_idle_conns"); hasOpt { - cfg.StorDBMaxIdleConns, _ = c.GetInt("global", "stordb_max_idle_conns") - } - if hasOpt = c.HasOption("global", "dbdata_encoding"); hasOpt { - cfg.DBDataEncoding, _ = c.GetString("global", "dbdata_encoding") - } - if hasOpt = c.HasOption("global", "rpc_json_listen"); hasOpt { - cfg.RPCJSONListen, _ = c.GetString("global", "rpc_json_listen") - } - if hasOpt = c.HasOption("global", "rpc_gob_listen"); hasOpt { - cfg.RPCGOBListen, _ = c.GetString("global", "rpc_gob_listen") - } - if hasOpt = c.HasOption("global", "http_listen"); hasOpt { - cfg.HTTPListen, _ = c.GetString("global", "http_listen") - } - if hasOpt = c.HasOption("global", "default_reqtype"); hasOpt { - cfg.DefaultReqType, _ = c.GetString("global", "default_reqtype") - } - if hasOpt = c.HasOption("global", "default_category"); hasOpt { - cfg.DefaultCategory, _ = c.GetString("global", "default_category") - } - if hasOpt = c.HasOption("global", "default_tenant"); hasOpt { - cfg.DefaultTenant, _ = c.GetString("global", "default_tenant") - } - if hasOpt = c.HasOption("global", "default_subject"); hasOpt { - cfg.DefaultSubject, _ = c.GetString("global", "default_subject") - } - if hasOpt = c.HasOption("global", "rounding_decimals"); hasOpt { - cfg.RoundingDecimals, _ = c.GetInt("global", "rounding_decimals") - } - if hasOpt = c.HasOption("global", "http_skip_tls_veify"); hasOpt { - cfg.HttpSkipTlsVerify, _ = c.GetBool("global", "http_skip_tls_veify") - } - if hasOpt = c.HasOption("global", "tpexport_dir"); hasOpt { - cfg.TpExportPath, _ = c.GetString("global", "tpexport_dir") - } - // XML config path defined, try loading the document - if hasOpt = c.HasOption("global", "xmlcfg_path"); hasOpt { - xmlCfgPath, _ := c.GetString("global", "xmlcfg_path") - xmlFile, err := os.Open(xmlCfgPath) - if err != nil { - return nil, err + // All good, start populating config variables + if jsnRatingDbCfg != nil { + if jsnRatingDbCfg.Db_type != nil { + self.RatingDBType = *jsnRatingDbCfg.Db_type } - if cgrXmlCfgDoc, err := ParseCgrXmlConfig(xmlFile); err != nil { - return nil, err - } else { - cfg.XmlCfgDocument = cgrXmlCfgDoc + if jsnRatingDbCfg.Db_host != nil { + self.RatingDBHost = *jsnRatingDbCfg.Db_host + } + if jsnRatingDbCfg.Db_port != nil { + self.RatingDBPort = strconv.Itoa(*jsnRatingDbCfg.Db_port) + } + if jsnRatingDbCfg.Db_name != nil { + self.RatingDBName = *jsnRatingDbCfg.Db_name + } + if jsnRatingDbCfg.Db_user != nil { + self.RatingDBUser = *jsnRatingDbCfg.Db_user + } + if jsnRatingDbCfg.Db_passwd != nil { + self.RatingDBPass = *jsnRatingDbCfg.Db_passwd } } - if hasOpt = c.HasOption("rater", "enabled"); hasOpt { - cfg.RaterEnabled, _ = c.GetBool("rater", "enabled") - } - if hasOpt = c.HasOption("rater", "balancer"); hasOpt { - cfg.RaterBalancer, _ = c.GetString("rater", "balancer") - } - if hasOpt = c.HasOption("balancer", "enabled"); hasOpt { - cfg.BalancerEnabled, _ = c.GetBool("balancer", "enabled") - } - if hasOpt = c.HasOption("scheduler", "enabled"); hasOpt { - cfg.SchedulerEnabled, _ = c.GetBool("scheduler", "enabled") - } - if hasOpt = c.HasOption("cdrs", "enabled"); hasOpt { - cfg.CDRSEnabled, _ = c.GetBool("cdrs", "enabled") - } - if hasOpt = c.HasOption("cdrs", "extra_fields"); hasOpt { - extraFieldsStr, _ := c.GetString("cdrs", "extra_fields") - if extraFields, err := utils.ParseRSRFields(extraFieldsStr, utils.FIELDS_SEP); err != nil { - return nil, err - } else { - cfg.CDRSExtraFields = extraFields + if jsnAccountingDbCfg != nil { + if jsnAccountingDbCfg.Db_type != nil { + self.AccountDBType = *jsnAccountingDbCfg.Db_type + } + if jsnAccountingDbCfg.Db_host != nil { + self.AccountDBHost = *jsnAccountingDbCfg.Db_host + } + if jsnAccountingDbCfg.Db_port != nil { + self.AccountDBPort = strconv.Itoa(*jsnAccountingDbCfg.Db_port) + } + if jsnAccountingDbCfg.Db_name != nil { + self.AccountDBName = *jsnAccountingDbCfg.Db_name + } + if jsnAccountingDbCfg.Db_user != nil { + self.AccountDBUser = *jsnAccountingDbCfg.Db_user + } + if jsnAccountingDbCfg.Db_passwd != nil { + self.AccountDBPass = *jsnAccountingDbCfg.Db_passwd } } - if hasOpt = c.HasOption("cdrs", "mediator"); hasOpt { - cfg.CDRSMediator, _ = c.GetString("cdrs", "mediator") + if jsnStorDbCfg != nil { + if jsnStorDbCfg.Db_type != nil { + self.StorDBType = *jsnStorDbCfg.Db_type + } + if jsnStorDbCfg.Db_host != nil { + self.StorDBHost = *jsnStorDbCfg.Db_host + } + if jsnStorDbCfg.Db_port != nil { + self.StorDBPort = strconv.Itoa(*jsnStorDbCfg.Db_port) + } + if jsnStorDbCfg.Db_name != nil { + self.StorDBName = *jsnStorDbCfg.Db_name + } + if jsnStorDbCfg.Db_user != nil { + self.StorDBUser = *jsnStorDbCfg.Db_user + } + if jsnStorDbCfg.Db_passwd != nil { + self.StorDBPass = *jsnStorDbCfg.Db_passwd + } + if jsnStorDbCfg.Max_open_conns != nil { + self.StorDBMaxOpenConns = *jsnStorDbCfg.Max_open_conns + } + if jsnStorDbCfg.Max_idle_conns != nil { + self.StorDBMaxIdleConns = *jsnStorDbCfg.Max_idle_conns + } } - if hasOpt = c.HasOption("cdrs", "cdrstats"); hasOpt { - cfg.CDRSStats, _ = c.GetString("cdrs", "cdrstats") + if jsnGeneralCfg != nil { + if jsnGeneralCfg.Dbdata_encoding != nil { + self.DBDataEncoding = *jsnGeneralCfg.Dbdata_encoding + } + if jsnGeneralCfg.Default_reqtype != nil { + self.DefaultReqType = *jsnGeneralCfg.Default_reqtype + } + if jsnGeneralCfg.Default_category != nil { + self.DefaultCategory = *jsnGeneralCfg.Default_category + } + if jsnGeneralCfg.Default_tenant != nil { + self.DefaultTenant = *jsnGeneralCfg.Default_tenant + } + if jsnGeneralCfg.Default_subject != nil { + self.DefaultSubject = *jsnGeneralCfg.Default_subject + } + if jsnGeneralCfg.Rounding_decimals != nil { + self.RoundingDecimals = *jsnGeneralCfg.Rounding_decimals + } + if jsnGeneralCfg.Http_skip_tls_veify != nil { + self.HttpSkipTlsVerify = *jsnGeneralCfg.Http_skip_tls_veify + } + if jsnGeneralCfg.Tpexport_dir != nil { + self.TpExportPath = *jsnGeneralCfg.Tpexport_dir + } } - if hasOpt = c.HasOption("cdrs", "store_disable"); hasOpt { - cfg.CDRSStoreDisable, _ = c.GetBool("cdrs", "store_disable") + if jsnListenCfg != nil { + if jsnListenCfg.Rpc_json != nil { + self.RPCJSONListen = *jsnListenCfg.Rpc_json + } + if jsnListenCfg.Rpc_gob != nil { + self.RPCGOBListen = *jsnListenCfg.Rpc_gob + } + if jsnListenCfg.Http != nil { + self.HTTPListen = *jsnListenCfg.Http + } } - if hasOpt = c.HasOption("cdrstats", "enabled"); hasOpt { - cfg.CDRStatsEnabled, _ = c.GetBool("cdrstats", "enabled") + if jsnRaterCfg != nil { + if jsnRaterCfg.Enabled != nil { + self.RaterEnabled = *jsnRaterCfg.Enabled + } + if jsnRaterCfg.Balancer != nil { + self.RaterBalancer = *jsnRaterCfg.Balancer + } } - if cfg.CDRStatConfig, err = ParseCfgDefaultCDRStatsConfig(c); err != nil { - return nil, err + if jsnBalancerCfg != nil && jsnBalancerCfg.Enabled != nil { + self.BalancerEnabled = *jsnBalancerCfg.Enabled } - if hasOpt = c.HasOption("cdre", "cdr_format"); hasOpt { - cfg.CdreDefaultInstance.CdrFormat, _ = c.GetString("cdre", "cdr_format") + if jsnSchedCfg != nil && jsnSchedCfg.Enabled != nil { + self.SchedulerEnabled = *jsnSchedCfg.Enabled } - if hasOpt = c.HasOption("cdre", "mask_destination_id"); hasOpt { - cfg.CdreDefaultInstance.MaskDestId, _ = c.GetString("cdre", "mask_destination_id") + if jsnCdrsCfg != nil { + if jsnCdrsCfg.Enabled != nil { + self.CDRSEnabled = *jsnCdrsCfg.Enabled + } + if jsnCdrsCfg.Extra_fields != nil { + if self.CDRSExtraFields, err = utils.ParseRSRFieldsFromSlice(*jsnCdrsCfg.Extra_fields); err != nil { + return err + } + } + if jsnCdrsCfg.Mediator != nil { + self.CDRSMediator = *jsnCdrsCfg.Mediator + } + if jsnCdrsCfg.Cdrstats != nil { + self.CDRSStats = *jsnCdrsCfg.Cdrstats + } + if jsnCdrsCfg.Store_disable != nil { + self.CDRSStoreDisable = *jsnCdrsCfg.Store_disable + } } - if hasOpt = c.HasOption("cdre", "mask_length"); hasOpt { - cfg.CdreDefaultInstance.MaskLength, _ = c.GetInt("cdre", "mask_length") + if jsnCdrstatsCfg != nil { + if jsnCdrstatsCfg.Enabled != nil { + self.CDRStatsEnabled = *jsnCdrstatsCfg.Enabled + } + if jsnCdrstatsCfg != nil { // Have CDRStats config, load it in default object + if self.CDRStatConfig == nil { + self.CDRStatConfig = &CdrStatsConfig{Id: utils.META_DEFAULT} + } + if err = self.CDRStatConfig.loadFromJsonCfg(jsnCdrstatsCfg); err != nil { + return err + } + } } - if hasOpt = c.HasOption("cdre", "data_usage_multiply_factor"); hasOpt { - cfg.CdreDefaultInstance.DataUsageMultiplyFactor, _ = c.GetFloat64("cdre", "data_usage_multiply_factor") - } - if hasOpt = c.HasOption("cdre", "cost_multiply_factor"); hasOpt { - cfg.CdreDefaultInstance.CostMultiplyFactor, _ = c.GetFloat64("cdre", "cost_multiply_factor") - } - if hasOpt = c.HasOption("cdre", "cost_rounding_decimals"); hasOpt { - cfg.CdreDefaultInstance.CostRoundingDecimals, _ = c.GetInt("cdre", "cost_rounding_decimals") - } - if hasOpt = c.HasOption("cdre", "cost_shift_digits"); hasOpt { - cfg.CdreDefaultInstance.CostShiftDigits, _ = c.GetInt("cdre", "cost_shift_digits") - } - if hasOpt = c.HasOption("cdre", "export_template"); hasOpt { // Load configs for csv normally from template, fixed_width from xml file - exportTemplate, _ := c.GetString("cdre", "export_template") - if strings.HasPrefix(exportTemplate, utils.XML_PROFILE_PREFIX) { - if xmlTemplates := cfg.XmlCfgDocument.GetCdreCfgs(exportTemplate[len(utils.XML_PROFILE_PREFIX):]); xmlTemplates != nil { - if cfg.CdreDefaultInstance, err = NewCdreConfigFromXmlCdreCfg(xmlTemplates[exportTemplate[len(utils.XML_PROFILE_PREFIX):]]); err != nil { - return nil, err + if jsnCdreCfg != nil { + if self.CdreProfiles == nil { + self.CdreProfiles = make(map[string]*CdreConfig) + } + for profileName, jsnCdre1Cfg := range jsnCdreCfg { + if _, hasProfile := self.CdreProfiles[profileName]; !hasProfile { // New profile, create before loading from json + self.CdreProfiles[profileName] = new(CdreConfig) + if profileName != utils.META_DEFAULT { + self.CdreProfiles[profileName] = self.CdreProfiles[utils.META_DEFAULT] // Load defaults into newly initialized config } } - } else { // Not loading out of template - if flds, err := NewCfgCdrFieldsFromIds(cfg.CdreDefaultInstance.CdrFormat == utils.CDRE_FIXED_WIDTH, - strings.Split(exportTemplate, string(utils.CSV_SEP))...); err != nil { - return nil, err - } else { - cfg.CdreDefaultInstance.ContentFields = flds + if err = self.CdreProfiles[profileName].loadFromJsonCfg(jsnCdre1Cfg); err != nil { // Update the existing profile with content from json config + return err } } } - if hasOpt = c.HasOption("cdre", "export_dir"); hasOpt { - cfg.CdreDefaultInstance.ExportDir, _ = c.GetString("cdre", "export_dir") - } - // CDRC Default instance parsing - if cdrcFileCfgInst, err := NewCdrcConfigFromFileParams(c); err != nil { - return nil, err - } else { - cfg.CdrcInstances = []*CdrcConfig{cdrcFileCfgInst} - } - if cfg.XmlCfgDocument != nil { // Add the possible configured instances inside xml doc - for id, xmlCdrcCfg := range cfg.XmlCfgDocument.GetCdrcCfgs("") { - if cdrcInst, err := NewCdrcConfigFromCgrXmlCdrcCfg(id, xmlCdrcCfg); err != nil { - return nil, err - } else { - cfg.CdrcInstances = append(cfg.CdrcInstances, cdrcInst) + if jsnCdrcCfg != nil { + if self.CdrcProfiles == nil { + self.CdrcProfiles = make(map[string]*CdrcConfig) + } + for profileName, jsnCrc1Cfg := range jsnCdrcCfg { + if _, hasProfile := self.CdrcProfiles[profileName]; !hasProfile { + self.CdrcProfiles[profileName] = new(CdrcConfig) + if profileName != utils.META_DEFAULT { + self.CdrcProfiles[profileName] = self.CdrcProfiles[utils.META_DEFAULT] // Load defaults into newly initialized config + } + } + if err = self.CdrcProfiles[profileName].loadFromJsonCfg(jsnCrc1Cfg); err != nil { + return err } } } - if hasOpt = c.HasOption("mediator", "enabled"); hasOpt { - cfg.MediatorEnabled, _ = c.GetBool("mediator", "enabled") - } - if hasOpt = c.HasOption("mediator", "rater"); hasOpt { - cfg.MediatorRater, _ = c.GetString("mediator", "rater") - } - if hasOpt = c.HasOption("mediator", "reconnects"); hasOpt { - cfg.MediatorReconnects, _ = c.GetInt("mediator", "reconnects") - } - if hasOpt = c.HasOption("mediator", "cdrstats"); hasOpt { - cfg.MediatorStats, _ = c.GetString("mediator", "cdrstats") - } - if hasOpt = c.HasOption("mediator", "store_disable"); hasOpt { - cfg.MediatorStoreDisable, _ = c.GetBool("mediator", "store_disable") - } - if hasOpt = c.HasOption("session_manager", "enabled"); hasOpt { - cfg.SMEnabled, _ = c.GetBool("session_manager", "enabled") - } - if hasOpt = c.HasOption("session_manager", "switch_type"); hasOpt { - cfg.SMSwitchType, _ = c.GetString("session_manager", "switch_type") - } - if hasOpt = c.HasOption("session_manager", "rater"); hasOpt { - cfg.SMRater, _ = c.GetString("session_manager", "rater") - } - if hasOpt = c.HasOption("session_manager", "cdrs"); hasOpt { - cfg.SMCdrS, _ = c.GetString("session_manager", "cdrs") - } - if hasOpt = c.HasOption("session_manager", "reconnects"); hasOpt { - cfg.SMReconnects, _ = c.GetInt("session_manager", "reconnects") - } - if hasOpt = c.HasOption("session_manager", "debit_interval"); hasOpt { - cfg.SMDebitInterval, _ = c.GetInt("session_manager", "debit_interval") - } - if hasOpt = c.HasOption("session_manager", "min_call_duration"); hasOpt { - minCallDurStr, _ := c.GetString("session_manager", "min_call_duration") - if cfg.SMMinCallDuration, err = utils.ParseDurationWithSecs(minCallDurStr); err != nil { - return nil, err + if jsnSMCfg != nil { + if jsnSMCfg.Enabled != nil { + self.SMEnabled = *jsnSMCfg.Enabled + } + if jsnSMCfg.Switch_type != nil { + self.SMSwitchType = *jsnSMCfg.Switch_type + } + if jsnSMCfg.Rater != nil { + self.SMRater = *jsnSMCfg.Rater + } + if jsnSMCfg.Cdrs != nil { + self.SMCdrS = *jsnSMCfg.Cdrs + } + if jsnSMCfg.Reconnects != nil { + self.SMReconnects = *jsnSMCfg.Reconnects + } + if jsnSMCfg.Debit_interval != nil { + self.SMDebitInterval = *jsnSMCfg.Debit_interval + } + if jsnSMCfg.Max_call_duration != nil { + if self.SMMaxCallDuration, err = utils.ParseDurationWithSecs(*jsnSMCfg.Max_call_duration); err != nil { + return err + } + } + if jsnSMCfg.Min_call_duration != nil { + if self.SMMinCallDuration, err = utils.ParseDurationWithSecs(*jsnSMCfg.Min_call_duration); err != nil { + return err + } } } - if hasOpt = c.HasOption("session_manager", "max_call_duration"); hasOpt { - maxCallDurStr, _ := c.GetString("session_manager", "max_call_duration") - if cfg.SMMaxCallDuration, err = utils.ParseDurationWithSecs(maxCallDurStr); err != nil { - return nil, err + if jsnMediatorCfg != nil { + if jsnMediatorCfg.Enabled != nil { + self.MediatorEnabled = *jsnMediatorCfg.Enabled + } + if jsnMediatorCfg.Reconnects != nil { + self.MediatorReconnects = *jsnMediatorCfg.Reconnects + } + if jsnMediatorCfg.Rater != nil { + self.MediatorRater = *jsnMediatorCfg.Rater + } + if jsnMediatorCfg.Cdrstats != nil { + self.MediatorStats = *jsnMediatorCfg.Cdrstats + } + if jsnMediatorCfg.Store_disable != nil { + self.MediatorStoreDisable = *jsnMediatorCfg.Store_disable } } - if hasOpt = c.HasOption("freeswitch", "server"); hasOpt { - cfg.FreeswitchServer, _ = c.GetString("freeswitch", "server") - } - if hasOpt = c.HasOption("freeswitch", "passwd"); hasOpt { - cfg.FreeswitchPass, _ = c.GetString("freeswitch", "passwd") - } - if hasOpt = c.HasOption("freeswitch", "reconnects"); hasOpt { - cfg.FreeswitchReconnects, _ = c.GetInt("freeswitch", "reconnects") - } - if hasOpt = c.HasOption("freeswitch", "min_dur_low_balance"); hasOpt { - minDurStr, _ := c.GetString("freeswitch", "min_dur_low_balance") - if cfg.FSMinDurLowBalance, err = utils.ParseDurationWithSecs(minDurStr); err != nil { - return nil, err + if jsnFSCfg != nil { + if jsnFSCfg.Server != nil { + self.FreeswitchServer = *jsnFSCfg.Server + } + if jsnFSCfg.Password != nil { + self.FreeswitchPass = *jsnFSCfg.Password + } + if jsnFSCfg.Reconnects != nil { + self.FreeswitchReconnects = *jsnFSCfg.Reconnects + } + if jsnFSCfg.Min_dur_low_balance != nil { + if self.FSMinDurLowBalance, err = utils.ParseDurationWithSecs(*jsnFSCfg.Min_dur_low_balance); err != nil { + return err + } + } + if jsnFSCfg.Low_balance_ann_file != nil { + self.FSLowBalanceAnnFile = *jsnFSCfg.Low_balance_ann_file + } + if jsnFSCfg.Empty_balance_context != nil { + self.FSEmptyBalanceContext = *jsnFSCfg.Empty_balance_context + } + if jsnFSCfg.Empty_balance_ann_file != nil { + self.FSEmptyBalanceAnnFile = *jsnFSCfg.Empty_balance_ann_file + } + if jsnFSCfg.Cdr_extra_fields != nil { + if self.FSCdrExtraFields, err = utils.ParseRSRFieldsFromSlice(*jsnFSCfg.Cdr_extra_fields); err != nil { + return err + } } } - if hasOpt = c.HasOption("freeswitch", "low_balance_ann_file"); hasOpt { - cfg.FSLowBalanceAnnFile, _ = c.GetString("freeswitch", "low_balance_ann_file") - } - if hasOpt = c.HasOption("freeswitch", "empty_balance_context"); hasOpt { - cfg.FSEmptyBalanceContext, _ = c.GetString("freeswitch", "empty_balance_context") - } - if hasOpt = c.HasOption("freeswitch", "empty_balance_ann_file"); hasOpt { - cfg.FSEmptyBalanceAnnFile, _ = c.GetString("freeswitch", "empty_balance_ann_file") - } - if hasOpt = c.HasOption("freeswitch", "cdr_extra_fields"); hasOpt { - extraFieldsStr, _ := c.GetString("freeswitch", "cdr_extra_fields") - if extraFields, err := utils.ParseRSRFields(extraFieldsStr, utils.FIELDS_SEP); err != nil { - return nil, err - } else { - cfg.FSCdrExtraFields = extraFields + if jsnOsipsCfg != nil { + if jsnOsipsCfg.Listen_udp != nil { + self.OsipsListenUdp = *jsnOsipsCfg.Listen_udp + } + if jsnOsipsCfg.Mi_addr != nil { + self.OsipsMiAddr = *jsnOsipsCfg.Mi_addr + } + if jsnOsipsCfg.Events_subscribe_interval != nil { + if self.OsipsEvSubscInterval, err = utils.ParseDurationWithSecs(*jsnOsipsCfg.Events_subscribe_interval); err != nil { + return err + } + } + if jsnOsipsCfg.Reconnects != nil { + self.OsipsReconnects = *jsnOsipsCfg.Reconnects } } - if hasOpt = c.HasOption("opensips", "listen_udp"); hasOpt { - cfg.OsipsListenUdp, _ = c.GetString("opensips", "listen_udp") - } - if hasOpt = c.HasOption("opensips", "mi_addr"); hasOpt { - cfg.OsipsMiAddr, _ = c.GetString("opensips", "mi_addr") - } - if hasOpt = c.HasOption("opensips", "events_subscribe_interval"); hasOpt { - evSubscIntervalStr, _ := c.GetString("opensips", "events_subscribe_interval") - if cfg.OsipsEvSubscInterval, err = utils.ParseDurationWithSecs(evSubscIntervalStr); err != nil { - return nil, err + if jsnKamCfg != nil { + if jsnKamCfg.Evapi_addr != nil { + self.KamailioEvApiAddr = *jsnKamCfg.Evapi_addr + } + if jsnKamCfg.Reconnects != nil { + self.KamailioReconnects = *jsnKamCfg.Reconnects } } - if hasOpt = c.HasOption("opensips", "reconnects"); hasOpt { - cfg.OsipsReconnects, _ = c.GetInt("opensips", "reconnects") - } - if hasOpt = c.HasOption("kamailio", "evapi_addr"); hasOpt { - cfg.KamailioEvApiAddr, _ = c.GetString("kamailio", "evapi_addr") - } - if hasOpt = c.HasOption("kamailio", "reconnects"); hasOpt { - cfg.KamailioReconnects, _ = c.GetInt("kamailio", "reconnects") - } - if cfg.DerivedChargers, err = ParseCfgDerivedCharging(c); err != nil { - return nil, err - } - if hasOpt = c.HasOption("derived_charging", "combined_chargers"); hasOpt { - cfg.CombinedDerivedChargers, _ = c.GetBool("derived_charging", "combined_chargers") - } - if hasOpt = c.HasOption("history_agent", "enabled"); hasOpt { - cfg.HistoryAgentEnabled, _ = c.GetBool("history_agent", "enabled") - } - if hasOpt = c.HasOption("history_agent", "server"); hasOpt { - cfg.HistoryServer, _ = c.GetString("history_agent", "server") - } - if hasOpt = c.HasOption("history_server", "enabled"); hasOpt { - cfg.HistoryServerEnabled, _ = c.GetBool("history_server", "enabled") - } - if hasOpt = c.HasOption("history_server", "history_dir"); hasOpt { - cfg.HistoryDir, _ = c.GetString("history_server", "history_dir") - } - if hasOpt = c.HasOption("history_server", "save_interval"); hasOpt { - saveIntvlStr, _ := c.GetString("history_server", "save_interval") - if cfg.HistorySaveInterval, err = utils.ParseDurationWithSecs(saveIntvlStr); err != nil { - return nil, err + if jsnHistAgentCfg != nil { + if jsnHistAgentCfg.Enabled != nil { + self.HistoryAgentEnabled = *jsnHistAgentCfg.Enabled + } + if jsnHistAgentCfg.Server != nil { + self.HistoryServer = *jsnHistAgentCfg.Server } } - if hasOpt = c.HasOption("mailer", "server"); hasOpt { - cfg.MailerServer, _ = c.GetString("mailer", "server") + if jsnHistServCfg != nil { + if jsnHistServCfg.Enabled != nil { + self.HistoryServerEnabled = *jsnHistServCfg.Enabled + } + if jsnHistServCfg.History_dir != nil { + self.HistoryDir = *jsnHistServCfg.History_dir + } + if jsnHistServCfg.Save_interval != nil { + if self.HistorySaveInterval, err = utils.ParseDurationWithSecs(*jsnHistServCfg.Save_interval); err != nil { + return err + } + } } - if hasOpt = c.HasOption("mailer", "auth_user"); hasOpt { - cfg.MailerAuthUser, _ = c.GetString("mailer", "auth_user") + if jsnMailerCfg != nil { + if jsnMailerCfg.Server != nil { + self.MailerServer = *jsnMailerCfg.Server + } + if jsnMailerCfg.Auth_user != nil { + self.MailerAuthUser = *jsnMailerCfg.Auth_user + } + if jsnMailerCfg.Auth_passwd != nil { + self.MailerAuthPass = *jsnMailerCfg.Auth_passwd + } + if jsnMailerCfg.From_address != nil { + self.MailerFromAddr = *jsnMailerCfg.From_address + } } - if hasOpt = c.HasOption("mailer", "auth_passwd"); hasOpt { - cfg.MailerAuthPass, _ = c.GetString("mailer", "auth_passwd") - } - if hasOpt = c.HasOption("mailer", "from_address"); hasOpt { - cfg.MailerFromAddr, _ = c.GetString("mailer", "from_address") - } - return cfg, nil + return nil } diff --git a/config/cgrates_cfg_defaults.json b/config/config_defaults.go similarity index 74% rename from config/cgrates_cfg_defaults.json rename to config/config_defaults.go index 876824d85..2985b3ea1 100644 --- a/config/cgrates_cfg_defaults.json +++ b/config/config_defaults.go @@ -1,3 +1,24 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package config + +const CGRATES_CFG_JSON = ` { // Real-time Charging System for Telecom & ISP environments @@ -111,13 +132,14 @@ "mediation_run_ids": [], // filter on CDR MediationRunId fields "rated_accounts": [], // filter on CDR RatedAccount fields "rated_subjects": [], // filter on CDR RatedSubject fields - "cost_intervals": [], // filter on CDR Cost + "cost_interval": [], // filter on CDR Cost }, "cdre": { - "CDRE-FW1": { + "*default": { "cdr_format": "csv", // exported CDRs format + "field_separator": ",", "data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes) "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT "cost_rounding_decimals": -1, // rounding decimals for Cost values. -1 to disable rounding @@ -127,21 +149,21 @@ "export_dir": "/var/log/cgrates/cdre", // path where the exported CDRs will be placed "header_fields": [], // template of the exported header fields "content_fields": [ // template of the exported content fields - {"tag": "CgrId", "type": "cdrfield", "value": "cgrid", "width": 40, "mandatory": true}, - {"tag":"RunId", "type": "cdrfield", "value": "mediation_runid", "width": 20}, - {"tag":"Tor", "type": "cdrfield", "value": "tor", "width": 6}, - {"tag":"AccId", "type": "cdrfield", "value": "accid", "width": 36}, - {"tag":"ReqType", "type": "cdrfield", "value": "reqtype", "width": 13}, - {"tag":"Direction", "type": "cdrfield", "value": "direction", "width": 4}, - {"tag":"Tenant", "type": "cdrfield", "value": "tenant", "width": 24}, - {"tag":"Category", "type": "cdrfield", "value": "category", "width": 10}, - {"tag":"Account", "type": "cdrfield", "value": "account", "width": 24}, - {"tag":"Subject", "type": "cdrfield", "value": "subject", "width": 24}, - {"tag":"Destination", "type": "cdrfield", "value": "destination", "width": 24}, - {"tag":"SetupTime", "type": "cdrfield", "value": "setup_time", "layout": "2006-01-02T15:04:05Z07:00", "width": 30}, - {"tag":"AnswerTime", "type": "cdrfield", "value": "answer_time", "layout": "2006-01-02T15:04:05Z07:00", "width": 30}, - {"tag":"Usage", "type": "cdrfield", "value": "usage", "width": 30}, - {"tag":"Cost", "type": "cdrfield", "value": "cost", "width": 24}, + {"tag": "CgrId", "cdr_field_id": "cgrid", "type": "cdrfield", "value": "cgrid"}, + {"tag":"RunId", "cdr_field_id": "mediation_runid", "type": "cdrfield", "value": "mediation_runid"}, + {"tag":"Tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "tor"}, + {"tag":"AccId", "cdr_field_id": "accid", "type": "cdrfield", "value": "accid"}, + {"tag":"ReqType", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "reqtype"}, + {"tag":"Direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "direction"}, + {"tag":"Tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "tenant"}, + {"tag":"Category", "cdr_field_id": "category", "type": "cdrfield", "value": "category"}, + {"tag":"Account", "cdr_field_id": "account", "type": "cdrfield", "value": "account"}, + {"tag":"Subject", "cdr_field_id": "subject", "type": "cdrfield", "value": "subject"}, + {"tag":"Destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "destination"}, + {"tag":"SetupTime", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "setup_time", "layout": "2006-01-02T15:04:05Z07:00"}, + {"tag":"AnswerTime", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "answer_time", "layout": "2006-01-02T15:04:05Z07:00"}, + {"tag":"Usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "usage"}, + {"tag":"Cost", "cdr_field_id": "cost", "type": "cdrfield", "value": "cost"}, ], "trailer_fields": [], // template of the exported trailer fields } @@ -149,7 +171,7 @@ "cdrc": { - "instance1": { + "*default": { "enabled": false, // enable CDR client functionality "cdrs_address": "internal", // address where to reach CDR server. "cdr_format": "csv", // CDR file format @@ -160,19 +182,18 @@ "cdr_out_dir": "/var/log/cgrates/cdrc/out", // absolute path towards the directory where processed CDRs will be moved "cdr_source_id": "freeswitch_csv", // free form field, tag identifying the source of the CDRs within CDRS database "cdr_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"tag": "accid", "value": "0;13"}, - {"tag": "reqtype", "value": "1"}, - {"tag": "direction", "value": "2"}, - {"tag": "tenant", "value": "3"}, - {"tag": "category", "value": "4"}, - {"tag": "account", "value": "5"}, - {"tag": "subject", "value": "6"}, - {"tag": "destination", "value": "7"}, - {"tag": "setup_time", "value": "8"}, - {"tag": "answer_time", "value": "9"}, - {"tag": "usage", "value": "10"}, - {"tag": "extr1", "value": "11"}, - {"tag": "extr2", "value": "12"}, + {"tag": "tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "2", "mandatory": true}, + {"tag": "accid", "cdr_field_id": "accid", "type": "cdrfield", "value": "3", "mandatory": true}, + {"tag": "reqtype", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "4", "mandatory": true}, + {"tag": "direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "5", "mandatory": true}, + {"tag": "tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "6", "mandatory": true}, + {"tag": "category", "cdr_field_id": "category", "type": "cdrfield", "value": "7", "mandatory": true}, + {"tag": "account", "cdr_field_id": "account", "type": "cdrfield", "value": "8", "mandatory": true}, + {"tag": "subject", "cdr_field_id": "subject", "type": "cdrfield", "value": "9", "mandatory": true}, + {"tag": "destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "10", "mandatory": true}, + {"tag": "setup_time", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "11", "mandatory": true}, + {"tag": "answer_time", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "12", "mandatory": true}, + {"tag": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "13", "mandatory": true}, ], } }, @@ -236,4 +257,4 @@ "from_address": "cgr-mailer@localhost.localdomain" // from address used when sending emails out }, -} \ No newline at end of file +}` diff --git a/config/config_json.go b/config/config_json.go index 189a987f9..003108664 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -49,23 +49,24 @@ const ( ) // Loads the json config out of io.Reader, eg other sources than file, maybe over http -func NewCgrJsonCfg(r io.Reader) (CgrJsonCfg, error) { +func NewCgrJsonCfgFromReader(r io.Reader) (*CgrJsonCfg, error) { var cgrJsonCfg CgrJsonCfg jr := JsonConfigReader.New(r) if err := json.NewDecoder(jr).Decode(&cgrJsonCfg); err != nil { return nil, err } - return cgrJsonCfg, nil + return &cgrJsonCfg, nil } // Loads the config out of file -func NewCgrJsonCfgFromFile(fpath string) (CgrJsonCfg, error) { +func NewCgrJsonCfgFromFile(fpath string) (*CgrJsonCfg, error) { cfgFile, err := os.Open(fpath) if err != nil { return nil, err } defer cfgFile.Close() - return NewCgrJsonCfg(cfgFile) + + return NewCgrJsonCfgFromReader(cfgFile) } // Main object holding the loaded config as section raw messages diff --git a/config/config_json_test.go b/config/config_json_test.go index 4f4722c43..f93dfdffe 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -21,19 +21,21 @@ package config import ( "github.com/cgrates/cgrates/utils" "reflect" + "strings" "testing" ) -var cgrJsonCfg CgrJsonCfg +var dfCgrJsonCfg *CgrJsonCfg -func TestNewCgrJsonCfgFromFile(t *testing.T) { +// Loads up the default configuration and tests it's sections one by one +func TestDfNewdfCgrJsonCfgFromReader(t *testing.T) { var err error - if cgrJsonCfg, err = NewCgrJsonCfgFromFile("cgrates_cfg_defaults.json"); err != nil { - t.Error(err.Error()) + if dfCgrJsonCfg, err = NewCgrJsonCfgFromReader(strings.NewReader(CGRATES_CFG_JSON)); err != nil { + t.Error(err) } } -func TestGeneralJsonCfg(t *testing.T) { +func TestDfGeneralJsonCfg(t *testing.T) { eCfg := &GeneralJsonCfg{ Http_skip_tls_veify: utils.BoolPointer(false), Rounding_decimals: utils.IntPointer(10), @@ -43,26 +45,26 @@ func TestGeneralJsonCfg(t *testing.T) { Default_category: utils.StringPointer("call"), Default_tenant: utils.StringPointer("cgrates.org"), Default_subject: utils.StringPointer("cgrates")} - if gCfg, err := cgrJsonCfg.GeneralJsonCfg(); err != nil { + if gCfg, err := dfCgrJsonCfg.GeneralJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, gCfg) { t.Error("Received: ", gCfg) } } -func TestListenJsonCfg(t *testing.T) { +func TestDfListenJsonCfg(t *testing.T) { eCfg := &ListenJsonCfg{ Rpc_json: utils.StringPointer("127.0.0.1:2012"), Rpc_gob: utils.StringPointer("127.0.0.1:2013"), Http: utils.StringPointer("127.0.0.1:2080")} - if cfg, err := cgrJsonCfg.ListenJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.ListenJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestDbJsonCfg(t *testing.T) { +func TestDfDbJsonCfg(t *testing.T) { eCfg := &DbJsonCfg{ Db_type: utils.StringPointer("redis"), Db_host: utils.StringPointer("127.0.0.1"), @@ -71,7 +73,7 @@ func TestDbJsonCfg(t *testing.T) { Db_user: utils.StringPointer(""), Db_passwd: utils.StringPointer(""), } - if cfg, err := cgrJsonCfg.DbJsonCfg(RATINGDB_JSN); err != nil { + if cfg, err := dfCgrJsonCfg.DbJsonCfg(RATINGDB_JSN); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) @@ -84,7 +86,7 @@ func TestDbJsonCfg(t *testing.T) { Db_user: utils.StringPointer(""), Db_passwd: utils.StringPointer(""), } - if cfg, err := cgrJsonCfg.DbJsonCfg(ACCOUNTINGDB_JSN); err != nil { + if cfg, err := dfCgrJsonCfg.DbJsonCfg(ACCOUNTINGDB_JSN); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) @@ -99,41 +101,41 @@ func TestDbJsonCfg(t *testing.T) { Max_open_conns: utils.IntPointer(0), Max_idle_conns: utils.IntPointer(-1), } - if cfg, err := cgrJsonCfg.DbJsonCfg(STORDB_JSN); err != nil { + if cfg, err := dfCgrJsonCfg.DbJsonCfg(STORDB_JSN); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestBalancerJsonCfg(t *testing.T) { +func TestDfBalancerJsonCfg(t *testing.T) { eCfg := &BalancerJsonCfg{Enabled: utils.BoolPointer(false)} - if cfg, err := cgrJsonCfg.BalancerJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.BalancerJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestRaterJsonCfg(t *testing.T) { +func TestDfRaterJsonCfg(t *testing.T) { eCfg := &RaterJsonCfg{Enabled: utils.BoolPointer(false), Balancer: utils.StringPointer("")} - if cfg, err := cgrJsonCfg.RaterJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.RaterJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestSchedulerJsonCfg(t *testing.T) { +func TestDfSchedulerJsonCfg(t *testing.T) { eCfg := &SchedulerJsonCfg{Enabled: utils.BoolPointer(false)} - if cfg, err := cgrJsonCfg.SchedulerJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.SchedulerJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestCdrsJsonCfg(t *testing.T) { +func TestDfCdrsJsonCfg(t *testing.T) { eCfg := &CdrsJsonCfg{ Enabled: utils.BoolPointer(false), Extra_fields: utils.StringSlicePointer([]string{}), @@ -141,14 +143,14 @@ func TestCdrsJsonCfg(t *testing.T) { Cdrstats: utils.StringPointer(""), Store_disable: utils.BoolPointer(false), } - if cfg, err := cgrJsonCfg.CdrsJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.CdrsJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestMediatorJsonCfg(t *testing.T) { +func TestDfMediatorJsonCfg(t *testing.T) { eCfg := &MediatorJsonCfg{ Enabled: utils.BoolPointer(false), Reconnects: utils.IntPointer(3), @@ -156,14 +158,14 @@ func TestMediatorJsonCfg(t *testing.T) { Cdrstats: utils.StringPointer(""), Store_disable: utils.BoolPointer(false), } - if cfg, err := cgrJsonCfg.MediatorJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.MediatorJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestCdrStatsJsonCfg(t *testing.T) { +func TestDfCdrStatsJsonCfg(t *testing.T) { eCfg := &CdrStatsJsonCfg{ Enabled: utils.BoolPointer(false), Queue_length: utils.IntPointer(50), @@ -184,85 +186,85 @@ func TestCdrStatsJsonCfg(t *testing.T) { Mediation_run_ids: utils.StringSlicePointer([]string{}), Rated_accounts: utils.StringSlicePointer([]string{}), Rated_subjects: utils.StringSlicePointer([]string{}), - Cost_intervals: utils.StringSlicePointer([]string{}), + Cost_interval: utils.Float64SlicePointer([]float64{}), } - if cfg, err := cgrJsonCfg.CdrStatsJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.CdrStatsJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestCdreJsonCfgs(t *testing.T) { +func TestDfCdreJsonCfgs(t *testing.T) { eFields := []*CdrFieldJsonCfg{} eContentFlds := []*CdrFieldJsonCfg{ &CdrFieldJsonCfg{Tag: utils.StringPointer("CgrId"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("cgrid"), - Width: utils.IntPointer(40), - Mandatory: utils.BoolPointer(true)}, + Cdr_field_id: utils.StringPointer("cgrid"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("cgrid")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("RunId"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("mediation_runid"), - Width: utils.IntPointer(20)}, + Cdr_field_id: utils.StringPointer("mediation_runid"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("mediation_runid")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Tor"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("tor"), - Width: utils.IntPointer(6)}, + Cdr_field_id: utils.StringPointer("tor"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("tor")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("AccId"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("accid"), - Width: utils.IntPointer(36)}, + Cdr_field_id: utils.StringPointer("accid"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("accid")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("ReqType"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("reqtype"), - Width: utils.IntPointer(13)}, + Cdr_field_id: utils.StringPointer("reqtype"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("reqtype")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Direction"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("direction"), - Width: utils.IntPointer(4)}, + Cdr_field_id: utils.StringPointer("direction"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("direction")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("tenant"), - Width: utils.IntPointer(24)}, + Cdr_field_id: utils.StringPointer("tenant"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("tenant")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Category"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("category"), - Width: utils.IntPointer(10)}, + Cdr_field_id: utils.StringPointer("category"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("category")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("account"), - Width: utils.IntPointer(24)}, + Cdr_field_id: utils.StringPointer("account"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("account")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("subject"), - Width: utils.IntPointer(24)}, + Cdr_field_id: utils.StringPointer("subject"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("subject")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("destination"), - Width: utils.IntPointer(24)}, + Cdr_field_id: utils.StringPointer("destination"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("destination")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("setup_time"), - Width: utils.IntPointer(30), - Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, + Cdr_field_id: utils.StringPointer("setup_time"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("setup_time"), + Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("AnswerTime"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("answer_time"), - Width: utils.IntPointer(30), - Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, + Cdr_field_id: utils.StringPointer("answer_time"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("answer_time"), + Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Usage"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("usage"), - Width: utils.IntPointer(30)}, + Cdr_field_id: utils.StringPointer("usage"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("usage")}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"), - Type: utils.StringPointer("cdrfield"), - Value: utils.StringPointer("cost"), - Width: utils.IntPointer(24)}, + Cdr_field_id: utils.StringPointer("cost"), + Type: utils.StringPointer("cdrfield"), + Value: utils.StringPointer("cost")}, } eCfg := map[string]*CdreJsonCfg{ - "CDRE-FW1": &CdreJsonCfg{ + utils.META_DEFAULT: &CdreJsonCfg{ Cdr_format: utils.StringPointer("csv"), + Field_separator: utils.StringPointer(","), Data_usage_multiply_factor: utils.Float64Pointer(1.0), Cost_multiply_factor: utils.Float64Pointer(1.0), Cost_rounding_decimals: utils.IntPointer(-1), @@ -275,31 +277,42 @@ func TestCdreJsonCfgs(t *testing.T) { Trailer_fields: &eFields, }, } - if cfg, err := cgrJsonCfg.CdreJsonCfgs(); err != nil { + if cfg, err := dfCgrJsonCfg.CdreJsonCfgs(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestCdrcJsonCfg(t *testing.T) { +func TestDfCdrcJsonCfg(t *testing.T) { cdrFields := []*CdrFieldJsonCfg{ - &CdrFieldJsonCfg{Tag: utils.StringPointer("accid"), Value: utils.StringPointer("0;13")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("reqtype"), Value: utils.StringPointer("1")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("direction"), Value: utils.StringPointer("2")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("tenant"), Value: utils.StringPointer("3")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("category"), Value: utils.StringPointer("4")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("account"), Value: utils.StringPointer("5")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("subject"), Value: utils.StringPointer("6")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("destination"), Value: utils.StringPointer("7")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("setup_time"), Value: utils.StringPointer("8")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("answer_time"), Value: utils.StringPointer("9")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("usage"), Value: utils.StringPointer("10")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("extr1"), Value: utils.StringPointer("11")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("extr2"), Value: utils.StringPointer("12")}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("tor"), Cdr_field_id: utils.StringPointer("tor"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("2"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("accid"), Cdr_field_id: utils.StringPointer("accid"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("3"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("reqtype"), Cdr_field_id: utils.StringPointer("reqtype"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("4"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("direction"), Cdr_field_id: utils.StringPointer("direction"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("5"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("tenant"), Cdr_field_id: utils.StringPointer("tenant"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("6"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("category"), Cdr_field_id: utils.StringPointer("category"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("7"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("account"), Cdr_field_id: utils.StringPointer("account"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("8"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("subject"), Cdr_field_id: utils.StringPointer("subject"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("9"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("destination"), Cdr_field_id: utils.StringPointer("destination"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("10"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("setup_time"), Cdr_field_id: utils.StringPointer("setup_time"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("11"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("answer_time"), Cdr_field_id: utils.StringPointer("answer_time"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("12"), Mandatory: utils.BoolPointer(true)}, + &CdrFieldJsonCfg{Tag: utils.StringPointer("usage"), Cdr_field_id: utils.StringPointer("usage"), Type: utils.StringPointer(utils.CDRFIELD), + Value: utils.StringPointer("13"), Mandatory: utils.BoolPointer(true)}, } eCfg := map[string]*CdrcJsonCfg{ - "instance1": &CdrcJsonCfg{ + "*default": &CdrcJsonCfg{ Enabled: utils.BoolPointer(false), Cdrs_address: utils.StringPointer("internal"), Cdr_format: utils.StringPointer("csv"), @@ -312,14 +325,14 @@ func TestCdrcJsonCfg(t *testing.T) { Cdr_fields: &cdrFields, }, } - if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.CdrcJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestSessionManagerJsonCfg(t *testing.T) { +func TestDfSessionManagerJsonCfg(t *testing.T) { eCfg := &SessionManagerJsonCfg{ Enabled: utils.BoolPointer(false), Switch_type: utils.StringPointer("freeswitch"), @@ -330,14 +343,14 @@ func TestSessionManagerJsonCfg(t *testing.T) { Min_call_duration: utils.StringPointer("0s"), Max_call_duration: utils.StringPointer("3h"), } - if cfg, err := cgrJsonCfg.SessionManagerJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.SessionManagerJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestFSJsonCfg(t *testing.T) { +func TestDfFSJsonCfg(t *testing.T) { eCfg := &FSJsonCfg{ Server: utils.StringPointer("127.0.0.1:8021"), Password: utils.StringPointer("ClueCon"), @@ -348,74 +361,117 @@ func TestFSJsonCfg(t *testing.T) { Empty_balance_ann_file: utils.StringPointer(""), Cdr_extra_fields: utils.StringSlicePointer([]string{}), } - if cfg, err := cgrJsonCfg.FSJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.FSJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestKamailioJsonCfg(t *testing.T) { +func TestDfKamailioJsonCfg(t *testing.T) { eCfg := &KamailioJsonCfg{ Evapi_addr: utils.StringPointer("127.0.0.1:8448"), Reconnects: utils.IntPointer(3), } - if cfg, err := cgrJsonCfg.KamailioJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.KamailioJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestOsipsJsonCfg(t *testing.T) { +func TestDfOsipsJsonCfg(t *testing.T) { eCfg := &OsipsJsonCfg{ Listen_udp: utils.StringPointer("127.0.0.1:2020"), Mi_addr: utils.StringPointer("127.0.0.1:8020"), Events_subscribe_interval: utils.StringPointer("60s"), Reconnects: utils.IntPointer(3), } - if cfg, err := cgrJsonCfg.OsipsJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.OsipsJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestHistServJsonCfg(t *testing.T) { +func TestDfHistServJsonCfg(t *testing.T) { eCfg := &HistServJsonCfg{ Enabled: utils.BoolPointer(false), History_dir: utils.StringPointer("/var/log/cgrates/history"), Save_interval: utils.StringPointer("1s"), } - if cfg, err := cgrJsonCfg.HistServJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.HistServJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestHistAgentJsonCfg(t *testing.T) { +func TestDfHistAgentJsonCfg(t *testing.T) { eCfg := &HistAgentJsonCfg{ Enabled: utils.BoolPointer(false), Server: utils.StringPointer("internal"), } - if cfg, err := cgrJsonCfg.HistAgentJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.HistAgentJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } -func TestMailerJsonCfg(t *testing.T) { +func TestDfMailerJsonCfg(t *testing.T) { eCfg := &MailerJsonCfg{ Server: utils.StringPointer("localhost"), Auth_user: utils.StringPointer("cgrates"), Auth_passwd: utils.StringPointer("CGRateS.org"), From_address: utils.StringPointer("cgr-mailer@localhost.localdomain"), } - if cfg, err := cgrJsonCfg.MailerJsonCfg(); err != nil { + if cfg, err := dfCgrJsonCfg.MailerJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { t.Error("Received: ", cfg) } } + +func TestNewCgrJsonCfgFromFile(t *testing.T) { + cgrJsonCfg, err := NewCgrJsonCfgFromFile("cfg_data.json") + if err != nil { + t.Error(err) + } + eCfg := &GeneralJsonCfg{Default_reqtype: utils.StringPointer("pseudoprepaid")} + if gCfg, err := cgrJsonCfg.GeneralJsonCfg(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eCfg, gCfg) { + t.Error("Received: ", gCfg) + } + cdrFields := []*CdrFieldJsonCfg{ + &CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("tor"), Value: utils.StringPointer("~7:s/^(voice|data|sms)$/*$1/")}, + &CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("answer_time"), Value: utils.StringPointer("1")}, + &CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("usage"), Value: utils.StringPointer(`~9:s/^(\d+)$/${1}s/`)}, + } + eCfgCdrc := map[string]*CdrcJsonCfg{ + "CDRC-CSV1": &CdrcJsonCfg{ + Enabled: utils.BoolPointer(true), + Cdr_in_dir: utils.StringPointer("/tmp/cgrates/cdrc1/in"), + Cdr_out_dir: utils.StringPointer("/tmp/cgrates/cdrc1/out"), + Cdr_source_id: utils.StringPointer("csv1"), + }, + "CDRC-CSV2": &CdrcJsonCfg{ + Enabled: utils.BoolPointer(true), + Cdr_in_dir: utils.StringPointer("/tmp/cgrates/cdrc2/in"), + Cdr_out_dir: utils.StringPointer("/tmp/cgrates/cdrc2/out"), + Cdr_source_id: utils.StringPointer("csv2"), + Cdr_fields: &cdrFields, + }, + } + if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eCfgCdrc, cfg) { + t.Error("Received: ", cfg) + } + if cfg, err := cgrJsonCfg.HistAgentJsonCfg(); err != nil { + t.Error(err) + } else if cfg != nil { + t.Error("Received: ", cfg) + } +} diff --git a/config/config_local_test.go b/config/config_local_test.go deleted file mode 100644 index 5f5fbd4ba..000000000 --- a/config/config_local_test.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2014 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "flag" - "path" - "testing" -) - -var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args -var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") - -func TestLoadXmlCfg(t *testing.T) { - if !*testLocal { - return - } - cfgPath := path.Join(*dataDir, "conf", "samples", "config_local_test.cfg") - cfg, err := NewCGRConfigFromFile(&cfgPath) - if err != nil { - t.Error(err) - } - if cfg.XmlCfgDocument == nil { - t.Error("Did not load the XML Config Document") - } - if cdreFWCfg := cfg.XmlCfgDocument.GetCdreCfgs("CDREFW-A"); cdreFWCfg == nil { - t.Error("Could not retrieve CDRExporter FixedWidth config instance") - } -} diff --git a/config/config_test.go b/config/config_test.go index 6bac80e34..369fd90e2 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -19,13 +19,8 @@ along with this program. If not, see package config import ( - "fmt" "reflect" - "regexp" "testing" - "time" - - "github.com/cgrates/cgrates/utils" ) func TestConfigSharing(t *testing.T) { @@ -36,338 +31,3 @@ func TestConfigSharing(t *testing.T) { t.Errorf("Retrieved %v, Expected %v", cfgReturn, cfg) } } - -// Make sure defaults did not change by mistake -func TestDefaults(t *testing.T) { - cfg := &CGRConfig{} - errSet := cfg.setDefaults() - if errSet != nil { - t.Log(fmt.Sprintf("Coud not set defaults: %s!", errSet.Error())) - t.FailNow() - } - eCfg := &CGRConfig{} - eCfg.RatingDBType = REDIS - eCfg.RatingDBHost = "127.0.0.1" - eCfg.RatingDBPort = "6379" - eCfg.RatingDBName = "10" - eCfg.RatingDBUser = "" - eCfg.RatingDBPass = "" - eCfg.AccountDBType = REDIS - eCfg.AccountDBHost = "127.0.0.1" - eCfg.AccountDBPort = "6379" - eCfg.AccountDBName = "11" - eCfg.AccountDBUser = "" - eCfg.AccountDBPass = "" - eCfg.StorDBType = utils.MYSQL - eCfg.StorDBHost = "localhost" - eCfg.StorDBPort = "3306" - eCfg.StorDBName = "cgrates" - eCfg.StorDBUser = "cgrates" - eCfg.StorDBPass = "CGRateS.org" - eCfg.StorDBMaxOpenConns = 100 - eCfg.StorDBMaxIdleConns = 10 - eCfg.DBDataEncoding = utils.MSGPACK - eCfg.RPCJSONListen = "127.0.0.1:2012" - eCfg.RPCGOBListen = "127.0.0.1:2013" - eCfg.HTTPListen = "127.0.0.1:2080" - eCfg.DefaultReqType = utils.RATED - eCfg.DefaultCategory = "call" - eCfg.DefaultTenant = "cgrates.org" - eCfg.DefaultSubject = "cgrates" - eCfg.RoundingDecimals = 10 - eCfg.HttpSkipTlsVerify = false - eCfg.TpExportPath = "/var/log/cgrates/tpe" - eCfg.XmlCfgDocument = nil - eCfg.RaterEnabled = false - eCfg.RaterBalancer = "" - eCfg.BalancerEnabled = false - eCfg.SchedulerEnabled = false - eCfg.CdreDefaultInstance = NewDefaultCdreConfig() - eCfg.CdrcInstances = []*CdrcConfig{NewDefaultCdrcConfig()} - eCfg.CDRSEnabled = false - eCfg.CDRSExtraFields = []*utils.RSRField{} - eCfg.CDRSMediator = "" - eCfg.CDRSStats = "" - eCfg.CDRSStoreDisable = false - eCfg.CDRStatsEnabled = false - eCfg.CDRStatConfig = &CdrStatsConfig{Id: utils.META_DEFAULT, QueueLength: 50, TimeWindow: time.Duration(1) * time.Hour, Metrics: []string{"ASR", "ACD", "ACC"}} - eCfg.MediatorEnabled = false - eCfg.MediatorRater = utils.INTERNAL - eCfg.MediatorReconnects = 3 - eCfg.MediatorStats = "" - eCfg.MediatorStoreDisable = false - eCfg.SMEnabled = false - eCfg.SMSwitchType = FS - eCfg.SMRater = utils.INTERNAL - eCfg.SMCdrS = "" - eCfg.SMReconnects = 3 - eCfg.SMDebitInterval = 10 - eCfg.SMMinCallDuration = time.Duration(0) - eCfg.SMMaxCallDuration = time.Duration(3) * time.Hour - eCfg.FreeswitchServer = "127.0.0.1:8021" - eCfg.FreeswitchPass = "ClueCon" - eCfg.FreeswitchReconnects = 5 - eCfg.FSMinDurLowBalance = time.Duration(5) * time.Second - eCfg.FSLowBalanceAnnFile = "" - eCfg.FSEmptyBalanceContext = "" - eCfg.FSEmptyBalanceAnnFile = "" - eCfg.FSCdrExtraFields = []*utils.RSRField{} - eCfg.OsipsListenUdp = "127.0.0.1:2020" - eCfg.OsipsMiAddr = "127.0.0.1:8020" - eCfg.OsipsEvSubscInterval = time.Duration(60) * time.Second - eCfg.OsipsReconnects = 3 - eCfg.KamailioEvApiAddr = "127.0.0.1:8448" - eCfg.KamailioReconnects = 3 - eCfg.DerivedChargers = make(utils.DerivedChargers, 0) - eCfg.CombinedDerivedChargers = true - eCfg.HistoryAgentEnabled = false - eCfg.HistoryServer = utils.INTERNAL - eCfg.HistoryServerEnabled = false - eCfg.HistoryDir = "/var/log/cgrates/history" - eCfg.HistorySaveInterval = time.Duration(1) * time.Second - eCfg.MailerServer = "localhost:25" - eCfg.MailerAuthUser = "cgrates" - eCfg.MailerAuthPass = "CGRateS.org" - eCfg.MailerFromAddr = "cgr-mailer@localhost.localdomain" - eCfg.DataFolderPath = "/usr/share/cgrates/" - if !reflect.DeepEqual(cfg, eCfg) { - t.Log(eCfg) - t.Log(cfg) - t.Error("Defaults different than expected!") - } - -} - -func TestSanityCheck(t *testing.T) { - cfg := &CGRConfig{} - errSet := cfg.setDefaults() - if errSet != nil { - t.Error("Coud not set defaults: ", errSet.Error()) - } - if err := cfg.checkConfigSanity(); err != nil { - t.Error("Invalid defaults: ", err) - } - cfg = &CGRConfig{} - cfg.CDRSStats = utils.INTERNAL - if err := cfg.checkConfigSanity(); err == nil { - t.Error("Failed detecting improper CDRStats configuration within CDRS") - } - cfg = &CGRConfig{} - cfg.MediatorStats = utils.INTERNAL - if err := cfg.checkConfigSanity(); err == nil { - t.Error("Failed detecting improper CDRStats configuration within Mediator") - } - cfg = &CGRConfig{} - cfg.SMCdrS = utils.INTERNAL - if err := cfg.checkConfigSanity(); err == nil { - t.Error("Failed detecting improper CDRS configuration within SM") - } -} - -// Load config from file and make sure we have all set -func TestConfigFromFile(t *testing.T) { - cfgPth := "test_data.txt" - cfg, err := NewCGRConfigFromFile(&cfgPth) - if err != nil { - t.Log(fmt.Sprintf("Could not parse config: %s!", err)) - t.FailNow() - } - eCfg := &CGRConfig{} // Instance we expect to get out after reading config file - eCfg.setDefaults() - eCfg.RatingDBType = "test" - eCfg.RatingDBHost = "test" - eCfg.RatingDBPort = "test" - eCfg.RatingDBName = "test" - eCfg.RatingDBUser = "test" - eCfg.RatingDBPass = "test" - eCfg.AccountDBType = "test" - eCfg.AccountDBHost = "test" - eCfg.AccountDBPort = "test" - eCfg.AccountDBName = "test" - eCfg.AccountDBUser = "test" - eCfg.AccountDBPass = "test" - eCfg.StorDBType = "test" - eCfg.StorDBHost = "test" - eCfg.StorDBPort = "test" - eCfg.StorDBName = "test" - eCfg.StorDBUser = "test" - eCfg.StorDBPass = "test" - eCfg.StorDBMaxOpenConns = 99 - eCfg.StorDBMaxIdleConns = 99 - eCfg.DBDataEncoding = "test" - eCfg.RPCJSONListen = "test" - eCfg.RPCGOBListen = "test" - eCfg.HTTPListen = "test" - eCfg.DefaultReqType = "test" - eCfg.DefaultCategory = "test" - eCfg.DefaultTenant = "test" - eCfg.DefaultSubject = "test" - eCfg.RoundingDecimals = 99 - eCfg.HttpSkipTlsVerify = true - eCfg.TpExportPath = "test" - eCfg.RaterEnabled = true - eCfg.RaterBalancer = "test" - eCfg.BalancerEnabled = true - eCfg.SchedulerEnabled = true - eCfg.CDRSEnabled = true - eCfg.CDRSExtraFields = []*utils.RSRField{&utils.RSRField{Id: "test"}} - eCfg.CDRSMediator = "test" - eCfg.CDRSStats = "test" - eCfg.CDRSStoreDisable = true - eCfg.CDRStatsEnabled = true - eCfg.CDRStatConfig = &CdrStatsConfig{Id: utils.META_DEFAULT, QueueLength: 99, TimeWindow: time.Duration(99) * time.Second, - Metrics: []string{"test"}, TORs: []string{"test"}, CdrHosts: []string{"test"}, CdrSources: []string{"test"}, ReqTypes: []string{"test"}, Directions: []string{"test"}, - Tenants: []string{"test"}, Categories: []string{"test"}, Accounts: []string{"test"}, Subjects: []string{"test"}, DestinationPrefixes: []string{"test"}, - UsageInterval: []time.Duration{time.Duration(99) * time.Second}, - MediationRunIds: []string{"test"}, RatedAccounts: []string{"test"}, RatedSubjects: []string{"test"}, CostInterval: []float64{99.0}} - eCfg.CDRSStats = "test" - eCfg.CdreDefaultInstance = &CdreConfig{ - CdrFormat: "test", - FieldSeparator: utils.CSV_SEP, - DataUsageMultiplyFactor: 99.0, - CostMultiplyFactor: 99.0, - CostRoundingDecimals: 99, - CostShiftDigits: 99, - MaskDestId: "test", - MaskLength: 99, - ExportDir: "test"} - eCfg.CdreDefaultInstance.ContentFields, _ = NewCfgCdrFieldsFromIds(false, "test") - cdrcCfg := NewDefaultCdrcConfig() - cdrcCfg.Enabled = true - cdrcCfg.CdrsAddress = "test" - cdrcCfg.RunDelay = time.Duration(99) * time.Second - cdrcCfg.CdrFormat = "test" - cdrcCfg.FieldSeparator = ";" - cdrcCfg.DataUsageMultiplyFactor = 99.0 - cdrcCfg.CdrInDir = "test" - cdrcCfg.CdrOutDir = "test" - cdrcCfg.CdrSourceId = "test" - cdrcCfg.CdrFields = []*CfgCdrField{ - &CfgCdrField{Tag: utils.TOR, Type: utils.CDRFIELD, CdrFieldId: utils.TOR, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.ACCID, Type: utils.CDRFIELD, CdrFieldId: utils.ACCID, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.REQTYPE, Type: utils.CDRFIELD, CdrFieldId: utils.REQTYPE, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.DIRECTION, Type: utils.CDRFIELD, CdrFieldId: utils.DIRECTION, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.TENANT, Type: utils.CDRFIELD, CdrFieldId: utils.TENANT, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.CATEGORY, Type: utils.CDRFIELD, CdrFieldId: utils.CATEGORY, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.ACCOUNT, Type: utils.CDRFIELD, CdrFieldId: utils.ACCOUNT, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.SUBJECT, Type: utils.CDRFIELD, CdrFieldId: utils.SUBJECT, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.DESTINATION, Type: utils.CDRFIELD, CdrFieldId: utils.DESTINATION, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: utils.SETUP_TIME, Type: utils.CDRFIELD, CdrFieldId: utils.SETUP_TIME, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true, Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: utils.ANSWER_TIME, Type: utils.CDRFIELD, CdrFieldId: utils.ANSWER_TIME, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true, Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: utils.USAGE, Type: utils.CDRFIELD, CdrFieldId: utils.USAGE, Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}, Mandatory: true}, - &CfgCdrField{Tag: "test", Type: utils.CDRFIELD, CdrFieldId: "test", Value: []*utils.RSRField{&utils.RSRField{Id: "test"}}}, - } - eCfg.CdrcInstances = []*CdrcConfig{cdrcCfg} - eCfg.MediatorEnabled = true - eCfg.MediatorRater = "test" - eCfg.MediatorReconnects = 99 - eCfg.MediatorStats = "test" - eCfg.MediatorStoreDisable = true - eCfg.SMEnabled = true - eCfg.SMSwitchType = "test" - eCfg.SMRater = "test" - eCfg.SMCdrS = "test" - eCfg.SMReconnects = 99 - eCfg.SMDebitInterval = 99 - eCfg.SMMinCallDuration = time.Duration(98) * time.Second - eCfg.SMMaxCallDuration = time.Duration(99) * time.Second - eCfg.FreeswitchServer = "test" - eCfg.FreeswitchPass = "test" - eCfg.FreeswitchReconnects = 99 - eCfg.FSMinDurLowBalance = time.Duration(99) * time.Second - eCfg.FSLowBalanceAnnFile = "test" - eCfg.FSEmptyBalanceContext = "test" - eCfg.FSEmptyBalanceAnnFile = "test" - eCfg.FSCdrExtraFields = []*utils.RSRField{&utils.RSRField{Id: "test"}} - eCfg.OsipsListenUdp = "test" - eCfg.OsipsMiAddr = "test" - eCfg.OsipsEvSubscInterval = time.Duration(99) * time.Second - eCfg.OsipsReconnects = 99 - eCfg.KamailioEvApiAddr = "test" - eCfg.KamailioReconnects = 99 - eCfg.DerivedChargers = utils.DerivedChargers{&utils.DerivedCharger{RunId: "test", RunFilters: "", ReqTypeField: "test", DirectionField: "test", TenantField: "test", - CategoryField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", UsageField: "test"}} - eCfg.CombinedDerivedChargers = true - eCfg.HistoryAgentEnabled = true - eCfg.HistoryServer = "test" - eCfg.HistoryServerEnabled = true - eCfg.HistoryDir = "test" - eCfg.HistorySaveInterval = time.Duration(99) * time.Second - eCfg.MailerServer = "test" - eCfg.MailerAuthUser = "test" - eCfg.MailerAuthPass = "test" - eCfg.MailerFromAddr = "test" - eCfg.DataFolderPath = "/usr/share/cgrates/" - if !reflect.DeepEqual(cfg, eCfg) { - t.Log(eCfg) - t.Log(cfg) - t.Error("Loading of configuration from file failed!") - } - -} - -func TestCdrsExtraFields(t *testing.T) { - eFieldsCfg := []byte(`[cdrs] -extra_fields = extr1,extr2 -`) - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.CDRSExtraFields, []*utils.RSRField{&utils.RSRField{Id: "extr1"}, &utils.RSRField{Id: "extr2"}}) { - t.Errorf("Unexpected value for CdrsExtraFields: %v", cfg.CDRSExtraFields) - } - eFieldsCfg = []byte(`[cdrs] -extra_fields = ~effective_caller_id_number:s/(\d+)/+$1/ -`) - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.CDRSExtraFields, []*utils.RSRField{&utils.RSRField{Id: "effective_caller_id_number", - RSRules: []*utils.ReSearchReplace{&utils.ReSearchReplace{SearchRegexp: regexp.MustCompile(`(\d+)`), ReplaceTemplate: "+$1"}}}}) { - t.Errorf("Unexpected value for config CdrsExtraFields: %v", cfg.CDRSExtraFields) - } - eFieldsCfg = []byte(`[cdrs] -extra_fields = extr1,~extr2:s/x.+/ -`) - if _, err := NewCGRConfigFromBytes(eFieldsCfg); err == nil { - t.Error("Failed to detect failed RSRParsing") - } - -} - -func TestCdreExtraFields(t *testing.T) { - eFieldsCfg := []byte(`[cdre] -cdr_format = csv -export_template = cgrid,mediation_runid,accid -`) - expectedFlds := []*CfgCdrField{ - &CfgCdrField{Tag: "cgrid", Type: utils.CDRFIELD, CdrFieldId: "cgrid", Value: []*utils.RSRField{&utils.RSRField{Id: "cgrid"}}, Mandatory: true}, - &CfgCdrField{Tag: "mediation_runid", Type: utils.CDRFIELD, CdrFieldId: "mediation_runid", Value: []*utils.RSRField{&utils.RSRField{Id: "mediation_runid"}}, Mandatory: true}, - &CfgCdrField{Tag: "accid", Type: utils.CDRFIELD, CdrFieldId: "accid", Value: []*utils.RSRField{&utils.RSRField{Id: "accid"}}, Mandatory: true}, - } - expCdreCfg := &CdreConfig{CdrFormat: utils.CSV, FieldSeparator: utils.CSV_SEP, CostRoundingDecimals: -1, ExportDir: "/var/log/cgrates/cdre", ContentFields: expectedFlds} - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.CdreDefaultInstance, expCdreCfg) { - t.Errorf("Expecting: %v, received: %v", expCdreCfg, cfg.CdreDefaultInstance) - } - eFieldsCfg = []byte(`[cdre] -cdr_format = csv -export_template = cgrid,~effective_caller_id_number:s/(\d+)/+$1/ -`) - rsrField, _ := utils.NewRSRField(`~effective_caller_id_number:s/(\d+)/+$1/`) - expectedFlds = []*CfgCdrField{ - &CfgCdrField{Tag: "cgrid", Type: utils.CDRFIELD, CdrFieldId: "cgrid", Value: []*utils.RSRField{&utils.RSRField{Id: "cgrid"}}, Mandatory: true}, - &CfgCdrField{Tag: "effective_caller_id_number", Type: utils.CDRFIELD, CdrFieldId: "effective_caller_id_number", Value: []*utils.RSRField{rsrField}, Mandatory: false}} - expCdreCfg.ContentFields = expectedFlds - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.CdreDefaultInstance, expCdreCfg) { - t.Errorf("Expecting: %v, received: %v", expCdreCfg, cfg.CdreDefaultInstance) - } - eFieldsCfg = []byte(`[cdre] -cdr_format = csv -export_template = cgrid,~accid:s/(\d)/$1,runid -`) - if _, err := NewCGRConfigFromBytes(eFieldsCfg); err == nil { - t.Error("Failed to detect failed RSRParsing") - } -} diff --git a/config/helpers.go b/config/helpers.go deleted file mode 100644 index baeb6e422..000000000 --- a/config/helpers.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "code.google.com/p/goconf/conf" - "errors" - "fmt" - "strings" - - "github.com/cgrates/cgrates/utils" -) - -// Adds support for slice values in config -func ConfigSlice(cfgVal string) ([]string, error) { - cfgValStrs := strings.Split(cfgVal, utils.FIELDS_SEP) // If need arrises, we can make the separator configurable - for idx, elm := range cfgValStrs { - cfgValStrs[idx] = strings.TrimSpace(elm) // By default spaces are not removed so we do it here to avoid unpredicted results in config - } - return cfgValStrs, nil -} - -// Parse the configuration file and returns utils.DerivedChargers instance if no errors -func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.DerivedChargers, err error) { - var runIds, runFilters, reqTypeFlds, directionFlds, tenantFlds, torFlds, acntFlds, subjFlds, dstFlds, sTimeFlds, aTimeFlds, durFlds []string - cfgVal, _ := c.GetString("derived_charging", "run_ids") - if runIds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "run_filters") - if runFilters, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "reqtype_fields") - if reqTypeFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "direction_fields") - if directionFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "tenant_fields") - if tenantFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "category_fields") - if torFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "account_fields") - if acntFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "subject_fields") - if subjFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "destination_fields") - if dstFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "setup_time_fields") - if sTimeFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "answer_time_fields") - if aTimeFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - cfgVal, _ = c.GetString("derived_charging", "usage_fields") - if durFlds, err = ConfigSlice(cfgVal); err != nil { - return nil, err - } - // We need all to be the same length - if len(runFilters) != len(runIds) || - len(reqTypeFlds) != len(runIds) || - len(directionFlds) != len(runIds) || - len(tenantFlds) != len(runIds) || - len(torFlds) != len(runIds) || - len(acntFlds) != len(runIds) || - len(subjFlds) != len(runIds) || - len(dstFlds) != len(runIds) || - len(sTimeFlds) != len(runIds) || - len(aTimeFlds) != len(runIds) || - len(durFlds) != len(runIds) { - return nil, errors.New(" Inconsistent fields length in derivated_charging section") - } - // Create the individual chargers and append them to the final instance - dcs = make(utils.DerivedChargers, 0) - if len(runIds) == 1 && len(runIds[0]) == 0 { // Avoid iterating on empty runid - return dcs, nil - } - for runIdx, runId := range runIds { - dc, err := utils.NewDerivedCharger(runId, runFilters[runIdx], reqTypeFlds[runIdx], directionFlds[runIdx], tenantFlds[runIdx], torFlds[runIdx], - acntFlds[runIdx], subjFlds[runIdx], dstFlds[runIdx], sTimeFlds[runIdx], aTimeFlds[runIdx], durFlds[runIdx]) - if err != nil { - return nil, err - } - if dcs, err = dcs.Append(dc); err != nil { - return nil, err - } - } - return dcs, nil -} - -func ParseCdrcCdrFields(torFld, accIdFld, reqtypeFld, directionFld, tenantFld, categoryFld, acntFld, subjectFld, destFld, - setupTimeFld, answerTimeFld, durFld, extraFlds string) (map[string][]*utils.RSRField, error) { - cdrcCdrFlds := make(map[string][]*utils.RSRField) - if len(extraFlds) != 0 { - if sepExtraFlds, err := ConfigSlice(extraFlds); err != nil { - return nil, err - } else { - for _, fldStr := range sepExtraFlds { - // extra fields defined as: : - if spltLbl := strings.Split(fldStr, utils.CONCATENATED_KEY_SEP); len(spltLbl) != 2 { - return nil, fmt.Errorf("Wrong format for cdrc.extra_fields: %s", fldStr) - } else { - if rsrFlds, err := utils.ParseRSRFields(spltLbl[1], utils.INFIELD_SEP); err != nil { - return nil, err - } else { - cdrcCdrFlds[spltLbl[0]] = rsrFlds - } - } - } - } - } - for fldTag, fldVal := range map[string]string{utils.TOR: torFld, utils.ACCID: accIdFld, utils.REQTYPE: reqtypeFld, utils.DIRECTION: directionFld, utils.TENANT: tenantFld, - utils.CATEGORY: categoryFld, utils.ACCOUNT: acntFld, utils.SUBJECT: subjectFld, utils.DESTINATION: destFld, utils.SETUP_TIME: setupTimeFld, - utils.ANSWER_TIME: answerTimeFld, utils.USAGE: durFld} { - if len(fldVal) != 0 { - if rsrFlds, err := utils.ParseRSRFields(fldVal, utils.INFIELD_SEP); err != nil { - return nil, err - } else { - cdrcCdrFlds[fldTag] = rsrFlds - } - } - } - return cdrcCdrFlds, nil -} diff --git a/config/helpers_test.go b/config/helpers_test.go deleted file mode 100644 index 8238bafa0..000000000 --- a/config/helpers_test.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/cgrates/cgrates/utils" -) - -func TestConfigSlice(t *testing.T) { - eCS := []string{"", ""} - if cs, err := ConfigSlice(" , "); err != nil { - t.Error("Unexpected error: ", err) - } else if !reflect.DeepEqual(eCS, cs) { - t.Errorf("Expecting: %v, received: %v", eCS, cs) - } -} - -func TestParseCfgDerivedCharging(t *testing.T) { - eFieldsCfg := []byte(`[derived_charging] -run_ids = run1, run2 -run_filters =, -reqtype_fields = test1, test2 -direction_fields = test1, test2 -tenant_fields = test1, test2 -category_fields = test1, test2 -account_fields = test1, test2 -subject_fields = test1, test2 -destination_fields = test1, test2 -setup_time_fields = test1, test2 -answer_time_fields = test1, test2 -usage_fields = test1, test2 -`) - edcs := utils.DerivedChargers{ - &utils.DerivedCharger{RunId: "run1", ReqTypeField: "test1", DirectionField: "test1", TenantField: "test1", CategoryField: "test1", - AccountField: "test1", SubjectField: "test1", DestinationField: "test1", SetupTimeField: "test1", AnswerTimeField: "test1", UsageField: "test1"}, - &utils.DerivedCharger{RunId: "run2", ReqTypeField: "test2", DirectionField: "test2", TenantField: "test2", CategoryField: "test2", - AccountField: "test2", SubjectField: "test2", DestinationField: "test2", SetupTimeField: "test2", AnswerTimeField: "test2", UsageField: "test2"}} - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.DerivedChargers, edcs) { - t.Errorf("Expecting: %v, received: %v", edcs, cfg.DerivedChargers) - } -} - -func TestParseCfgDerivedChargingDn1(t *testing.T) { - eFieldsCfg := []byte(`[derived_charging] -run_ids = run1, run2 -run_filters =~account:s/^\w+[mpls]\d{6}$//,~account:s/^0\d{9}$//;^account/value/ -reqtype_fields = test1, test2 -direction_fields = test1, test2 -tenant_fields = test1, test2 -category_fields = test1, test2 -account_fields = test1, test2 -subject_fields = test1, test2 -destination_fields = test1, test2 -setup_time_fields = test1, test2 -answer_time_fields = test1, test2 -usage_fields = test1, test2 -`) - eDcs := make(utils.DerivedChargers, 2) - if dc, err := utils.NewDerivedCharger("run1", `~account:s/^\w+[mpls]\d{6}$//`, "test1", "test1", "test1", - "test1", "test1", "test1", "test1", "test1", "test1", "test1"); err != nil { - t.Error("Unexpected error: ", err) - } else { - eDcs[0] = dc - } - if dc, err := utils.NewDerivedCharger("run2", `~account:s/^0\d{9}$//;^account/value/`, "test2", "test2", "test2", - "test2", "test2", "test2", "test2", "test2", "test2", "test2"); err != nil { - t.Error("Unexpected error: ", err) - } else { - eDcs[1] = dc - } - - if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(cfg.DerivedChargers, eDcs) { - dcsJson, _ := json.Marshal(cfg.DerivedChargers) - t.Errorf("Received: %s", string(dcsJson)) - } -} - -func TestParseCdrcCdrFields(t *testing.T) { - eCdrcCdrFlds := map[string][]*utils.RSRField{ - utils.TOR: []*utils.RSRField{&utils.RSRField{Id: "tor1"}}, - utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "accid1"}}, - utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "reqtype1"}}, - utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "direction1"}}, - utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "tenant1"}}, - utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "category1"}}, - utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "account1"}}, - utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "subject1"}}, - utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "destination1"}}, - utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "setuptime1"}}, - utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "answertime1"}}, - utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "duration1"}}, - "extra1": []*utils.RSRField{&utils.RSRField{Id: "extraval1"}}, - "extra2": []*utils.RSRField{&utils.RSRField{Id: "extraval1"}}, - } - if cdrFlds, err := ParseCdrcCdrFields("tor1", "accid1", "reqtype1", "direction1", "tenant1", "category1", "account1", "subject1", "destination1", - "setuptime1", "answertime1", "duration1", "extra1:extraval1,extra2:extraval1"); err != nil { - t.Error("Could not parse the config", err.Error()) - } else if !reflect.DeepEqual(eCdrcCdrFlds, cdrFlds) { - t.Errorf("Expecting: %v, received: %v, tor: %v", eCdrcCdrFlds, cdrFlds, cdrFlds[utils.TOR]) - } -} diff --git a/config/libconfig_json.go b/config/libconfig_json.go index aecb13696..24e37067a 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -104,7 +104,7 @@ type CdrStatsJsonCfg struct { Mediation_run_ids *[]string Rated_accounts *[]string Rated_subjects *[]string - Cost_intervals *[]string + Cost_interval *[]float64 } // One cdr field config, used in cdre and cdrc @@ -124,6 +124,7 @@ type CdrFieldJsonCfg struct { // Cdre config section type CdreJsonCfg struct { Cdr_format *string + Field_separator *string Data_usage_multiply_factor *float64 Cost_multiply_factor *float64 Cost_rounding_decimals *int diff --git a/config/test_data.txt b/config/test_data.txt deleted file mode 100644 index 9c6a73d75..000000000 --- a/config/test_data.txt +++ /dev/null @@ -1,177 +0,0 @@ -# TEST DATA - NOT FOR PRODUCTION USAGE -# - -[global] -ratingdb_type = test # Rating subsystem database: . -ratingdb_host = test # Rating subsystem database host address. -ratingdb_port = test # Rating subsystem port to reach the database. -ratingdb_name = test # Rating subsystem database name to connect to. -ratingdb_user = test # Rating subsystem username to use when connecting to database. -ratingdb_passwd = test # Rating subsystem password to use when connecting to database. -accountdb_type = test # Accounting subsystem database: . -accountdb_host = test # Accounting subsystem database host address. -accountdb_port = test # Accounting subsystem port to reach the database. -accountdb_name = test # Accounting subsystem database name to connect to. -accountdb_user = test # Accounting subsystem username to use when connecting to database. -accountdb_passwd = test # Accounting subsystem password to use when connecting to database. -stordb_type = test # Log/scategoryed database type to use: -stordb_host = test # The host to connect to. Values that start with / are for UNIX domain sockets. -stordb_port = test # The port to reach the logdb. -stordb_name = test # The name of the log database to connect to. -stordb_user = test # Username to use when connecting to logdb. -stordb_passwd = test # Password to use when connecting to logdb. -stordb_max_open_conns = 99 # Maximum database connections opened -stordb_max_idle_conns = 99 # Maximum database connections idle -dbdata_encoding = test # The encoding used to scategorye object data in strings: -rpc_json_listen = test # RPC JSON listening address -rpc_gob_listen = test # RPC GOB listening address -http_listen = test # HTTP listening address -default_reqtype = test # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>. -default_category = test # Default Type of Record to consider when missing from requests. -default_tenant = test # Default Tenant to consider when missing from requests. -default_subject = test # Default rating Subject to consider when missing from requests. -rounding_decimals = 99 # Number of decimals to round floats/costs at -http_skip_tls_veify = true # If enabled Http Client will accept any TLS certificate -tpexport_dir = test # Path towards export folder for offline Tariff Plans - -[balancer] -enabled = true # Start Balancer service: . - -[rater] -enabled = true # Enable Rater service: . -balancer = test # Register to Balancer as worker: . - -[scheduler] -enabled = true # Starts Scheduler service: . - -[cdrs] -enabled = true # Start the CDR Server service: . -extra_fields = test # Extra fields to scategorye in CDRs -mediator = test # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> -cdrstats = test # Address where to reach the CDRStats server. Empty for disabling stats. <""|internal> -store_disable = true # When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - -[cdre] -cdr_format = test # Exported CDRs format -data_usage_multiply_factor = 99.0 # Multiply data usage before export (eg: convert from KBytes to Bytes) -cost_multiply_factor = 99.0 # Multiply cost before export (0.0 to disable), eg: add VAT -cost_rounding_decimals = 99 # Rounding decimals for Cost values. -1 to disable rounding -cost_shift_digits = 99 # Shift digits in the cost on export (eg: convert from EUR to cents) -mask_destination_id = test # Destination id containing called addresses to be masked on export -mask_length = 99 # Length of the destination suffix to be masked -export_dir = test # Path where the exported CDRs will be placed -export_template = test # List of fields in the exported CDRs - -[cdrc] -enabled = true # Enable CDR client functionality -cdrs = test # Address where to reach CDR server -run_delay = 99 # Period to sleep between two runs, 0 to use automation via inotify -cdr_format = test # CDR file format . -field_separator =; # Csv separator, one character only and should be next to equal sign -data_usage_multiply_factor = 99 -cdr_in_dir = test # Absolute path towards the direccategoryy where the CDRs are kept (file scategoryed CDRs). -cdr_out_dir = test # Absolute path towards the direccategoryy where processed CDRs will be moved after processing. -cdr_source_id = test # Tag identifying the source of the CDRs within CGRS database. -tor_field = test # TypeOfRecord field identifier. Use index number in case of .csv cdrs. -accid_field = test # Accounting id field identifier. Use index number in case of .csv cdrs. -reqtype_field = test # Request type field identifier. Use index number in case of .csv cdrs. -direction_field = test # Direction field identifier. Use index numbers in case of .csv cdrs. -tenant_field = test # Tenant field identifier. Use index numbers in case of .csv cdrs. -category_field = test # Type of Record field identifier. Use index numbers in case of .csv cdrs. -account_field = test # Account field identifier. Use index numbers in case of .csv cdrs. -subject_field = test # Subject field identifier. Use index numbers in case of .csv CDRs. -destination_field = test # Destination field identifier. Use index numbers in case of .csv cdrs. -setup_time_field = test # Answer time field identifier. Use index numbers in case of .csv cdrs. -answer_time_field = test # Answer time field identifier. Use index numbers in case of .csv cdrs. -usage_field = test # Duration field identifier. Use index numbers in case of .csv cdrs. -extra_fields = test:test # Field identifiers of the fields to add in extra fields section, special format in case of .csv "index1|field1,index2|field2" - -[mediator] -enabled = true # Starts Mediacategory service: . -rater = test # Address where to reach the Rater: -reconnects = 99 # Number of reconnects to rater before giving up. -cdrstats = test # Address where to reach the cdrstats service: -store_disable = true # When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - -[cdrstats] -enabled = true # Start the CDR stats service: . -queue_length = 99 # Number of items in the stats buffer -time_window = 99 # Will only keep the CDRs who's call setup time is not older than time.Now()-TimeWindow -metrics = test # Stat metric ids to build -setup_interval = # Filter on CDR SetupTime -tors = test # Filter on CDR TOR fields -cdr_hosts= test # Filter on CDR CdrHost fields -cdr_sources = test # Filter on CDR CdrSource fields -req_types = test # Filter on CDR ReqType fields -directions = test # Filter on CDR Direction fields -tenants = test # Filter on CDR Tenant fields -categories = test # Filter on CDR Category fields -accounts = test # Filter on CDR Account fields -subjects = test # Filter on CDR Subject fields -destination_prefixes = test # Filter on CDR Destination prefixes -usage_interval = 99 # Filter on CDR Usage -mediation_run_ids = test # Filter on CDR MediationRunId fields -rated_accounts = test # Filter on CDR RatedAccount fields -rated_subjects = test # Filter on CDR RatedSubject fields -cost_intervals = 99 # Filter on CDR Cost - -[session_manager] -enabled = true # Starts SessionManager service: . -switch_type = test # Defines the type of switch behind: . -rater = test # Address where to reach the Rater. -cdrs = test # Address where to reach CDR Server, empty to disable CDR capturing <""|internal|127.0.0.1:2013> -reconnects = 99 # Number of reconnects to rater before giving up. -debit_interval = 99 # Interval to perform debits on. -min_call_duration = 98 # Only authorize calls with allowed duration bigger than this -max_call_duration = 99 # Maximum call duration a prepaid call can last - -[freeswitch] -server = test # Adress where to connect to FreeSWITCH socket. -passwd = test # FreeSWITCH socket password. -reconnects = 99 # Number of attempts on connect failure. -min_dur_low_balance = 99 # Threshold which will trigger low balance warnings -low_balance_ann_file = test # File to be played when low balance is reached -empty_balance_context = test # If defined, call will be transfered to this context on empty balance -empty_balance_ann_file = test # File to be played before disconnecting prepaid calls (applies only if no context defined) -cdr_extra_fields = test # Extra fields to store in CDRs in case of processing them - -[kamailio] -evapi_addr = test -reconnects = 99 # Number of attempts on connect failure. - -[opensips] -listen_udp = test # Address where to listen for event datagrams coming from OpenSIPS -mi_addr = test # Adress where to reach OpenSIPS mi_datagram module -events_subscribe_interval = 99 # Automatic events subscription to OpenSIPS, 0 to disable it -cdrs = test # Address where to reach CDR Server, empty to disable CDR processing <""|internal|127.0.0.1:2013> -reconnects = 99 # Number of attempts on connect failure. - -[derived_charging] -run_ids = test # Identifiers of additional sessions control. -run_filters = # No filters applied -reqtype_fields = test # Name of request type fields to be used during additional sessions control <""|*default|field_name>. -direction_fields = test # Name of direction fields to be used during additional sessions control <""|*default|field_name>. -tenant_fields = test # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. -category_fields = test # Name of category fields to be used during additional sessions control <""|*default|field_name>. -account_fields = test # Name of account fields to be used during additional sessions control <""|*default|field_name>. -subject_fields = test # Name of fields to be used during additional sessions control <""|*default|field_name>. -destination_fields = test # Name of destination fields to be used during additional sessions control <""|*default|field_name>. -setup_time_fields = test # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. -answer_time_fields = test # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. -usage_fields = test # Name of duration fields to be used during additional sessions control <""|*default|field_name>. -combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . - -[history_server] -enabled = true # Starts Hiscategoryy service: . -history_dir = test # Location on disk where to scategorye hiscategoryy files. -save_interval = 99 # Timeout duration between saves - -[history_agent] -enabled = true # Starts Hiscategoryy as a client: . -server = test # Address where to reach the master hiscategoryy server: - -[mailer] -server = test # The server to use when sending emails out -auth_user = test # Authenticate to email server using this user -auth_passwd = test # Authenticate to email server with this password -from_address = test # From address used when sending emails out diff --git a/config/xmlcdrc_test.go b/config/xmlcdrc_test.go deleted file mode 100644 index d34df847c..000000000 --- a/config/xmlcdrc_test.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "encoding/xml" - "github.com/cgrates/cgrates/utils" - "reflect" - "strings" - "testing" -) - -var cfgDocCdrc *CgrXmlCfgDocument // Will be populated by first test - -/* -func TestSetDefaults(t *testing.T) { - cfgXmlStr := ` - - - true - -` - var xmlCdrc *CgrXmlCdrcCfg - reader := strings.NewReader(cfgXmlStr) - if cfgDocCdrcDf, err := ParseCgrXmlConfig(reader); err != nil { - t.Error(err.Error()) - } else if cfgDocCdrcDf == nil { - t.Fatal("Could not parse xml configuration document") - } else if len(cfgDocCdrcDf.cdrcs) != 1 { - t.Error("Did not load cdrc") - } else { - xmlCdrc = cfgDocCdrcDf.cdrcs["CDRC-CSVDF"] - } - dfCfg, _ := NewDefaultCGRConfig() - xmlCdrc.setDefaults() - if xmlCdrc.CdrsAddress != dfCfg.CdrcCdrs || - xmlCdrc.CdrFormat != dfCfg.CdrcCdrType || - xmlCdrc.CsvSeparator != dfCfg.CdrcCsvSep || - xmlCdrc.CdrInDir != dfCfg.CdrcCdrInDir || - xmlCdrc.CdrOutDir != dfCfg.CdrcCdrOutDir || - xmlCdrc.CdrSourceId != dfCfg.CdrcSourceId || - len(xmlCdrc.CdrFields) != len(dfCfg.CdrcCdrFields) { - t.Error("Failed loading default configuration") - } -} -*/ - -func TestParseXmlCdrcConfig(t *testing.T) { - cfgXmlStr := ` - - - true - internal - csv - , - 1024 - 0 - /var/log/cgrates/cdrc/in - /var/log/cgrates/cdrc/out - freeswitch_csv - - - - - - - - - - - - - - - - -` - var err error - reader := strings.NewReader(cfgXmlStr) - if cfgDocCdrc, err = ParseCgrXmlConfig(reader); err != nil { - t.Error(err.Error()) - } else if cfgDocCdrc == nil { - t.Fatal("Could not parse xml configuration document") - } - if len(cfgDocCdrc.cdrcs) != 1 { - t.Error("Did not cache") - } -} - -func TestGetCdrcCfgs(t *testing.T) { - cdrcfgs := cfgDocCdrc.GetCdrcCfgs("CDRC-CSV1") - if cdrcfgs == nil { - t.Error("No config instance returned") - } - enabled := true - cdrsAddr := "internal" - cdrFormat := "csv" - fldSep := "," - dataUsageMultiplyFactor := 1024.0 - runDelay := int64(0) - cdrInDir := "/var/log/cgrates/cdrc/in" - cdrOutDir := "/var/log/cgrates/cdrc/out" - cdrSrcId := "freeswitch_csv" - expectCdrc := &CgrXmlCdrcCfg{Enabled: &enabled, CdrsAddress: &cdrsAddr, CdrFormat: &cdrFormat, FieldSeparator: &fldSep, DataUsageMultiplyFactor: &dataUsageMultiplyFactor, - RunDelay: &runDelay, CdrInDir: &cdrInDir, CdrOutDir: &cdrOutDir, CdrSourceId: &cdrSrcId} - accIdTag, reqTypeTag, dirTag, tntTag, categTag, acntTag, subjTag, dstTag, sTimeTag, aTimeTag, usageTag, extr1, extr2 := utils.ACCID, - utils.REQTYPE, utils.DIRECTION, utils.TENANT, utils.CATEGORY, utils.ACCOUNT, utils.SUBJECT, utils.DESTINATION, utils.SETUP_TIME, utils.ANSWER_TIME, utils.USAGE, "extr1", "extr2" - accIdVal, reqVal, dirVal, tntVal, categVal, acntVal, subjVal, dstVal, sTimeVal, aTimeVal, usageVal, extr1Val, extr2Val := "0;13", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" - cdrFlds := []*XmlCfgCdrField{ - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &accIdTag, Value: &accIdVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &reqTypeTag, Value: &reqVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &dirTag, Value: &dirVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &tntTag, Value: &tntVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &categTag, Value: &categVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &acntTag, Value: &acntVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &subjTag, Value: &subjVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &dstTag, Value: &dstVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &sTimeTag, Value: &sTimeVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &aTimeTag, Value: &aTimeVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &usageTag, Value: &usageVal}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &extr1, Value: &extr1Val}, - &XmlCfgCdrField{XMLName: xml.Name{Local: "field"}, Tag: &extr2, Value: &extr2Val}} - expectCdrc.CdrFields = cdrFlds - if !reflect.DeepEqual(expectCdrc, cdrcfgs["CDRC-CSV1"]) { - t.Errorf("Expecting: %v, received: %v", expectCdrc, cdrcfgs["CDRC-CSV1"]) - } -} diff --git a/config/xmlcdre_test.go b/config/xmlcdre_test.go deleted file mode 100644 index 298467af5..000000000 --- a/config/xmlcdre_test.go +++ /dev/null @@ -1,300 +0,0 @@ -/* -Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "github.com/cgrates/cgrates/utils" - "reflect" - "strings" - "testing" -) - -var cfgDoc *CgrXmlCfgDocument // Will be populated by first test - -func TestXmlCdreCfgParseXmlConfig(t *testing.T) { - cfgXmlStr := ` - - - fwv - 1.0 - 0.0 - -1 - 0 - MASKED_DESTINATIONS - 0 - /var/log/cgrates/cdre - -
- - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - -
` - var err error - reader := strings.NewReader(cfgXmlStr) - if cfgDoc, err = ParseCgrXmlConfig(reader); err != nil { - t.Error(err.Error()) - } else if cfgDoc == nil { - t.Fatal("Could not parse xml configuration document") - } - if len(cfgDoc.cdres) != 2 { - t.Error("Did not cache") - } -} - -func TestXmlCdreCfgGetCdreCfg(t *testing.T) { - cdreFWCfg := cfgDoc.GetCdreCfgs("CDRE-FW1") - if cdreFWCfg == nil { - t.Error("Could not parse CdreFw instance") - } - if len(cdreFWCfg["CDRE-FW1"].Header.Fields) != 8 { - t.Error("Unexpected number of header fields parsed", len(cdreFWCfg["CDRE-FW1"].Header.Fields)) - } - if len(cdreFWCfg["CDRE-FW1"].Content.Fields) != 20 { - t.Error("Unexpected number of content fields parsed", len(cdreFWCfg["CDRE-FW1"].Content.Fields)) - } - if len(cdreFWCfg["CDRE-FW1"].Trailer.Fields) != 9 { - t.Error("Unexpected number of trailer fields parsed", len(cdreFWCfg["CDRE-FW1"].Trailer.Fields)) - } - cdreCsvCfg1 := cfgDoc.GetCdreCfgs("CHECK-CSV1") - if cdreCsvCfg1 == nil { - t.Error("Could not parse CdreFw instance") - } - if len(cdreCsvCfg1["CHECK-CSV1"].Content.Fields) != 6 { - t.Error("Unexpected number of content fields parsed", len(cdreCsvCfg1["CHECK-CSV1"].Content.Fields)) - } -} - -func TestNewCdreConfigFromXmlCdreCfg(t *testing.T) { - cfgXmlStr := ` - - - fwv - ; - 1024.0 - 1.19 - -1 - -3 - MASKED_DESTINATIONS - 1 - /var/log/cgrates/cdre - -
- - - - -
- - - - - - - - - - - - - - - -
-
-
` - var err error - reader := strings.NewReader(cfgXmlStr) - if cfgDoc, err = ParseCgrXmlConfig(reader); err != nil { - t.Error(err.Error()) - } else if cfgDoc == nil { - t.Fatal("Could not parse xml configuration document") - } - xmlCdreCfgs := cfgDoc.GetCdreCfgs("CDRE-FW2") - if xmlCdreCfgs == nil { - t.Error("Could not parse XmlCdre instance") - } - eCdreCfg := &CdreConfig{ - CdrFormat: "fwv", - FieldSeparator: ';', - DataUsageMultiplyFactor: 1024.0, - CostMultiplyFactor: 1.19, - CostRoundingDecimals: -1, - CostShiftDigits: -3, - MaskDestId: "MASKED_DESTINATIONS", - MaskLength: 1, - ExportDir: "/var/log/cgrates/cdre", - } - fltrCombiMed, _ := utils.ParseRSRFields("~mediation_runid:s/DEFAULT/SECOND_RUN/", utils.INFIELD_SEP) - torVal, _ := utils.ParseRSRFields("^10", utils.INFIELD_SEP) - lastCdrVal, _ := utils.ParseRSRFields("^last_cdr_time", utils.INFIELD_SEP) - eCdreCfg.HeaderFields = []*CfgCdrField{ - &CfgCdrField{ - Tag: "TypeOfRecord", - Type: "constant", - Value: torVal, - Width: 2, - }, - &CfgCdrField{ - Tag: "LastCdr", - Type: "metatag", - CdrFieldId: "last_cdr_time", - Value: lastCdrVal, - Layout: "020106150400", - Strip: "xright", - Padding: "left", - Width: 12, - }, - } - networkIdVal, _ := utils.ParseRSRFields("^3", utils.INFIELD_SEP) - fromHttpPost1Val, _ := utils.ParseRSRFields("^https://localhost:8000", utils.INFIELD_SEP) - eCdreCfg.ContentFields = []*CfgCdrField{ - &CfgCdrField{ - Tag: "OperatorCode", - Type: "cdrfield", - CdrFieldId: "operator", - Value: []*utils.RSRField{ - &utils.RSRField{Id: "operator"}}, - Width: 2, - Strip: "xright", - Padding: "left", - }, - &CfgCdrField{ - Tag: "ProductId", - Type: "cdrfield", - CdrFieldId: "productid", - Value: []*utils.RSRField{ - &utils.RSRField{Id: "productid"}}, - Width: 5, - Strip: "xright", - Padding: "left", - }, - &CfgCdrField{ - Tag: "NetworkId", - Type: "constant", - Value: networkIdVal, - Width: 1, - }, - &CfgCdrField{ - Tag: "FromHttpPost1", - Type: "http_post", - Value: fromHttpPost1Val, - Width: 10, - Strip: "xright", - Padding: "left", - }, - &CfgCdrField{ - Tag: "CombiMed1", - Type: "combimed", - CdrFieldId: "cost", - Value: []*utils.RSRField{ - &utils.RSRField{Id: "cost"}}, - Width: 10, - Strip: "xright", - Padding: "left", - Filter: fltrCombiMed, - Mandatory: true, - }, - } - distribCodeVal, _ := utils.ParseRSRFields("^VOI", utils.INFIELD_SEP) - fileSeqNrVal, _ := utils.ParseRSRFields("^export_id", utils.INFIELD_SEP) - eCdreCfg.TrailerFields = []*CfgCdrField{ - &CfgCdrField{ - Tag: "DistributorCode", - Type: "constant", - Value: distribCodeVal, - Width: 3, - }, - &CfgCdrField{ - Tag: "FileSeqNr", - Type: "metatag", - CdrFieldId: "export_id", - Value: fileSeqNrVal, - Width: 5, - Strip: "xright", - Padding: "zeroleft", - }, - } - if rcvCdreCfg, err := NewCdreConfigFromXmlCdreCfg(xmlCdreCfgs["CDRE-FW2"]); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(rcvCdreCfg, eCdreCfg) { - t.Errorf("Expecting: %v, received: %v", eCdreCfg, rcvCdreCfg) - } -} diff --git a/config/xmlconfig.go b/config/xmlconfig.go deleted file mode 100644 index a6433068b..000000000 --- a/config/xmlconfig.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package config - -import ( - "encoding/xml" - "fmt" - "github.com/cgrates/cgrates/utils" - "io" -) - -// Decodes a reader enforcing specific format of the configuration file -func ParseCgrXmlConfig(reader io.Reader) (*CgrXmlCfgDocument, error) { - xmlConfig := new(CgrXmlCfgDocument) - decoder := xml.NewDecoder(reader) - if err := decoder.Decode(xmlConfig); err != nil { - return nil, err - } - if err := xmlConfig.cacheAll(); err != nil { - return nil, err - } - return xmlConfig, nil -} - -// XML CDR field, used for both cdrc and cdre -type XmlCfgCdrField struct { - XMLName xml.Name `xml:"field"` - Tag *string `xml:"tag,attr"` - Type *string `xml:"type,attr"` - CdrFieldId *string `xml:"cdr_field,attr"` - Value *string `xml:"value,attr"` - Width *int `xml:"width,attr"` // Field width - Strip *string `xml:"strip,attr"` // Strip strategy in case value is bigger than field width <""|left|xleft|right|xright> - Padding *string `xml:"padding,attr"` // Padding strategy in case of value is smaller than width <""left|zeroleft|right> - Layout *string `xml:"layout,attr"` // Eg. time format layout - Filter *string `xml:"filter,attr"` // Eg. combimed filters - Mandatory *bool `xml:"mandatory,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported -} - -// One CDRC Configuration instance -type CgrXmlCdrcCfg struct { - Enabled *bool `xml:"enabled"` // Enable/Disable the - CdrsAddress *string `xml:"cdrs_address"` // The address where CDRs can be reached - CdrFormat *string `xml:"cdr_format"` // The type of CDR to process - FieldSeparator *string `xml:"field_separator"` // The separator to use when reading csvs - DataUsageMultiplyFactor *float64 `xml:"data_usage_multiply_factor"` // Conversion factor for data usage - RunDelay *int64 `xml:"run_delay"` // Delay between runs - CdrInDir *string `xml:"cdr_in_dir"` // Folder to process CDRs from - CdrOutDir *string `xml:"cdr_out_dir"` // Folder to move processed CDRs to - CdrSourceId *string `xml:"cdr_source_id"` // Source identifier for the processed CDRs - CdrFields []*XmlCfgCdrField `xml:"fields>field"` -} - -// The CdrExporter configuration instance -type CgrXmlCdreCfg struct { - CdrFormat *string `xml:"cdr_format"` - FieldSeparator *string `xml:"field_separator"` - DataUsageMultiplyFactor *float64 `xml:"data_usage_multiply_factor"` - CostMultiplyFactor *float64 `xml:"cost_multiply_factor"` - CostRoundingDecimals *int `xml:"cost_rounding_decimals"` - CostShiftDigits *int `xml:"cost_shift_digits"` - MaskDestId *string `xml:"mask_destination_id"` - MaskLength *int `xml:"mask_length"` - ExportDir *string `xml:"export_dir"` - Header *CgrXmlCfgCdrHeader `xml:"export_template>header"` - Content *CgrXmlCfgCdrContent `xml:"export_template>content"` - Trailer *CgrXmlCfgCdrTrailer `xml:"export_template>trailer"` -} - -// CDR header -type CgrXmlCfgCdrHeader struct { - XMLName xml.Name `xml:"header"` - Fields []*XmlCfgCdrField `xml:"fields>field"` -} - -// CDR content -type CgrXmlCfgCdrContent struct { - XMLName xml.Name `xml:"content"` - Fields []*XmlCfgCdrField `xml:"fields>field"` -} - -// CDR trailer -type CgrXmlCfgCdrTrailer struct { - XMLName xml.Name `xml:"trailer"` - Fields []*XmlCfgCdrField `xml:"fields>field"` -} - -// Define a format for configuration file, one doc contains more configuration instances, identified by section, type and id -type CgrXmlCfgDocument struct { - XMLName xml.Name `xml:"document"` - Type string `xml:"type,attr"` - Configurations []*CgrXmlConfiguration `xml:"configuration"` - cdrcs map[string]*CgrXmlCdrcCfg - cdres map[string]*CgrXmlCdreCfg // Cahe cdrexporter instances, key will be the ID -} - -// Storage for raw configuration -type CgrXmlConfiguration struct { - XMLName xml.Name `xml:"configuration"` - Section string `xml:"section,attr"` - Id string `xml:"id,attr"` - RawConfig []byte `xml:",innerxml"` // Used to store the configuration struct, as raw so we can store different types -} - -func (cfgInst *CgrXmlConfiguration) rawConfigElement() []byte { - rawConfig := append([]byte(""), cfgInst.RawConfig...) // Encapsulate the rawConfig in one element so we can Unmarshall into one struct - rawConfig = append(rawConfig, []byte("")...) - return rawConfig -} - -func (xmlCfg *CgrXmlCfgDocument) cacheAll() error { - for _, cacheFunc := range []func() error{xmlCfg.cacheCdrcCfgs, xmlCfg.cacheCdreCfgs} { - if err := cacheFunc(); err != nil { - return err - } - } - return nil -} - -// Avoid building from raw config string always, so build cache here -func (xmlCfg *CgrXmlCfgDocument) cacheCdrcCfgs() error { - xmlCfg.cdrcs = make(map[string]*CgrXmlCdrcCfg) - for _, cfgInst := range xmlCfg.Configurations { - if cfgInst.Section != utils.CDRC { - continue // Another type of config instance, not interesting to process - } - cdrcCfg := new(CgrXmlCdrcCfg) - if err := xml.Unmarshal(cfgInst.rawConfigElement(), cdrcCfg); err != nil { - return err - } else if cdrcCfg == nil { - return fmt.Errorf("Could not unmarshal config instance: %s", cfgInst.Id) - } - xmlCfg.cdrcs[cfgInst.Id] = cdrcCfg - } - return nil -} - -// Avoid building from raw config string always, so build cache here -func (xmlCfg *CgrXmlCfgDocument) cacheCdreCfgs() error { - xmlCfg.cdres = make(map[string]*CgrXmlCdreCfg) - for _, cfgInst := range xmlCfg.Configurations { - if cfgInst.Section != utils.CDRE { - continue - } - cdreCfg := new(CgrXmlCdreCfg) - if err := xml.Unmarshal(cfgInst.rawConfigElement(), cdreCfg); err != nil { - return err - } else if cdreCfg == nil { - return fmt.Errorf("Could not unmarshal CgrXmlCdreCfg: %s", cfgInst.Id) - } - xmlCfg.cdres[cfgInst.Id] = cdreCfg - } - return nil -} - -// Return instances or filtered instance of cdrefw configuration -func (xmlCfg *CgrXmlCfgDocument) GetCdreCfgs(instName string) map[string]*CgrXmlCdreCfg { - if len(instName) != 0 { - if cfg, hasIt := xmlCfg.cdres[instName]; !hasIt { - return nil - } else { - return map[string]*CgrXmlCdreCfg{instName: cfg} - } - } - return xmlCfg.cdres -} - -// Return instances or filtered instance of cdrc configuration -func (xmlCfg *CgrXmlCfgDocument) GetCdrcCfgs(instName string) map[string]*CgrXmlCdrcCfg { - if len(instName) != 0 { - if cfg, hasIt := xmlCfg.cdrcs[instName]; !hasIt { - return nil - } else { - return map[string]*CgrXmlCdrcCfg{instName: cfg} // Filtered - } - } - return xmlCfg.cdrcs // Unfiltered -} diff --git a/data/conf/cgrates.cfg b/data/conf/cgrates.cfg deleted file mode 100644 index efe8530fa..000000000 --- a/data/conf/cgrates.cfg +++ /dev/null @@ -1,182 +0,0 @@ -# Real-time Charging System for Telecom & ISP environments -# Copyright (C) ITsysCOM GmbH -# -# This file contains the default configuration hardcoded into CGRateS. -# This is what you get when you load CGRateS with an empty configuration file. - -[global] -# ratingdb_type = redis # Rating subsystem database: . -# ratingdb_host = 127.0.0.1 # Rating subsystem database host address. -# ratingdb_port = 6379 # Rating subsystem port to reach the database. -# ratingdb_name = 10 # Rating subsystem database name to connect to. -# ratingdb_user = # Rating subsystem username to use when connecting to database. -# ratingdb_passwd = # Rating subsystem password to use when connecting to database. -# accountdb_type = redis # Accounting subsystem database: . -# accountdb_host = 127.0.0.1 # Accounting subsystem database host address. -# accountdb_port = 6379 # Accounting subsystem port to reach the database. -# accountdb_name = 11 # Accounting subsystem database name to connect to. -# accountdb_user = # Accounting subsystem username to use when connecting to database. -# accountdb_passwd = # Accounting subsystem password to use when connecting to database. -# stordb_type = mysql # Stor database type to use: -# stordb_host = 127.0.0.1 # The host to connect to. Values that start with / are for UNIX domain sockets. -# stordb_port = 3306 # The port to reach the stordb. -# stordb_name = cgrates # The name of the log database to connect to. -# stordb_user = cgrates # Username to use when connecting to stordb. -# stordb_passwd = CGRateS.org # Password to use when connecting to stordb. -# stordb_max_open_conns = 0 # Maximum database connections opened -# stordb_max_idle_conns = -10 # Maximum database connections idle -# dbdata_encoding = msgpack # The encoding used to store object data in strings: -# rpc_json_listen = 127.0.0.1:2012 # RPC JSON listening address -# rpc_gob_listen = 127.0.0.1:2013 # RPC GOB listening address -# http_listen = 127.0.0.1:2080 # HTTP listening address -# default_reqtype = rated # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>. -# default_category = call # Default Type of Record to consider when missing from requests. -# default_tenant = cgrates.org # Default Tenant to consider when missing from requests. -# default_subject = cgrates # Default rating Subject to consider when missing from requests. -# rounding_decimals = 10 # System level precision for floats -# http_skip_tls_veify = false # If enabled Http Client will accept any TLS certificate -# tpexport_dir = /var/log/cgrates/tpe # Path towards export folder for offline Tariff Plans -# xmlcfg_path = # Path towards additional config defined in xml file - -[balancer] -# enabled = false # Start Balancer service: . - -[rater] -# enabled = false # Enable Rater service: . -# balancer = # Register to Balancer as worker: <""|internal|127.0.0.1:2013>. - -[scheduler] -# enabled = false # Starts Scheduler service: . - -[cdrs] -# enabled = false # Start the CDR Server service: . -# extra_fields = # Extra fields to store in CDRs for non-generic CDRs -# mediator = # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> -# cdrstats = # Address where to reach the cdrstats service. Empty to disable stats gathering from raw CDRs <""|internal|x.y.z.y:1234> -# store_disable = false # When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - -[cdre] -# cdr_format = csv # Exported CDRs format -# data_usage_multiply_factor = 0.0 # Multiply data usage before export (eg: convert from KBytes to Bytes) -# cost_multiply_factor = 0.0 # Multiply cost before export (0.0 to disable), eg: add VAT -# cost_rounding_decimals = -1 # Rounding decimals for Cost values. -1 to disable rounding -# cost_shift_digits = 0 # Shift digits in the cost on export (eg: convert from EUR to cents) -# mask_destination_id = # Destination id containing called addresses to be masked on export -# mask_length = 0 # Length of the destination suffix to be masked -# export_dir = /var/log/cgrates/cdre # Path where the exported CDRs will be placed -# export_template = cgrid,mediation_runid,tor,accid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,usage,cost - # Exported fields template <""|fld1,fld2|*xml:instance_name> -[cdrc] -# enabled = false # Enable CDR client functionality -# cdrs = internal # Address where to reach CDR server. -# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify -# cdr_format = csv # CDR file format . -# field_separator = , # Separator used in case of csv files. One character only supported and needs to be right after equal sign -# data_usage_multiply_factor = 1 # Conversion factor for data usage -# cdr_in_dir = /var/log/cgrates/cdrc/in # Absolute path towards the directory where the CDRs are stored. -# cdr_out_dir = /var/log/cgrates/cdrc/out # Absolute path towards the directory where processed CDRs will be moved. -# cdr_source_id = csv # Free form field, tag identifying the source of the CDRs within CGRS database. -# tor_field = 2 # TypeOfRecord field identifier. Use index number in case of .csv cdrs. -# accid_field = 3 # Accounting id field identifier. Use index number in case of .csv cdrs. -# reqtype_field = 4 # Request type field identifier. Use index number in case of .csv cdrs. -# direction_field = 5 # Direction field identifier. Use index numbers in case of .csv cdrs. -# tenant_field = 6 # Tenant field identifier. Use index numbers in case of .csv cdrs. -# category_field = 7 # Type of Record field identifier. Use index numbers in case of .csv cdrs. -# account_field = 8 # Account field identifier. Use index numbers in case of .csv cdrs. -# subject_field = 9 # Subject field identifier. Use index numbers in case of .csv CDRs. -# destination_field = 10 # Destination field identifier. Use index numbers in case of .csv cdrs. -# setup_time_field = 11 # Setup time field identifier. Use index numbers in case of .csv cdrs. -# answer_time_field = 12 # Answer time field identifier. Use index numbers in case of .csv cdrs. -# usage_field = 13 # Usage field identifier. Use index numbers in case of .csv cdrs. -# extra_fields = # Extra fields identifiers. For .csv, format: :[...,:] - -[mediator] -# enabled = false # Starts Mediator service: . -# reconnects = 3 # Number of reconnects to rater/cdrs before giving up. -# rater = internal # Address where to reach the Rater: -# cdrstats = # Address where to reach the cdrstats service. Empty to disable stats gathering out of mediated CDRs <""|internal|x.y.z.y:1234> -# store_disable = false # When true, CDRs will not longer be saved in stordb, useful for cdrstats only scenario - - -[cdrstats] -# enabled = false # Starts the cdrstats service: -# queue_length = 50 # Number of items in the stats buffer -# time_window = 1h # Will only keep the CDRs who's call setup time is not older than time.Now()-TimeWindow -# metrics = ASR, ACD, ACC # Stat metric ids to build -# setup_interval = # Filter on CDR SetupTime -# tors = # Filter on CDR TOR fields -# cdr_hosts= # Filter on CDR CdrHost fields -# cdr_sources = # Filter on CDR CdrSource fields -# req_types = # Filter on CDR ReqType fields -# directions = # Filter on CDR Direction fields -# tenants = # Filter on CDR Tenant fields -# categories = # Filter on CDR Category fields -# accounts = # Filter on CDR Account fields -# subjects = # Filter on CDR Subject fields -# destination_prefixes = # Filter on CDR Destination prefixes -# usage_interval = # Filter on CDR Usage -# mediation_run_ids = # Filter on CDR MediationRunId fields -# rated_accounts = # Filter on CDR RatedAccount fields -# rated_subjects = # Filter on CDR RatedSubject fields -# cost_intervals = # Filter on CDR Cost - -[session_manager] -# enabled = false # Starts SessionManager service: -# switch_type = freeswitch # Defines the type of switch behind: -# rater = internal # Address where to reach the Rater <""|internal|127.0.0.1:2013> -# cdrs = # Address where to reach CDR Server, empty to disable CDR capturing <""|internal|127.0.0.1:2013> -# reconnects = 3 # Number of reconnects to rater/cdrs before giving up. -# debit_interval = 10 # Interval to perform debits on. -# min_call_duration = 0s # Only authorize calls with allowed duration bigger than this -# max_call_duration = 3h # Maximum call duration a prepaid call can last - -[freeswitch] -# server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket. -# passwd = ClueCon # FreeSWITCH socket password. -# reconnects = 5 # Number of attempts on connect failure. -# min_dur_low_balance = 5s # Threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval) -# low_balance_ann_file = # File to be played when low balance is reached for prepaid calls -# empty_balance_context = # If defined, prepaid calls will be transfered to this context on empty balance -# empty_balance_ann_file = # File to be played before disconnecting prepaid calls on empty balance (applies only if no context defined) -# cdr_extra_fields = # Extra fields to store in CDRs in case of processing them - -[kamailio] -# evapi_addr = 127.0.0.1:8448 # Address of the kamailio evapi server -# reconnects = 3 # Number of attempts on connect failure. - -[opensips] -# listen_udp = 127.0.0.1:2020 # Address where to listen for datagram events coming from OpenSIPS -# mi_addr = 127.0.0.1:8020 # Adress where to reach OpenSIPS mi_datagram module -# events_subscribe_interval = 60s # Automatic events subscription to OpenSIPS, 0 to disable it -# reconnects = 3 # Number of attempts on connect failure. - -[derived_charging] -# run_ids = # Identifiers of additional sessions control. -# run_filters = # List of cdr field filters for each run. -# reqtype_fields = # Name of request type fields to be used during additional sessions control <""|*default|field_name>. -# direction_fields = # Name of direction fields to be used during additional sessions control <""|*default|field_name>. -# tenant_fields = # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. -# category_fields = # Name of tor fields to be used during additional sessions control <""|*default|field_name>. -# account_fields = # Name of account fields to be used during additional sessions control <""|*default|field_name>. -# subject_fields = # Name of fields to be used during additional sessions control <""|*default|field_name>. -# destination_fields = # Name of destination fields to be used during additional sessions control <""|*default|field_name>. -# setup_time_fields = # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. -# answer_time_fields = # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. -# usage_fields = # Name of usage fields to be used during additional sessions control <""|*default|field_name>. -# combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . - -[history_server] -# enabled = false # Starts History service: . -# history_dir = /var/log/cgrates/history # Location on disk where to store history files. -# save_interval = 1s # Interval to save changed cache into .git archive - -[history_agent] -# enabled = false # Starts History as a client: . -# server = internal # Address where to reach the master history server: - -[mailer] -# server = localhost # The server to use when sending emails out -# auth_user = cgrates # Authenticate to email server using this user -# auth_passwd = CGRateS.org # Authenticate to email server with this password -# from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out - diff --git a/data/conf/cgrates.json b/data/conf/cgrates/cgrates.json similarity index 100% rename from data/conf/cgrates.json rename to data/conf/cgrates/cgrates.json diff --git a/data/conf/samples/apier/apier.json b/data/conf/samples/apier/apier.json new file mode 100644 index 000000000..6bb268430 --- /dev/null +++ b/data/conf/samples/apier/apier.json @@ -0,0 +1,31 @@ +{ +// CGRateS Configuration file +// +// Used in apier_local_tests +// Starts rater, cdrs and mediator connecting over internal channel + +"rater": { + "enabled": true, // enable Rater service: +}, + +"scheduler": { + "enabled": true, // start Scheduler service: +}, + +"cdrs": { + "enabled": true, // start the CDR Server service: + "mediator": "internal", // address where to reach the Mediator. Empty for disabling mediation. <""|internal> +}, + +"cdre": { + "*default": { + "export_dir": "/tmp/cgrates/cdr/cdrexport/csv", // path where the exported CDRs will be placed + } +}, + +"mediator": { + "enabled": true, // starts Mediator service: . + +}, + +} \ No newline at end of file diff --git a/data/conf/samples/mediator1/mediator_test1.json b/data/conf/samples/mediator1/mediator_test1.json new file mode 100644 index 000000000..f446edd2a --- /dev/null +++ b/data/conf/samples/mediator1/mediator_test1.json @@ -0,0 +1,30 @@ +{ +// CGRateS Configuration file +// +// Used in mediator_local_test +// Starts rater, cdrs and mediator connecting over internal channel + +"rater": { + "enabled": true, // enable Rater service: +}, + +"scheduler": { + "enabled": true, // start Scheduler service: +}, + +"cdrs": { + "enabled": true, // start the CDR Server service: + "mediator": "internal", // address where to reach the Mediator. Empty for disabling mediation. <""|internal> +}, + +"cdre": { + "*default": { + "export_dir": "/tmp/cgrates/cdr/cdrexport/csv", // path where the exported CDRs will be placed + } +}, + +"mediator": { + "enabled": true, // starts Mediator service: . +}, + +} \ No newline at end of file diff --git a/data/conf/samples/mediator_test1.cfg b/data/conf/samples/mediator_test1.cfg deleted file mode 100644 index e35edb524..000000000 --- a/data/conf/samples/mediator_test1.cfg +++ /dev/null @@ -1,38 +0,0 @@ -# CGRateS Configuration file -# -# Used in mediator_local_test -# Starts rater, cdrs and mediator connecting over internal channel - -[rater] -enabled = true # Enable RaterCDRSExportPath service: . - -[scheduler] -enabled = true # Starts Scheduler service: . - -[cdrs] -enabled = true # Start the CDR Server service: . -mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> - -[cdre] -export_dir = /tmp/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed - -[mediator] -enabled = true # Starts Mediator service: . -rater = internal # Address where to reach the Rater: -cdrstats = - -[derived_charging] -run_ids = run2 # Identifiers of additional sessions control. -reqtype_fields = *default # Name of request type fields to be used during additional sessions control <""|*default|field_name>. -direction_fields = *default # Name of direction fields to be used during additional sessions control <""|*default|field_name>. -tenant_fields = *default # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. -category_fields = *default # Name of tor fields to be used during additional sessions control <""|*default|field_name>. -account_fields = ^dc2 # Name of account fields to be used during additional sessions control <""|*default|field_name>. -subject_fields = ^dc2 # Name of fields to be used during additional sessions control <""|*default|field_name>. -destination_fields = *default # Name of destination fields to be used during additional sessions control <""|*default|field_name>. -setup_time_fields = *default # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. -answer_time_fields = *default # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. -usage_fields = *default # Name of usage fields to be used during additional sessions control <""|*default|field_name>. - - - diff --git a/data/conf/samples/multiplecdrc/multiplecdrc_fwexport.json b/data/conf/samples/multiplecdrc/multiplecdrc_fwexport.json new file mode 100644 index 000000000..d06eda407 --- /dev/null +++ b/data/conf/samples/multiplecdrc/multiplecdrc_fwexport.json @@ -0,0 +1,125 @@ +{ +// CGRateS Configuration file +// +// Used in mediator_local_test +// Starts rater, cdrs and mediator connecting over internal channel + +"rater": { + "enabled": true, // enable Rater service: +}, + +"scheduler": { + "enabled": true, // start Scheduler service: +}, + +"cdrs": { + "enabled": true, // start the CDR Server service: + "mediator": "internal", // address where to reach the Mediator. Empty for disabling mediation. <""|internal> +}, + +"cdrc": { + "CDRC-CSV1": { + "enabled": true, // enable CDR client functionality + "cdr_in_dir": "/tmp/cgrates/cdrc1/in", // absolute path towards the directory where the CDRs are stored + "cdr_out_dir": "/tmp/cgrates/cdrc1/out", // absolute path towards the directory where processed CDRs will be moved + "cdr_source_id": "csv1", // free form field, tag identifying the source of the CDRs within CDRS database + }, + "CDRC-CSV2": { + "enabled": true, // enable CDR client functionality + "cdr_in_dir": "/tmp/cgrates/cdrc2/in", // absolute path towards the directory where the CDRs are stored + "cdr_out_dir": "/tmp/cgrates/cdrc2/out", // absolute path towards the directory where processed CDRs will be moved + "cdr_source_id": "csv2", // free form field, tag identifying the source of the CDRs within CDRS database + "cdr_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"cdr_field_id": "tor", "value": "~7:s/^(voice|data|sms)$/*$1/"}, + {"cdr_field_id": "accid", "value": "0"}, + {"cdr_field_id": "reqtype", "value": "^rated"}, + {"cdr_field_id": "direction", "value": "^*out"}, + {"cdr_field_id": "tenant", "value": "^cgrates.org"}, + {"cdr_field_id": "category", "value": "~7:s/^voice$/call/"}, + {"cdr_field_id": "account", "value": "3"}, + {"cdr_field_id": "subject", "value": "3"}, + {"cdr_field_id": "destination", "value": "~5:s/^0([1-9]\\d+)$/+49$1/"}, + {"cdr_field_id": "setup_time", "value": "1"}, + {"cdr_field_id": "answer_time", "value": "1"}, + {"cdr_field_id": "usage", "value": "~9:s/^(\\d+)$/${1}s/"}, + ], + }, + "CDRC-CSV3": { + "enabled": true, // enable CDR client functionality + "field_separator": ";", // separator used in case of csv files + "cdr_in_dir": "/tmp/cgrates/cdrc3/in", // absolute path towards the directory where the CDRs are stored + "cdr_out_dir": "/tmp/cgrates/cdrc3/out", // absolute path towards the directory where processed CDRs will be moved + "cdr_source_id": "csv3", // free form field, tag identifying the source of the CDRs within CDRS database + "cdr_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"cdr_field_id": "tor", "value": "^*voice"}, + {"cdr_field_id": "accid", "value": "~3:s/^(\\d{2})\\.(\\d{2})\\.(\\d{4})\\s{2}(\\d{2}):(\\d{2}):(\\d{2})$/$1$2$3$4$5$6/"}, + {"cdr_field_id": "reqtype", "value": "^rated"}, + {"cdr_field_id": "direction", "value": "^*out"}, + {"cdr_field_id": "tenant", "value": "^cgrates.org"}, + {"cdr_field_id": "category", "value": "^call"}, + {"cdr_field_id": "account", "value": "~0:s/^([1-9]\\d+)$/+$1/"}, + {"cdr_field_id": "subject", "value": "~0:s/^([1-9]\\d+)$/+$1/"}, + {"cdr_field_id": "destination", "value": "~1:s/^([1-9]\\d+)$/+$1/"}, + {"cdr_field_id": "setup_time", "value": "4"}, + {"cdr_field_id": "answer_time", "value": "4"}, + {"cdr_field_id": "usage", "value": "~6:s/^(\\d+)$/${1}s/"}, + ], + } +}, + +"mediator": { + "enabled": true, // starts Mediator service: . +}, + +"cdre": { + "CDRE-FW1": { + "cdr_format": "fwv", + "field_separator": "", + "header_fields": [ + {"tag": "ToR", "type": "constant", "value": "10", "width": 2}, + {"tag": "Filler1", "type": "filler", "width": 3}, + {"tag": "FileType", "type": "constant", "value": "SIP", "width": 3}, + {"tag": "FileSeqNr", "type": "metatag", "value": "export_id", "padding": "zeroleft", "width": 5}, + {"tag": "LastCdr", "type": "metatag", "value": "last_cdr_atime", "layout": "020106150405", "width": 12}, + {"tag": "FileCreationfTime", "type": "metatag", "value": "time_now", "layout": "020106150405", "width": 12}, + {"tag": "FileVersion", "type": "constant", "value": "01", "width": 2}, + {"tag": "Filler2", "type": "filler", "width": 105}, + ], // template of the exported header fields + "content_fields": [ // template of the exported content fields + {"tag": "ToR", "type": "constant", "value": "20", "width": 2}, + {"tag": "Subject", "type": "cdrfield", "value": "subject", "width": 12, "padding": "right", "mandatory": true}, + {"tag": "ConnectionNumber", "type": "constant", "value": "00000", "width": 5}, + {"tag": "CallerId", "type": "cdrfield", "value": "~callerid:s/\\+(\\d+)/00$1/", "strip": "xright", "width": 15, "padding": "right"}, + {"tag": "Destination", "type": "cdrfield", "value": "~destination:s/^\\+311400(\\d+)/$1/:s/^\\+311412\\d\\d112/112/:s/^\\+31(\\d+)/0$1/:s/^\\+(\\d+)/00$1/", + "strip": "xright", "width": 24, "padding": "right", "mandatory": true}, + {"tag": "TypeOfService", "type": "constant", "value": "00", "width": 2}, + {"tag": "ServiceId", "type": "constant", "value": "11", "width": 4, "padding": "right"}, + {"tag": "AnswerTime", "type": "cdrfield", "value": "answer_time", "layout": "020106150405", "width": 12, "mandatory": true}, + {"tag": "Usage", "type": "cdrfield", "value": "usage", "layout": "seconds", "width": 6, "padding": "right", "mandatory": true}, + {"tag": "DataCounter", "type": "filler", "width": 6}, + {"tag": "VatCode", "type": "constant", "value": "1", "width": 1}, + {"tag": "NetworkId", "type": "constant", "value": "S1", "width": 2}, + {"tag": "DestinationSubId", "type": "cdrfield", "value": "~cost_details:s/MatchedDestId:.+_(\\w{5})/$1/:s/(\\w{6})/$1/", "width": 5}, + {"tag": "NetworkSubtype", "type": "constant", "value": "3", "width": 1, "padding": "left"}, + {"tag": "CgrId", "type": "cdrfield", "value": "cgrid", "strip": "xleft", "width": 16, "padding": "right", "mandatory": true}, + {"tag": "FillerVolume1", "type": "filler", "width": 8}, + {"tag": "FillerVolume2", "type": "filler", "width": 8}, + {"tag": "DestinationSubId", "type": "cdrfield", "value": "~cost_details:s/MatchedDestId:.+_(\\w{5})/$1/:s/(\\w{6})/$1/", "width": 5}, + {"tag": "Cost", "type": "cdrfield", "value": "cost", "padding": "zeroleft", "width": 9}, + {"tag": "MaskDestination", "type": "metatag", "value": "mask_destination", "width": 1}, + ], + "trailer_fields": [ + {"tag": "ToR", "type": "constant", "value": "90", "width": 2}, + {"tag": "Filler1", "type": "filler", "width": 3}, + {"tag": "FileType", "type": "constant", "value": "SIP", "width": 3}, + {"tag": "FileSeqNr", "type": "metatag", "value": "export_id", "padding": "zeroleft", "width": 5}, + {"tag": "TotalRecords", "type": "metatag", "value": "cdrs_number", "padding": "zeroleft", "width": 6}, + {"tag": "TotalDuration", "type": "metatag", "value": "cdrs_duration", "padding": "zeroleft", "width": 8}, + {"tag": "FirstCdrTime", "type": "metatag", "value": "first_cdr_atime", "layout": "020106150405", "width": 12}, + {"tag": "LastCdrTime", "type": "metatag", "value": "last_cdr_atime", "layout": "020106150405", "width": 12}, + {"tag": "Filler1", "type": "filler", "width": 93}, + ], // template of the exported trailer fields + } +}, + +} \ No newline at end of file diff --git a/data/conf/samples/multiplecdrc_fwexport.cfg b/data/conf/samples/multiplecdrc_fwexport.cfg deleted file mode 100644 index 3961e8609..000000000 --- a/data/conf/samples/multiplecdrc_fwexport.cfg +++ /dev/null @@ -1,28 +0,0 @@ -# Real-time Charging System for Telecom & ISP environments -# Copyright (C) 2012-2014 ITsysCOM GmbH - -[global] -xmlcfg_path = /usr/share/cgrates/conf/samples/multiplecdrc_fwexport.xml - -[rater] -enabled = true # Enable RaterCDRSExportPath service: . - -[scheduler] -enabled = true # Starts Scheduler service: . - -[cdrs] -enabled = true # Start the CDR Server service: . -mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> - -[cdre] -export_dir = /tmp/cgrates/cdr/cdre/csv # Path where the exported CDRs will be placed -export_template = *xml:CDRE-FW1 - -[cdrc] -enabled = true -cdr_in_dir = /tmp/cgrates/cdrc1/in # Absolute path towards the directory where the CDRs are stored. -cdr_out_dir =/tmp/cgrates/cdrc1/out # Absolute path towards the directory where processed CDRs will be moved. -cdr_source_id = csv1 # Free form field, tag identifying the source of the CDRs within CGRS database. - -[mediator] -enabled = true # Starts Mediator service: . \ No newline at end of file diff --git a/data/conf/samples/multiplecdrc_fwexport.xml b/data/conf/samples/multiplecdrc_fwexport.xml deleted file mode 100644 index 6f911c041..000000000 --- a/data/conf/samples/multiplecdrc_fwexport.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - true - internal - csv - , - 0 - /tmp/cgrates/cdrc2/in - /tmp/cgrates/cdrc2/out - csv2 - - - - - - - - - - - - - - - - - true - internal - csv - ; - 0 - /tmp/cgrates/cdrc3/in - /tmp/cgrates/cdrc3/out - csv3 - - - - - - - - - - - - - - - - - fwv - 0.0 - 0.0 - 0 - MASKED_DESTINATIONS - 0 - /var/log/cgrates/cdre - -
- - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
\ No newline at end of file diff --git a/data/conf/samples/tutorial/tutorial.json b/data/conf/samples/tutorial/tutorial.json new file mode 100644 index 000000000..cfd9fe15b --- /dev/null +++ b/data/conf/samples/tutorial/tutorial.json @@ -0,0 +1,31 @@ +{ +// CGRateS Configuration file +// +// Used in mediator_local_test +// Starts rater, cdrs and mediator connecting over internal channel + +"rater": { + "enabled": true, // enable Rater service: +}, + +"scheduler": { + "enabled": true, // start Scheduler service: +}, + +"cdrs": { + "enabled": true, // start the CDR Server service: + "mediator": "internal", // address where to reach the Mediator. +}, + +"cdre": { + "*default": { + "export_dir": "/tmp", // path where the exported CDRs will be placed + } +}, + +"mediator": { + "enabled": true, // starts Mediator service: . + "cdrstats": "internal", +}, + +} \ No newline at end of file diff --git a/data/conf/samples/tutorial_local_test.cfg b/data/conf/samples/tutorial_local_test.cfg deleted file mode 100644 index 089506a5f..000000000 --- a/data/conf/samples/tutorial_local_test.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# Real-time Charging System for Telecom & ISP environments -# Copyright (C) ITsysCOM GmbH -# -# This file contains the default configuration hardcoded into CGRateS. -# This is what you get when you load CGRateS with an empty configuration file. - -[rater] -enabled = true # Enable RaterCDRSExportPath service: . - -[scheduler] -enabled = true # Starts Scheduler service: . - -[cdrs] -enabled = true # Start the CDR Server service: . -mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> - -[cdre] -cdr_format = csv # Exported CDRs format -export_dir = /tmp # Path where the exported CDRs will be placed - -[mediator] -enabled = true # Starts Mediator service: . -cdrstats = internal - -[cdrstats] -enabled = true # Starts the cdrstats service: \ No newline at end of file diff --git a/data/scripts/pkg/debian/rules b/data/scripts/pkg/debian/rules index b26ea2e9d..aa8b689b8 100755 --- a/data/scripts/pkg/debian/rules +++ b/data/scripts/pkg/debian/rules @@ -25,8 +25,7 @@ binary-arch: clean cd $(SRCDIR) && go install mkdir -p $(PKGDIR)/usr/bin cp $(GOPATH)/bin/cgr-* $(PKGDIR)/usr/bin/ - mkdir -p $(PKGDIR)/etc/cgrates - cp $(SRCDIR)/data/conf/cgrates.cfg $(PKGDIR)/etc/cgrates/ + cp -r $(SRCDIR)/data/conf/cgrates $(PKGDIR)/etc/ mkdir -p $(PKGDIR)/usr/share/cgrates cp -r $(SRCDIR)/data/* $(PKGDIR)/usr/share/cgrates/ mkdir -p $(PKGDIR)/var/log/cgrates/cdrc/in diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go index 1b2dd1c83..f69cd5aff 100644 --- a/engine/fscdr_test.go +++ b/engine/fscdr_test.go @@ -46,7 +46,10 @@ func TestFirstNonEmpty(t *testing.T) { } func TestCDRFields(t *testing.T) { - cfg, _ = config.NewDefaultCGRConfig() + cfg, err = config.NewDefaultCGRConfig() + if err != nil { + t.Error(err) + } cfg.CDRSExtraFields = []*utils.RSRField{&utils.RSRField{Id: "sip_user_agent"}} fsCdr, err := NewFSCdr(body) if err != nil { @@ -108,10 +111,9 @@ func TestSearchReplaceInExtraFields(t *testing.T) { } func TestDDazRSRExtraFields(t *testing.T) { - eFieldsCfg := []byte(`[cdrs] -extra_fields = ~effective_caller_id_number:s/(\d+)/+$1/ - -`) + eFieldsCfg := `{"cdrs": { + "extra_fields": ["~effective_caller_id_number:s/(\\d+)/+$1/"], +},}` simpleJsonCdr := []byte(`{ "core-uuid": "feef0b51-7fdf-4c4a-878e-aff233752de2", "channel_data": { @@ -144,7 +146,7 @@ extra_fields = ~effective_caller_id_number:s/(\d+)/+$1/ } }`) var err error - cfg, err = config.NewCGRConfigFromBytes(eFieldsCfg) + cfg, err = config.NewCGRConfigFromJsonString(eFieldsCfg) if err != nil { t.Error("Could not parse the config", err.Error()) } else if !reflect.DeepEqual(cfg.CDRSExtraFields, []*utils.RSRField{&utils.RSRField{Id: "effective_caller_id_number", diff --git a/engine/handler_derivedcharging.go b/engine/handler_derivedcharging.go index 6178d2d7d..44fc5d98c 100644 --- a/engine/handler_derivedcharging.go +++ b/engine/handler_derivedcharging.go @@ -19,17 +19,18 @@ along with this program. If not, see package engine import ( - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) -// Transparently handles merging between storage data and configuration, useful as local handler -func HandleGetDerivedChargers(acntStorage AccountingStorage, cfg *config.CGRConfig, attrs utils.AttrDerivedChargers) (utils.DerivedChargers, error) { +// Handles retrieving of DerivedChargers profile based on longest match from AccountingDb +func HandleGetDerivedChargers(acntStorage AccountingStorage, attrs utils.AttrDerivedChargers) (utils.DerivedChargers, error) { var dcs utils.DerivedChargers - var err error strictKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, attrs.Subject) anySubjKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, utils.ANY) - for _, dcKey := range []string{strictKey, anySubjKey} { + anyAcntKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, utils.ANY, utils.ANY) + anyCategKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, utils.ANY, utils.ANY, utils.ANY) + anyTenantKey := utils.DerivedChargersKey(attrs.Direction, utils.ANY, utils.ANY, utils.ANY, utils.ANY) + for _, dcKey := range []string{strictKey, anySubjKey, anyAcntKey, anyCategKey, anyTenantKey} { if dcsDb, err := acntStorage.GetDerivedChargers(dcKey, false); err != nil && err.Error() != utils.ERR_NOT_FOUND { return nil, err } else if dcsDb != nil { @@ -37,16 +38,5 @@ func HandleGetDerivedChargers(acntStorage AccountingStorage, cfg *config.CGRConf break } } - if dcs == nil { - dcs = cfg.DerivedChargers - return dcs, nil - } - if cfg.CombinedDerivedChargers { - for _, cfgDc := range cfg.DerivedChargers { - if dcs, err = dcs.Append(cfgDc); err != nil { - return nil, err - } - } - } return dcs, nil } diff --git a/engine/handler_derivedcharging_test.go b/engine/handler_derivedcharging_test.go index e212e05dd..43f65c92a 100644 --- a/engine/handler_derivedcharging_test.go +++ b/engine/handler_derivedcharging_test.go @@ -18,6 +18,7 @@ along with this program. If not, see package engine +/* import ( "reflect" "testing" @@ -39,10 +40,11 @@ func init() { acntDb.CacheAccounting(nil, nil, nil, nil) } + // Accounting db has no DerivedChargers nor configured defaults func TestHandleGetEmptyDC(t *testing.T) { attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "test2", Subject: "test2"} - if dcs, err := HandleGetDerivedChargers(acntDb, cfgDcT, attrs); err != nil { + if dcs, err := HandleGetDerivedChargers(acntDb, attrs); err != nil { t.Error("Unexpected error", err.Error()) } else if !reflect.DeepEqual(dcs, cfgDcT.DerivedChargers) { t.Error("Returned DerivedChargers not matching the configured ones") @@ -91,3 +93,4 @@ func TestHandleGetStoredDC(t *testing.T) { t.Error("Returned DerivedChargers not matching the configured ones") } } +*/ diff --git a/engine/mediator_local_test.go b/engine/mediator_local_test.go index e33b9b81c..a78ded552 100644 --- a/engine/mediator_local_test.go +++ b/engine/mediator_local_test.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2014 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,14 +55,14 @@ var httpClient *http.Client var storDbType = flag.String("stordb_type", utils.MYSQL, "The type of the storDb database ") var startDelay = flag.Int("delay_start", 300, "Number of miliseconds to it for rater to start and cache") -var cfgPath = path.Join(*dataDir, "conf", "samples", "mediator_test1.cfg") +var cfgPath = path.Join(*dataDir, "conf", "samples", "mediator_test1") func TestMediInitRatingDb(t *testing.T) { if !*testLocal { return } var err error - cgrCfg, err = config.NewCGRConfigFromFile(&cfgPath) + cgrCfg, err = config.NewCGRConfigFromFolder(cfgPath) if err != nil { t.Fatal("Got config error: ", err.Error()) } diff --git a/engine/responder.go b/engine/responder.go index efdd0af55..af0c81b1e 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -221,7 +221,7 @@ func (rs *Responder) GetSessionRuns(ev utils.Event, sRuns *[]*SessionRun) error func (rs *Responder) GetDerivedChargers(attrs utils.AttrDerivedChargers, dcs *utils.DerivedChargers) error { // ToDo: Make it work with balancer if needed - if dcsH, err := HandleGetDerivedChargers(accountingStorage, config.CgrConfig(), attrs); err != nil { + if dcsH, err := HandleGetDerivedChargers(accountingStorage, attrs); err != nil { return err } else if dcsH != nil { *dcs = dcsH diff --git a/engine/responder_test.go b/engine/responder_test.go index e73b3b132..556efdcd2 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,7 +15,6 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ - package engine import ( @@ -29,15 +28,24 @@ import ( var rsponder *Responder +func init() { + cfg, _ := config.NewDefaultCGRConfig() + config.SetCgrConfig(cfg) +} + // Test internal abilites of GetDerivedChargers func TestResponderGetDerivedChargers(t *testing.T) { - cfg, _ := config.NewDefaultCGRConfig() + cfgedDC := utils.DerivedChargers{&utils.DerivedCharger{RunId: "responder1", ReqTypeField: "test", DirectionField: "test", TenantField: "test", CategoryField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", UsageField: "test"}} - cfg.DerivedChargers = cfgedDC - config.SetCgrConfig(cfg) rsponder = &Responder{} attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "responder_test", Subject: "responder_test"} + if err := accountingStorage.SetDerivedChargers(utils.DerivedChargersKey(utils.OUT, utils.ANY, utils.ANY, utils.ANY, utils.ANY), cfgedDC); err != nil { + t.Error(err) + } + if err := accountingStorage.CacheAccounting(nil, []string{}, []string{}, []string{}); err != nil { + t.Error(err) + } var dcs utils.DerivedChargers if err := rsponder.GetDerivedChargers(attrs, &dcs); err != nil { t.Error("Unexpected error", err.Error()) @@ -47,7 +55,6 @@ func TestResponderGetDerivedChargers(t *testing.T) { } func TestGetDerivedMaxSessionTime(t *testing.T) { - config.CgrConfig().CombinedDerivedChargers = false testTenant := "vdf" cdr := &utils.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: "rated", Direction: "*out", Tenant: testTenant, Category: "call", Account: "dan", Subject: "dan", @@ -88,7 +95,6 @@ func TestGetDerivedMaxSessionTime(t *testing.T) { } func TestGetSessionRuns(t *testing.T) { - config.CgrConfig().CombinedDerivedChargers = false testTenant := "vdf" cdr := &utils.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: "prepaid", Direction: "*out", Tenant: testTenant, Category: "call", Account: "dan2", Subject: "dan2", diff --git a/engine/storage_utils.go b/engine/storage_utils.go index f22b50434..aa68b38dd 100644 --- a/engine/storage_utils.go +++ b/engine/storage_utils.go @@ -41,12 +41,6 @@ func ConfigureRatingStorage(db_type, host, port, name, user, pass, marshaler str host += ":" + port } d, err = NewRedisStorage(host, db_nb, pass, marshaler) - /* - // Add here as soon as interface implemented - case utils.MONGO: - d, err = NewMongoStorage(host, port, name, user, pass) - db = d.(RatingStorage) - */ default: err = errors.New("unknown db") } diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 009321733..ccd91debf 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -163,7 +163,11 @@ func TestDebit2(t *testing.T) { if acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value != 20 { t.Error("Account does not have expected minutes in balance", acnt.BalanceMap[engine.MINUTES+engine.OUTBOUND][0].Value) } - if acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value != -0.01 { - t.Error("Account does not have expected monetary balance", acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND][0].Value) + for _, blnc := range acnt.BalanceMap[engine.CREDIT+engine.OUTBOUND] { // Test negative balance for default one + if blnc.Weight == 10 && blnc.Value != 0 { + t.Errorf("Balance with weight: %d, having value: %f ", blnc.Weight, blnc.Value) + } else if blnc.Weight == 0 && blnc.Value != -0.01 { + t.Errorf("Balance with weight: %d, having value: %f ", blnc.Weight, blnc.Value) + } } } diff --git a/general_tests/multiplecdrc_local_test.go b/general_tests/multiplecdrc_local_test.go index fb5e90b95..a01ce9bf8 100644 --- a/general_tests/multiplecdrc_local_test.go +++ b/general_tests/multiplecdrc_local_test.go @@ -44,11 +44,6 @@ var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path h var storDbType = flag.String("stordb_type", "mysql", "The type of the storDb database ") var waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache") -func init() { - cfgPath = path.Join(*dataDir, "conf", "samples", "multiplecdrc_fwexport.cfg") - cfg, _ = config.NewCGRConfigFromFile(&cfgPath) -} - func startEngine() error { enginePath, err := exec.LookPath("cgr-engine") if err != nil { @@ -69,6 +64,14 @@ func stopEngine() error { return nil } +func TestLoadConfig(t *testing.T) { + var err error + cfgPath = path.Join(*dataDir, "conf", "samples", "multiplecdrc") + if cfg, err = config.NewCGRConfigFromFolder(cfgPath); err != nil { + t.Error(err) + } +} + func TestEmptyTables(t *testing.T) { if !*testLocal { return @@ -97,7 +100,7 @@ func TestCreateCdrDirs(t *testing.T) { if !*testLocal { return } - for _, cdrcInst := range cfg.CdrcInstances { + for _, cdrcInst := range cfg.CdrcProfiles { for _, dir := range []string{cdrcInst.CdrInDir, cdrcInst.CdrOutDir} { if err := os.RemoveAll(dir); err != nil { t.Fatal("Error removing folder: ", dir, err) @@ -151,7 +154,7 @@ dbafe9c8614c785a65aabd116dd3959c3c56f7f7,default,*voice,dsafdsag,rated,*out,cgra if err := ioutil.WriteFile(tmpFilePath, []byte(fileContent1), 0644); err != nil { t.Fatal(err.Error) } - if err := os.Rename(tmpFilePath, path.Join(cfg.CdrcInstances[0].CdrInDir, fileName)); err != nil { + if err := os.Rename(tmpFilePath, path.Join(cfg.CdrcProfiles["CDRC-CSV1"].CdrInDir, fileName)); err != nil { t.Fatal("Error moving file to processing directory: ", err) } } @@ -169,7 +172,7 @@ func TestHandleCdr2File(t *testing.T) { if err := ioutil.WriteFile(tmpFilePath, []byte(fileContent), 0644); err != nil { t.Fatal(err.Error) } - if err := os.Rename(tmpFilePath, path.Join(*cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV2")["CDRC-CSV2"].CdrInDir, fileName)); err != nil { + if err := os.Rename(tmpFilePath, path.Join(cfg.CdrcProfiles["CDRC-CSV2"].CdrInDir, fileName)); err != nil { t.Fatal("Error moving file to processing directory: ", err) } } @@ -186,7 +189,7 @@ func TestHandleCdr3File(t *testing.T) { if err := ioutil.WriteFile(tmpFilePath, []byte(fileContent), 0644); err != nil { t.Fatal(err.Error) } - if err := os.Rename(tmpFilePath, path.Join(*cfg.XmlCfgDocument.GetCdrcCfgs("CDRC-CSV3")["CDRC-CSV3"].CdrInDir, fileName)); err != nil { + if err := os.Rename(tmpFilePath, path.Join(cfg.CdrcProfiles["CDRC-CSV3"].CdrInDir, fileName)); err != nil { t.Fatal("Error moving file to processing directory: ", err) } } diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index c2a2e3640..2be1f7ac8 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -40,9 +40,9 @@ func TestInitCfg(t *testing.T) { return } // Init config first - tutCfgPath = path.Join(*dataDir, "conf", "samples", "tutorial_local_test.cfg") + tutCfgPath = path.Join(*dataDir, "conf", "samples", "tutorial") var err error - tutCfg, err = config.NewCGRConfigFromFile(&tutCfgPath) + tutCfg, err = config.NewCGRConfigFromFolder(tutCfgPath) if err != nil { t.Error(err) } diff --git a/hard_update_external_libs.py b/hard_update_external_libs.py index 6962c5880..17edeb87a 100755 --- a/hard_update_external_libs.py +++ b/hard_update_external_libs.py @@ -4,8 +4,7 @@ import os import os.path from subprocess import call -libs = ('code.google.com/p/goconf/conf', - 'github.com/bmizerany/pq', +libs = ('github.com/bmizerany/pq', 'github.com/ugorji/go/codec', 'labix.org/v2/mgo', 'github.com/cgrates/fsock', diff --git a/update_external_libs.sh b/update_external_libs.sh index efb14be02..49e2bb42a 100755 --- a/update_external_libs.sh +++ b/update_external_libs.sh @@ -1,5 +1,4 @@ #!/usr/bin/env sh -go get -v -u code.google.com/p/goconf/conf go get -v -u github.com/bmizerany/pq go get -v -u github.com/ugorji/go/codec go get -v -u labix.org/v2/mgo diff --git a/utils/coreutils.go b/utils/coreutils.go index e035887a0..8325a7e9b 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -355,3 +355,7 @@ func BoolPointer(b bool) *bool { func StringSlicePointer(slc []string) *[]string { return &slc } + +func Float64SlicePointer(slc []float64) *[]float64 { + return &slc +} diff --git a/utils/rsrfield.go b/utils/rsrfield.go index b3efc19ad..e89236f9e 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -1,6 +1,6 @@ /* Real-time Charging System for Telecom & ISP environments -Copyright (C) 2012-2014 ITsysCOM GmbH +Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,10 +21,14 @@ package utils import ( "fmt" "regexp" + "strconv" "strings" ) func NewRSRField(fldStr string) (*RSRField, error) { + if fldStrUnquoted, err := strconv.Unquote(fldStr); err == nil { + fldStr = fldStrUnquoted + } if len(fldStr) == 0 { return nil, nil } @@ -108,8 +112,16 @@ func ParseRSRFields(fldsStr, sep string) (RSRFields, error) { return nil, nil } rulesSplt := strings.Split(fldsStr, sep) - rsrFields := make(RSRFields, len(rulesSplt)) - for idx, ruleStr := range rulesSplt { + return ParseRSRFieldsFromSlice(rulesSplt) + +} + +func ParseRSRFieldsFromSlice(flds []string) (RSRFields, error) { + if len(flds) == 0 { + return nil, nil + } + rsrFields := make(RSRFields, len(flds)) + for idx, ruleStr := range flds { if rsrField, err := NewRSRField(ruleStr); err != nil { return nil, err } else { @@ -117,6 +129,7 @@ func ParseRSRFields(fldsStr, sep string) (RSRFields, error) { } } return rsrFields, nil + } type RSRFields []*RSRField