mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
NEW configuration format - cgrates.json
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -19,186 +19,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
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: <label_extrafield_1>:<index_extrafield_1>
|
||||
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 <csv>
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,77 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,163 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
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, <Cost)
|
||||
}
|
||||
|
||||
func (csCfg *CdrStatsConfig) setDefaults() {
|
||||
csCfg.Id = utils.META_DEFAULT
|
||||
csCfg.QueueLength = 50
|
||||
csCfg.TimeWindow = time.Duration(1) * time.Hour
|
||||
csCfg.Metrics = []string{"ASR", "ACD", "ACC"}
|
||||
func (self *CdrStatsConfig) loadFromJsonCfg(jsnCfg *CdrStatsJsonCfg) error {
|
||||
var err error
|
||||
if jsnCfg.Queue_length != nil {
|
||||
self.QueueLength = *jsnCfg.Queue_length
|
||||
}
|
||||
if jsnCfg.Time_window != nil {
|
||||
if self.TimeWindow, err = utils.ParseDurationWithSecs(*jsnCfg.Time_window); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if jsnCfg.Metrics != nil {
|
||||
self.Metrics = *jsnCfg.Metrics
|
||||
}
|
||||
for _, setupTimeStr := range *jsnCfg.Setup_interval {
|
||||
if setupTime, err := utils.ParseTimeDetectLayout(setupTimeStr); err != nil {
|
||||
return err
|
||||
} else {
|
||||
self.SetupInterval = append(self.SetupInterval, setupTime)
|
||||
}
|
||||
}
|
||||
if jsnCfg.Tors != nil {
|
||||
self.TORs = *jsnCfg.Tors
|
||||
}
|
||||
if jsnCfg.Cdr_hosts != nil {
|
||||
self.CdrHosts = *jsnCfg.Cdr_hosts
|
||||
}
|
||||
if jsnCfg.Cdr_sources != nil {
|
||||
self.CdrSources = *jsnCfg.Cdr_sources
|
||||
}
|
||||
if jsnCfg.Req_types != nil {
|
||||
self.ReqTypes = *jsnCfg.Req_types
|
||||
}
|
||||
if jsnCfg.Directions != nil {
|
||||
self.Directions = *jsnCfg.Directions
|
||||
}
|
||||
if jsnCfg.Tenants != nil {
|
||||
self.Tenants = *jsnCfg.Tenants
|
||||
}
|
||||
if jsnCfg.Categories != nil {
|
||||
self.Categories = *jsnCfg.Categories
|
||||
}
|
||||
if jsnCfg.Accounts != nil {
|
||||
self.Accounts = *jsnCfg.Accounts
|
||||
}
|
||||
if jsnCfg.Subjects != nil {
|
||||
self.Subjects = *jsnCfg.Subjects
|
||||
}
|
||||
if jsnCfg.Destination_prefixes != nil {
|
||||
self.DestinationPrefixes = *jsnCfg.Destination_prefixes
|
||||
}
|
||||
for _, usageDurStr := range *jsnCfg.Usage_interval {
|
||||
if usageDur, err := utils.ParseDurationWithSecs(usageDurStr); err != nil {
|
||||
return err
|
||||
} else {
|
||||
self.UsageInterval = append(self.UsageInterval, usageDur)
|
||||
}
|
||||
}
|
||||
if jsnCfg.Mediation_run_ids != nil {
|
||||
self.MediationRunIds = *jsnCfg.Mediation_run_ids
|
||||
}
|
||||
if jsnCfg.Rated_accounts != nil {
|
||||
self.RatedAccounts = *jsnCfg.Rated_accounts
|
||||
}
|
||||
if jsnCfg.Rated_subjects != nil {
|
||||
self.RatedSubjects = *jsnCfg.Rated_subjects
|
||||
}
|
||||
if jsnCfg.Cost_interval != nil {
|
||||
self.CostInterval = *jsnCfg.Cost_interval
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
33
config/cfg_data.json
Normal file
33
config/cfg_data.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
|
||||
// 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.
|
||||
|
||||
"general": {
|
||||
"default_reqtype": "pseudoprepaid", // default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>
|
||||
},
|
||||
|
||||
"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/"},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
992
config/config.go
992
config/config.go
File diff suppressed because it is too large
Load Diff
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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 <csv>
|
||||
"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. <internal|x.y.z.y:1234>
|
||||
"cdr_format": "csv", // CDR file format <csv|freeswitch_csv|fwv>
|
||||
@@ -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
|
||||
},
|
||||
|
||||
}
|
||||
}`
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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("<ConfigSanity> 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: <label_extrafield_1>:<index_extrafield_1>
|
||||
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
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
# TEST DATA - NOT FOR PRODUCTION USAGE
|
||||
#
|
||||
|
||||
[global]
|
||||
ratingdb_type = test # Rating subsystem database: <redis>.
|
||||
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: <redis>.
|
||||
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: <same|postgres|mongo|redis>
|
||||
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: <msgpack|json>
|
||||
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: <true|false>.
|
||||
|
||||
[rater]
|
||||
enabled = true # Enable Rater service: <true|false>.
|
||||
balancer = test # Register to Balancer as worker: <enabled|disabled>.
|
||||
|
||||
[scheduler]
|
||||
enabled = true # Starts Scheduler service: <true|false>.
|
||||
|
||||
[cdrs]
|
||||
enabled = true # Start the CDR Server service: <true|false>.
|
||||
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 <csv>
|
||||
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 <csv>.
|
||||
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: <true|false>.
|
||||
rater = test # Address where to reach the Rater: <internal|x.y.z.y:1234>
|
||||
reconnects = 99 # Number of reconnects to rater before giving up.
|
||||
cdrstats = test # Address where to reach the cdrstats service: <internal|x.y.z.y:1234>
|
||||
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: <true|false>.
|
||||
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: <true|false>.
|
||||
switch_type = test # Defines the type of switch behind: <freeswitch>.
|
||||
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 <true|false>.
|
||||
|
||||
[history_server]
|
||||
enabled = true # Starts Hiscategoryy service: <true|false>.
|
||||
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: <true|false>.
|
||||
server = test # Address where to reach the master hiscategoryy server: <internal|x.y.z.y:1234>
|
||||
|
||||
[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
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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 := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdrc" id="CDRC-CSVDF">
|
||||
<enabled>true</enabled>
|
||||
</configuration>
|
||||
</document>`
|
||||
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 := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdrc" id="CDRC-CSV1">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_format>csv</cdr_format>
|
||||
<field_separator>,</field_separator>
|
||||
<data_usage_multiply_factor>1024</data_usage_multiply_factor>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/var/log/cgrates/cdrc/in</cdr_in_dir>
|
||||
<cdr_out_dir>/var/log/cgrates/cdrc/out</cdr_out_dir>
|
||||
<cdr_source_id>freeswitch_csv</cdr_source_id>
|
||||
<fields>
|
||||
<field tag="accid" value="0;13" />
|
||||
<field tag="reqtype" value="1" />
|
||||
<field tag="direction" value="2" />
|
||||
<field tag="tenant" value="3" />
|
||||
<field tag="category" value="4" />
|
||||
<field tag="account" value="5" />
|
||||
<field tag="subject" value="6" />
|
||||
<field tag="destination" value="7" />
|
||||
<field tag="setup_time" value="8" />
|
||||
<field tag="answer_time" value="9" />
|
||||
<field tag="usage" value="10" />
|
||||
<field tag="extr1" value="11" />
|
||||
<field tag="extr2" value="12" />
|
||||
</fields>
|
||||
</configuration>
|
||||
</document>`
|
||||
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"])
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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 := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdre" id="CDRE-FW1">
|
||||
<cdr_format>fwv</cdr_format>
|
||||
<data_usage_multiply_factor>1.0</data_usage_multiply_factor>
|
||||
<cost_multiply_factor>0.0</cost_multiply_factor>
|
||||
<cost_rounding_decimals>-1</cost_rounding_decimals>
|
||||
<cost_shift_digits>0</cost_shift_digits>
|
||||
<mask_destination_id>MASKED_DESTINATIONS</mask_destination_id>
|
||||
<mask_length>0</mask_length>
|
||||
<export_dir>/var/log/cgrates/cdre</export_dir>
|
||||
<export_template>
|
||||
<header>
|
||||
<fields>
|
||||
<field tag="TypeOfRecord" type="constant" value="10" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="DistributorCode" type="constant" value="VOI" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="LastCdr" type="metatag" value="last_cdr_time" layout="020106150400" width="12" />
|
||||
<field tag="FileCreationfTime" type="metatag" value="time_now" layout="020106150400" width="12" />
|
||||
<field tag="Version" type="constant" value="01" width="2" />
|
||||
<field tag="Filler2" type="filler" width="105" />
|
||||
</fields>
|
||||
</header>
|
||||
<content>
|
||||
<fields>
|
||||
<field tag="TypeOfRecord" type="constant" value="20" width="2" />
|
||||
<field tag="Account" type="cdrfield" value="cgrid" width="12" mandatory="true" />
|
||||
<field tag="Subject" type="cdrfield" value="subject" strip="left" padding="left" width="5" />
|
||||
<field tag="CLI" type="cdrfield" value="cli" strip="xright" width="15" />
|
||||
<field tag="Destination" type="cdrfield" value="destination" strip="xright" width="24" />
|
||||
<field tag="TOR" type="constant" value="02" width="2" />
|
||||
<field tag="SubtypeTOR" type="constant" value="11" width="4" />
|
||||
<field tag="SetupTime" type="cdrfield" value="start_time" layout="020106150400" width="12" />
|
||||
<field tag="Duration" type="cdrfield" value="duration" width="6" />
|
||||
<field tag="DataVolume" type="filler" width="6" />
|
||||
<field tag="TaxCode" type="constant" value="1" width="1" />
|
||||
<field tag="OperatorCode" type="cdrfield" value="operator" width="2" />
|
||||
<field tag="ProductId" type="cdrfield" value="productid" width="5" />
|
||||
<field tag="NetworkId" type="constant" value="3" width="1" />
|
||||
<field tag="CallId" type="cdrfield" value="accid" width="16" />
|
||||
<field tag="Filler" type="filler" width="8" />
|
||||
<field tag="Filler" type="filler" width="8" />
|
||||
<field tag="TerminationCode" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\s\s\s\s\s)"/$1/" width="5" />
|
||||
<field tag="Cost" type="cdrfield" value="cost" padding="zeroleft" width="9" />
|
||||
<field tag="CalledMask" type="cdrfield" value="calledmask" width="1" />
|
||||
</fields>
|
||||
</content>
|
||||
<trailer>
|
||||
<fields>
|
||||
<field tag="TypeOfRecord" type="constant" value="90" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="DistributorCode" type="constant" value="VOI" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="NumberOfRecords" type="metatag" value="cdrs_number" padding="zeroleft" width="6" />
|
||||
<field tag="CdrsDuration" type="metatag" value="cdrs_duration" padding="zeroleft" width="8" />
|
||||
<field tag="FirstCdrTime" type="metatag" value="first_cdr_time" layout="020106150400" width="12" />
|
||||
<field tag="LastCdrTime" type="metatag" value="last_cdr_time" layout="020106150400" width="12" />
|
||||
<field tag="Filler1" type="filler" width="93" />
|
||||
</fields>
|
||||
</trailer>
|
||||
</export_template>
|
||||
</configuration>
|
||||
<configuration section="cdre" type="csv" id="CHECK-CSV1">
|
||||
<export_template>
|
||||
<content>
|
||||
<fields>
|
||||
<field tag="CGRID" type="cdrfield" value="cgrid" width="40"/>
|
||||
<field tag="RatingSubject" type="cdrfield" value="subject" width="24" padding="left" strip="xright" mandatory="true"/>
|
||||
<field tag="Usage" type="cdrfield" value="usage" layout="seconds" width="6" padding="right" mandatory="true"/>
|
||||
<field tag="AccountReference" type="http_post" value="https://localhost:8000" width="10" strip="xright" padding="left" mandatory="true" />
|
||||
<field tag="AccountType" type="http_post" value="https://localhost:8000" width="10" strip="xright" padding="left" mandatory="true" />
|
||||
<field tag="MultipleMed1" type="combimed" value="cost" strip="xright" padding="left" mandatory="true" filter="~mediation_runid:s/DEFAULT/SECOND_RUN/"/>
|
||||
</fields>
|
||||
</content>
|
||||
</export_template>
|
||||
</configuration>
|
||||
|
||||
</document>`
|
||||
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 := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdre" type="fixed_width" id="CDRE-FW2">
|
||||
<cdr_format>fwv</cdr_format>
|
||||
<field_separator>;</field_separator>
|
||||
<data_usage_multiply_factor>1024.0</data_usage_multiply_factor>
|
||||
<cost_multiply_factor>1.19</cost_multiply_factor>
|
||||
<cost_rounding_decimals>-1</cost_rounding_decimals>
|
||||
<cost_shift_digits>-3</cost_shift_digits>
|
||||
<mask_destination_id>MASKED_DESTINATIONS</mask_destination_id>
|
||||
<mask_length>1</mask_length>
|
||||
<export_dir>/var/log/cgrates/cdre</export_dir>
|
||||
<export_template>
|
||||
<header>
|
||||
<fields>
|
||||
<field tag="TypeOfRecord" type="constant" value="10" width="2" />
|
||||
<field tag="LastCdr" type="metatag" value="last_cdr_time" layout="020106150400" width="12" />
|
||||
</fields>
|
||||
</header>
|
||||
<content>
|
||||
<fields>
|
||||
<field tag="OperatorCode" type="cdrfield" value="operator" width="2" />
|
||||
<field tag="ProductId" type="cdrfield" value="productid" width="5" />
|
||||
<field tag="NetworkId" type="constant" value="3" width="1" />
|
||||
<field tag="FromHttpPost1" type="http_post" value="https://localhost:8000" width="10" strip="xright" padding="left" />
|
||||
<field tag="CombiMed1" type="combimed" value="cost" width="10" strip="xright" padding="left" filter="~mediation_runid:s/DEFAULT/SECOND_RUN/"/>
|
||||
</fields>
|
||||
</content>
|
||||
<trailer>
|
||||
<fields>
|
||||
<field tag="DistributorCode" type="constant" value="VOI" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
</fields>
|
||||
</trailer>
|
||||
</export_template>
|
||||
</configuration>
|
||||
</document>`
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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 <csv>
|
||||
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("<element>"), cfgInst.RawConfig...) // Encapsulate the rawConfig in one element so we can Unmarshall into one struct
|
||||
rawConfig = append(rawConfig, []byte("</element>")...)
|
||||
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
|
||||
}
|
||||
@@ -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: <redis>.
|
||||
# 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: <redis>.
|
||||
# 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: <mysql|postgres>
|
||||
# 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: <msgpack|json>
|
||||
# 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: <true|false>.
|
||||
|
||||
[rater]
|
||||
# enabled = false # Enable Rater service: <true|false>.
|
||||
# balancer = # Register to Balancer as worker: <""|internal|127.0.0.1:2013>.
|
||||
|
||||
[scheduler]
|
||||
# enabled = false # Starts Scheduler service: <true|false>.
|
||||
|
||||
[cdrs]
|
||||
# enabled = false # Start the CDR Server service: <true|false>.
|
||||
# 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 <csv>
|
||||
# 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. <internal|127.0.0.1:2080>
|
||||
# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
|
||||
# cdr_format = csv # CDR file format <csv|freeswitch_csv>.
|
||||
# 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: <label_extrafield_1>:<index_extrafield_1>[...,<label_extrafield_n>:<index_extrafield_n>]
|
||||
|
||||
[mediator]
|
||||
# enabled = false # Starts Mediator service: <true|false>.
|
||||
# reconnects = 3 # Number of reconnects to rater/cdrs before giving up.
|
||||
# rater = internal # Address where to reach the Rater: <internal|x.y.z.y:1234>
|
||||
# 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: <true|false>
|
||||
# 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: <true|false>
|
||||
# switch_type = freeswitch # Defines the type of switch behind: <freeswitch>
|
||||
# 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 <true|false>.
|
||||
|
||||
[history_server]
|
||||
# enabled = false # Starts History service: <true|false>.
|
||||
# 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: <true|false>.
|
||||
# server = internal # Address where to reach the master history server: <internal|x.y.z.y:1234>
|
||||
|
||||
[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
|
||||
|
||||
31
data/conf/samples/apier/apier.json
Normal file
31
data/conf/samples/apier/apier.json
Normal file
@@ -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: <true|false>
|
||||
},
|
||||
|
||||
"scheduler": {
|
||||
"enabled": true, // start Scheduler service: <true|false>
|
||||
},
|
||||
|
||||
"cdrs": {
|
||||
"enabled": true, // start the CDR Server service: <true|false>
|
||||
"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: <true|false>.
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
30
data/conf/samples/mediator1/mediator_test1.json
Normal file
30
data/conf/samples/mediator1/mediator_test1.json
Normal file
@@ -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: <true|false>
|
||||
},
|
||||
|
||||
"scheduler": {
|
||||
"enabled": true, // start Scheduler service: <true|false>
|
||||
},
|
||||
|
||||
"cdrs": {
|
||||
"enabled": true, // start the CDR Server service: <true|false>
|
||||
"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: <true|false>.
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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: <true|false>.
|
||||
|
||||
[scheduler]
|
||||
enabled = true # Starts Scheduler service: <true|false>.
|
||||
|
||||
[cdrs]
|
||||
enabled = true # Start the CDR Server service: <true|false>.
|
||||
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: <true|false>.
|
||||
rater = internal # Address where to reach the Rater: <internal|x.y.z.y:1234>
|
||||
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>.
|
||||
|
||||
|
||||
|
||||
125
data/conf/samples/multiplecdrc/multiplecdrc_fwexport.json
Normal file
125
data/conf/samples/multiplecdrc/multiplecdrc_fwexport.json
Normal file
@@ -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: <true|false>
|
||||
},
|
||||
|
||||
"scheduler": {
|
||||
"enabled": true, // start Scheduler service: <true|false>
|
||||
},
|
||||
|
||||
"cdrs": {
|
||||
"enabled": true, // start the CDR Server service: <true|false>
|
||||
"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: <true|false>.
|
||||
},
|
||||
|
||||
"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
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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: <true|false>.
|
||||
|
||||
[scheduler]
|
||||
enabled = true # Starts Scheduler service: <true|false>.
|
||||
|
||||
[cdrs]
|
||||
enabled = true # Start the CDR Server service: <true|false>.
|
||||
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: <true|false>.
|
||||
@@ -1,111 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<configuration section="cdrc" id="CDRC-CSV2">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_type>csv</cdr_type>
|
||||
<field_separator>,</field_separator>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/tmp/cgrates/cdrc2/in</cdr_in_dir>
|
||||
<cdr_out_dir>/tmp/cgrates/cdrc2/out</cdr_out_dir>
|
||||
<cdr_source_id>csv2</cdr_source_id>
|
||||
<fields>
|
||||
<field cdr_field="tor" filter="~7:s/^(voice|data|sms)$/*$1/" />
|
||||
<field cdr_field="accid" filter="0" />
|
||||
<field cdr_field="reqtype" filter="^rated" />
|
||||
<field cdr_field="direction" filter="^*out" />
|
||||
<field cdr_field="tenant" filter="^cgrates.org" />
|
||||
<field cdr_field="category" filter="~7:s/^voice$/call/" />
|
||||
<field cdr_field="account" filter="3" />
|
||||
<field cdr_field="subject" filter="3" />
|
||||
<field cdr_field="destination" filter="~5:s/^0([1-9]\d+)$/+49$1/" />
|
||||
<field cdr_field="setup_time" filter="1" />
|
||||
<field cdr_field="answer_time" filter="1" />
|
||||
<field cdr_field="usage" filter="~9:s/^(\d+)$/${1}s/" />
|
||||
</fields>
|
||||
</configuration>
|
||||
<configuration section="cdrc" id="CDRC-CSV3">
|
||||
<enabled>true</enabled>
|
||||
<cdrs_address>internal</cdrs_address>
|
||||
<cdr_type>csv</cdr_type>
|
||||
<field_separator>;</field_separator>
|
||||
<run_delay>0</run_delay>
|
||||
<cdr_in_dir>/tmp/cgrates/cdrc3/in</cdr_in_dir>
|
||||
<cdr_out_dir>/tmp/cgrates/cdrc3/out</cdr_out_dir>
|
||||
<cdr_source_id>csv3</cdr_source_id>
|
||||
<fields>
|
||||
<field cdr_field="tor" filter="^*voice" />
|
||||
<field cdr_field="accid" filter="~3:s/^(\d{2})\.(\d{2})\.(\d{4})\s{2}(\d{2}):(\d{2}):(\d{2})$/$1$2$3$4$5$6/" />
|
||||
<field cdr_field="reqtype" filter="^rated" />
|
||||
<field cdr_field="direction" filter="^*out" />
|
||||
<field cdr_field="tenant" filter="^cgrates.org" />
|
||||
<field cdr_field="category" filter="^call" />
|
||||
<field cdr_field="account" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="subject" filter="~0:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="destination" filter="~1:s/^([1-9]\d+)$/+$1/" />
|
||||
<field cdr_field="setup_time" filter="4" />
|
||||
<field cdr_field="answer_time" filter="4" />
|
||||
<field cdr_field="usage" filter="~6:s/^(\d+)$/${1}s/" />
|
||||
</fields>
|
||||
</configuration>
|
||||
<configuration section="cdre" type="fwv" id="CDRE-FW1">
|
||||
<cdr_format>fwv</cdr_format>
|
||||
<data_usage_multiply_factor>0.0</data_usage_multiply_factor>
|
||||
<cost_multiply_factor>0.0</cost_multiply_factor>
|
||||
<cost_shift_digits>0</cost_shift_digits>
|
||||
<mask_destination_id>MASKED_DESTINATIONS</mask_destination_id>
|
||||
<mask_length>0</mask_length>
|
||||
<export_dir>/var/log/cgrates/cdre</export_dir>
|
||||
<export_template>
|
||||
<header>
|
||||
<fields>
|
||||
<field tag="ToR" type="constant" value="10" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="FileType" type="constant" value="SIP" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="LastCdr" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="FileCreationfTime" type="metatag" value="time_now" layout="020106150405" width="12" />
|
||||
<field tag="FileVersion" type="constant" value="01" width="2" />
|
||||
<field tag="Filler2" type="filler" width="105" />
|
||||
</fields>
|
||||
</header>
|
||||
<content>
|
||||
<fields>
|
||||
<field tag="ToR" type="constant" value="20" width="2" />
|
||||
<field tag="Subject" type="cdrfield" value="subject" width="12" padding="right" mandatory="true" />
|
||||
<field tag="ConnectionNumber" type="constant" value="00000" width="5" />
|
||||
<field tag="CallerId" type="cdrfield" value="~callerid:s/\+(\d+)/00$1/" strip="xright" width="15" padding="right" />
|
||||
<field 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" />
|
||||
<field tag="TypeOfService" type="constant" value="00" width="2" />
|
||||
<field tag="ServiceId" type="constant" value="11" width="4" padding="right" />
|
||||
<field tag="AnswerTime" type="cdrfield" value="answer_time" layout="020106150405" width="12" mandatory="true" />
|
||||
<field tag="Usage" type="cdrfield" value="usage" layout="seconds" width="6" padding="right" mandatory="true" />
|
||||
<field tag="DataCounter" type="filler" width="6" />
|
||||
<field tag="VatCode" type="constant" value="1" width="1" />
|
||||
<field tag="NetworkId" type="constant" value="S1" width="2" />
|
||||
<field tag="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field tag="NetworkSubtype" type="constant" value="3" width="1" padding="left" />
|
||||
<field tag="CgrId" type="cdrfield" value="cgrid" strip="xleft" width="16" paddingi="right" mandatory="true" />
|
||||
<field tag="FillerVolume1" type="filler" width="8" />
|
||||
<field tag="FillerVolume2" type="filler" width="8" />
|
||||
<field tag="DestinationSubId" type="cdrfield" value="~cost_details:s/"MatchedDestId":".+_(\w{5})"/$1/:s/(\w{6})/$1/" width="5" />
|
||||
<field tag="Cost" type="cdrfield" value="cost" padding="zeroleft" width="9" />
|
||||
<field tag="MaskDestination" type="metatag" value="mask_destination" width="1" />
|
||||
</fields>
|
||||
</content>
|
||||
<trailer>
|
||||
<fields>
|
||||
<field tag="ToR" type="constant" value="90" width="2" />
|
||||
<field tag="Filler1" type="filler" width="3" />
|
||||
<field tag="FileType" type="constant" value="SIP" width="3" />
|
||||
<field tag="FileSeqNr" type="metatag" value="export_id" padding="zeroleft" width="5" />
|
||||
<field tag="TotalRecords" type="metatag" value="cdrs_number" padding="zeroleft" width="6" />
|
||||
<field tag="TotalDuration" type="metatag" value="cdrs_duration" padding="zeroleft" width="8" />
|
||||
<field tag="FirstCdrTime" type="metatag" value="first_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="LastCdrTime" type="metatag" value="last_cdr_atime" layout="020106150405" width="12" />
|
||||
<field tag="Filler1" type="filler" width="93" />
|
||||
</fields>
|
||||
</trailer>
|
||||
</export_template>
|
||||
</configuration>
|
||||
</document>
|
||||
31
data/conf/samples/tutorial/tutorial.json
Normal file
31
data/conf/samples/tutorial/tutorial.json
Normal file
@@ -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: <true|false>
|
||||
},
|
||||
|
||||
"scheduler": {
|
||||
"enabled": true, // start Scheduler service: <true|false>
|
||||
},
|
||||
|
||||
"cdrs": {
|
||||
"enabled": true, // start the CDR Server service: <true|false>
|
||||
"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: <true|false>.
|
||||
"cdrstats": "internal",
|
||||
},
|
||||
|
||||
}
|
||||
@@ -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: <true|false>.
|
||||
|
||||
[scheduler]
|
||||
enabled = true # Starts Scheduler service: <true|false>.
|
||||
|
||||
[cdrs]
|
||||
enabled = true # Start the CDR Server service: <true|false>.
|
||||
mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
|
||||
|
||||
[cdre]
|
||||
cdr_format = csv # Exported CDRs format <csv>
|
||||
export_dir = /tmp # Path where the exported CDRs will be placed
|
||||
|
||||
[mediator]
|
||||
enabled = true # Starts Mediator service: <true|false>.
|
||||
cdrstats = internal
|
||||
|
||||
[cdrstats]
|
||||
enabled = true # Starts the cdrstats service: <true|false>
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -19,17 +19,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
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
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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 <mysql>")
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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",
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <mysql>")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -355,3 +355,7 @@ func BoolPointer(b bool) *bool {
|
||||
func StringSlicePointer(slc []string) *[]string {
|
||||
return &slc
|
||||
}
|
||||
|
||||
func Float64SlicePointer(slc []float64) *[]float64 {
|
||||
return &slc
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user