NEW configuration format - cgrates.json

This commit is contained in:
DanB
2015-01-18 12:54:07 +01:00
parent bf27fa3512
commit 6126b69c4e
55 changed files with 1355 additions and 3834 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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]
}
}

View File

@@ -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"}

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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
View 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/"},
],
},
},
}

View File

@@ -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
}

View File

@@ -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)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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
},
}
}`

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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")
}
}

View File

@@ -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
}

View File

@@ -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])
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"])
}
}

View File

@@ -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/&quot;MatchedDestId&quot;:&quot;.+_(\s\s\s\s\s)&quot;/$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)
}
}

View File

@@ -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
}

View File

@@ -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

View 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>.
},
}

View 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>.
},
}

View File

@@ -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>.

View 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
}
},
}

View File

@@ -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>.

View File

@@ -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/&quot;MatchedDestId&quot;:&quot;.+_(\w{5})&quot;/$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/&quot;MatchedDestId&quot;:&quot;.+_(\w{5})&quot;/$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>

View 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",
},
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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")
}
}
*/

View File

@@ -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())
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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")
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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',

View File

@@ -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

View File

@@ -355,3 +355,7 @@ func BoolPointer(b bool) *bool {
func StringSlicePointer(slc []string) *[]string {
return &slc
}
func Float64SlicePointer(slc []float64) *[]float64 {
return &slc
}

View File

@@ -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