Fork cdrc over multiple folders based on xml configuration

This commit is contained in:
DanB
2014-05-21 17:39:25 +02:00
parent 201b7e7677
commit ad4fedc197
13 changed files with 157 additions and 107 deletions

View File

@@ -130,10 +130,8 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
}
exportTemplate := self.Config.CdreFWXmlTemplate
if len(attr.ExportTemplate) != 0 && self.Config.XmlCfgDocument != nil {
if xmlTemplate, err := self.Config.XmlCfgDocument.GetCdreFWCfg(attr.ExportTemplate[len(utils.XML_PROFILE_PREFIX):]); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
} else if xmlTemplate != nil {
exportTemplate = xmlTemplate
if xmlTemplate := self.Config.XmlCfgDocument.GetCdreFWCfgs(attr.ExportTemplate[len(utils.XML_PROFILE_PREFIX):]); xmlTemplate != nil {
exportTemplate = xmlTemplate[attr.ExportTemplate[len(utils.XML_PROFILE_PREFIX):]]
}
}
if exportTemplate == nil {

View File

@@ -31,7 +31,6 @@ import (
"time"
"github.com/cgrates/cgrates/cdrs"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/howeyc/fsnotify"
@@ -42,12 +41,13 @@ const (
FS_CSV = "freeswitch_csv"
)
func NewCdrc(config *config.CGRConfig, cdrServer *cdrs.CDRS) (*Cdrc, error) {
cdrc := &Cdrc{cgrCfg: config, cdrServer: cdrServer}
func NewCdrc(cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, cdrFields map[string]*utils.RSRField, cdrServer *cdrs.CDRS) (*Cdrc, error) {
cdrc := &Cdrc{cdrsAddress: cdrsAddress, cdrType: cdrType, cdrInDir: cdrInDir, cdrOutDir: cdrOutDir,
cdrSourceId: cdrSourceId, runDelay: runDelay, cdrFields: cdrFields, cdrServer: cdrServer}
// Before processing, make sure in and out folders exist
for _, dir := range []string{cdrc.cgrCfg.CdrcCdrInDir, cdrc.cgrCfg.CdrcCdrOutDir} {
for _, dir := range []string{cdrc.cdrInDir, cdrc.cdrOutDir} {
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
return nil, fmt.Errorf("Folder %s does not exist", dir)
return nil, fmt.Errorf("Inexistent folder: %s", dir)
}
}
cdrc.httpClient = new(http.Client)
@@ -55,31 +55,36 @@ func NewCdrc(config *config.CGRConfig, cdrServer *cdrs.CDRS) (*Cdrc, error) {
}
type Cdrc struct {
cgrCfg *config.CGRConfig
cdrServer *cdrs.CDRS
cfgCdrFields map[string]string // Key is the name of the field
httpClient *http.Client
cdrsAddress,
cdrType,
cdrInDir,
cdrOutDir,
cdrSourceId string
runDelay time.Duration
cdrFields map[string]*utils.RSRField
cdrServer *cdrs.CDRS // Reference towards internal cdrServer if that is the case
httpClient *http.Client
}
// When called fires up folder monitoring, either automated via inotify or manual by sleeping between processing
func (self *Cdrc) Run() error {
if self.cgrCfg.CdrcRunDelay == time.Duration(0) { // Automated via inotify
if self.runDelay == time.Duration(0) { // Automated via inotify
return self.trackCDRFiles()
}
// No automated, process and sleep approach
for {
self.processCdrDir()
time.Sleep(self.cgrCfg.CdrcRunDelay)
time.Sleep(self.runDelay)
}
}
// Takes the record out of csv and turns it into http form which can be posted
func (self *Cdrc) recordForkCdr(record []string) (*utils.StoredCdr, error) {
storedCdr := &utils.StoredCdr{TOR: utils.VOICE, CdrSource: self.cgrCfg.CdrcSourceId, ExtraFields: make(map[string]string), Cost: -1}
storedCdr := &utils.StoredCdr{TOR: utils.VOICE, CdrSource: self.cdrSourceId, ExtraFields: make(map[string]string), Cost: -1}
var err error
for cfgFieldName, cfgFieldRSR := range self.cgrCfg.CdrcCdrFields {
for cfgFieldName, cfgFieldRSR := range self.cdrFields {
var fieldVal string
if utils.IsSliceMember([]string{CSV, FS_CSV}, self.cgrCfg.CdrcCdrType) {
if utils.IsSliceMember([]string{CSV, FS_CSV}, self.cdrType) {
if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cfgFieldName)
} else {
@@ -128,11 +133,11 @@ func (self *Cdrc) recordForkCdr(record []string) (*utils.StoredCdr, error) {
// One run over the CDR folder
func (self *Cdrc) processCdrDir() error {
engine.Logger.Info(fmt.Sprintf("<Cdrc> Parsing folder %s for CDR files.", self.cgrCfg.CdrcCdrInDir))
filesInDir, _ := ioutil.ReadDir(self.cgrCfg.CdrcCdrInDir)
engine.Logger.Info(fmt.Sprintf("<Cdrc> Parsing folder %s for CDR files.", self.cdrInDir))
filesInDir, _ := ioutil.ReadDir(self.cdrInDir)
for _, file := range filesInDir {
if self.cgrCfg.CdrcCdrType != FS_CSV || path.Ext(file.Name()) != ".csv" {
if err := self.processFile(path.Join(self.cgrCfg.CdrcCdrInDir, file.Name())); err != nil {
if self.cdrType != FS_CSV || path.Ext(file.Name()) != ".csv" {
if err := self.processFile(path.Join(self.cdrInDir, file.Name())); err != nil {
return err
}
}
@@ -147,15 +152,15 @@ func (self *Cdrc) trackCDRFiles() (err error) {
return
}
defer watcher.Close()
err = watcher.Watch(self.cgrCfg.CdrcCdrInDir)
err = watcher.Watch(self.cdrInDir)
if err != nil {
return
}
engine.Logger.Info(fmt.Sprintf("<Cdrc> Monitoring %s for file moves.", self.cgrCfg.CdrcCdrInDir))
engine.Logger.Info(fmt.Sprintf("<Cdrc> Monitoring %s for file moves.", self.cdrInDir))
for {
select {
case ev := <-watcher.Event:
if ev.IsCreate() && (self.cgrCfg.CdrcCdrType != FS_CSV || path.Ext(ev.Name) != ".csv") {
if ev.IsCreate() && (self.cdrType != FS_CSV || path.Ext(ev.Name) != ".csv") {
if err = self.processFile(ev.Name); err != nil {
engine.Logger.Err(fmt.Sprintf("Processing file %s, error: %s", ev.Name, err.Error()))
}
@@ -190,20 +195,20 @@ func (self *Cdrc) processFile(filePath string) error {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Error in csv file: %s", err.Error()))
continue
}
if self.cgrCfg.CdrcCdrs == utils.INTERNAL {
if self.cdrsAddress == utils.INTERNAL {
if err := self.cdrServer.ProcessRawCdr(storedCdr); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Failed posting CDR, error: %s", err.Error()))
continue
}
} else { // CDRs listening on IP
if _, err := self.httpClient.PostForm(fmt.Sprintf("http://%s/cgr", self.cgrCfg.HTTPListen), storedCdr.AsHttpForm()); err != nil {
if _, err := self.httpClient.PostForm(fmt.Sprintf("http://%s/cgr", self.cdrsAddress), storedCdr.AsHttpForm()); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Failed posting CDR, error: %s", err.Error()))
continue
}
}
}
// Finished with file, move it to processed folder
newPath := path.Join(self.cgrCfg.CdrcCdrOutDir, fn)
newPath := path.Join(self.cdrOutDir, fn)
if err := os.Rename(filePath, newPath); err != nil {
engine.Logger.Err(err.Error())
return err

View File

@@ -144,7 +144,8 @@ func TestProcessCdrDir(t *testing.T) {
if err := startEngine(); err != nil {
t.Fatal(err.Error())
}
cdrc, err := NewCdrc(cfg, nil)
cdrc, err := NewCdrc(cfg.CdrcCdrs, cfg.CdrcCdrType, cfg.CdrcCdrInDir, cfg.CdrcCdrOutDir, cfg.CdrcSourceId, cfg.CdrcRunDelay,
cfg.CdrcCdrFields, nil)
if err != nil {
t.Fatal(err.Error())
}

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package cdrc
import (
"github.com/cgrates/cgrates/cdrs"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"reflect"
@@ -29,7 +30,8 @@ import (
func TestRecordForkCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cgrConfig.CdrcCdrFields["supplier"] = &utils.RSRField{Id: "11"}
cdrc := &Cdrc{cgrCfg: cgrConfig}
cdrc := &Cdrc{cgrConfig.CdrcCdrs, cgrConfig.CdrcCdrType, cgrConfig.CdrcCdrInDir, cgrConfig.CdrcCdrOutDir, cgrConfig.CdrcSourceId, cgrConfig.CdrcRunDelay,
cgrConfig.CdrcCdrFields, new(cdrs.CDRS), nil}
cdrRow := []string{"firstField", "secondField"}
_, err := cdrc.recordForkCdr(cdrRow)
if err == nil {

View File

@@ -123,11 +123,12 @@ func startMediator(responder *engine.Responder, loggerDb engine.LogStorage, cdrD
close(chanDone)
}
func startCdrc(cdrsChan chan struct{}) {
if cfg.CdrcCdrs == utils.INTERNAL {
// Fires up a cdrc instance
func startCdrc(cdrsChan chan struct{}, cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, cdrFields map[string]*utils.RSRField) {
if cdrsAddress == utils.INTERNAL {
<-cdrsChan // Wait for CDRServer to come up before start processing
}
cdrc, err := cdrc.NewCdrc(cfg, cdrServer)
cdrc, err := cdrc.NewCdrc(cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId, runDelay, cdrFields, cdrServer)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("Cdrc config parsing error: %s", err.Error()))
exitChan <- true
@@ -452,10 +453,23 @@ func main() {
// close all sessions on shutdown
go shutdownSessionmanagerSingnalHandler()
}
if cfg.CdrcEnabled {
var cdrcEnabled bool
if cfg.CdrcEnabled { // Start default cdrc configured in csv here
cdrcEnabled = true
go startCdrc(cdrsChan, cfg.CdrcCdrs, cfg.CdrcCdrType, cfg.CdrcCdrInDir, cfg.CdrcCdrOutDir, cfg.CdrcSourceId, cfg.CdrcRunDelay, cfg.CdrcCdrFields)
}
if cfg.XmlCfgDocument != nil {
for _, xmlCdrc := range cfg.XmlCfgDocument.GetCdrcCfgs("") {
if !xmlCdrc.Enabled {
continue
}
cdrcEnabled = true
go startCdrc(cdrsChan, xmlCdrc.CdrsAddress, xmlCdrc.CdrType, xmlCdrc.CdrInDir,
xmlCdrc.CdrOutDir, xmlCdrc.CdrSourceId, time.Duration(xmlCdrc.RunDelay), xmlCdrc.CdrRSRFields())
}
}
if cdrcEnabled {
engine.Logger.Info("Starting CGRateS CDR client.")
go startCdrc(cdrsChan)
}
// Start the servers

View File

@@ -438,10 +438,8 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
cfg.CdreExportedFields = extraFields
}
} else if strings.HasPrefix(exportTemplate, utils.XML_PROFILE_PREFIX) {
if xmlTemplate, err := cfg.XmlCfgDocument.GetCdreFWCfg(exportTemplate[len(utils.XML_PROFILE_PREFIX):]); err != nil {
return nil, err
} else {
cfg.CdreFWXmlTemplate = xmlTemplate
if xmlTemplate := cfg.XmlCfgDocument.GetCdreFWCfgs(exportTemplate[len(utils.XML_PROFILE_PREFIX):]); xmlTemplate != nil {
cfg.CdreFWXmlTemplate = xmlTemplate[exportTemplate[len(utils.XML_PROFILE_PREFIX):]]
}
}
}

View File

@@ -39,9 +39,7 @@ func TestLoadXmlCfg(t *testing.T) {
if cfg.XmlCfgDocument == nil {
t.Error("Did not load the XML Config Document")
}
if cdreFWCfg, err := cfg.XmlCfgDocument.GetCdreFWCfg("CDREFW-A"); err != nil {
t.Error(err)
} else if cdreFWCfg == nil {
if cdreFWCfg := cfg.XmlCfgDocument.GetCdreFWCfgs("CDREFW-A"); cdreFWCfg == nil {
t.Error("Could not retrieve CDRExporter FixedWidth config instance")
}
}

View File

@@ -24,26 +24,33 @@ import (
)
type CgrXmlCdrcCfg struct {
Enabled bool `xml:"enabled"` // Enable/Disable the
CdrsAddress string `xml:"cdrs_address"` // The address where CDRs can be reached
CdrsMethod string `xml:"cdrs_method"` // Method to use when posting CDRs
CdrType string `xml:"cdr_type"` // The type of CDR to process <csv>
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 []CdrcField `xml:"fields>field"`
Enabled bool `xml:"enabled"` // Enable/Disable the
CdrsAddress string `xml:"cdrs_address"` // The address where CDRs can be reached
CdrType string `xml:"cdr_type"` // The type of CDR to process <csv>
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 []*CdrcField `xml:"fields>field"`
}
func (cdrcCfg *CgrXmlCdrcCfg) CdrRSRFields() map[string]*utils.RSRField {
rsrFields := make(map[string]*utils.RSRField)
for _, fld := range cdrcCfg.CdrFields {
rsrFields[fld.Id] = fld.rsrField
}
return rsrFields
}
type CdrcField struct {
XMLName xml.Name `xml:"field"`
Id string `xml:"id,attr"`
Filter string `xml:"filter,attr"`
RSRField *utils.RSRField
rsrField *utils.RSRField
}
func (cdrcFld *CdrcField) PopulateRSRFIeld() (err error) {
if cdrcFld.RSRField, err = utils.NewRSRField(cdrcFld.Filter); err != nil {
func (cdrcFld *CdrcField) PopulateRSRField() (err error) {
if cdrcFld.rsrField, err = utils.NewRSRField(cdrcFld.Filter); err != nil {
return err
}
return nil

View File

@@ -30,15 +30,15 @@ var cfgDocCdrc *CgrXmlCfgDocument // Will be populated by first test
func TestPopulateRSRFIeld(t *testing.T) {
cdrcField := CdrcField{Id: "TEST1", Filter: `~effective_caller_id_number:s/(\d+)/+$1/`}
if err := cdrcField.PopulateRSRFIeld(); err != nil {
if err := cdrcField.PopulateRSRField(); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if cdrcField.RSRField == nil {
} else if cdrcField.rsrField == nil {
t.Error("Failed loading the RSRField")
}
cdrcField = CdrcField{Id: "TEST2", Filter: `1`}
if err := cdrcField.PopulateRSRFIeld(); err != nil {
cdrcField = CdrcField{Id: "TEST2", Filter: `99`}
if err := cdrcField.PopulateRSRField(); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if cdrcField.RSRField == nil {
} else if cdrcField.rsrField == nil {
t.Error("Failed loading the RSRField")
}
}
@@ -49,7 +49,6 @@ func TestParseXmlCdrcConfig(t *testing.T) {
<configuration section="cdrc" type="csv" id="CDRC-CSV1">
<enabled>true</enabled>
<cdrs_address>internal</cdrs_address>
<cdrs_method>http_cgr</cdrs_method>
<cdr_type>csv</cdr_type>
<run_delay>0</run_delay>
<cdr_in_dir>/var/log/cgrates/cdrc/in</cdr_in_dir>
@@ -84,33 +83,57 @@ func TestParseXmlCdrcConfig(t *testing.T) {
}
}
func TestGetCdrcCfg(t *testing.T) {
cdrcfg, err := cfgDocCdrc.GetCdrcCfg("CDRC-CSV1")
if err != nil {
t.Error("Unexpected error: ", err)
} else if cdrcfg == nil {
func TestGetCdrcCfgs(t *testing.T) {
cdrcfgs := cfgDocCdrc.GetCdrcCfgs("CDRC-CSV1")
if cdrcfgs == nil {
t.Error("No config instance returned")
}
expectCdrc := &CgrXmlCdrcCfg{Enabled: true, CdrsAddress: "internal", CdrsMethod: "http_cgr", CdrType: "csv",
expectCdrc := &CgrXmlCdrcCfg{Enabled: true, CdrsAddress: "internal", CdrType: "csv",
RunDelay: 0, CdrInDir: "/var/log/cgrates/cdrc/in", CdrOutDir: "/var/log/cgrates/cdrc/out", CdrSourceId: "freeswitch_csv"}
cdrFlds := []CdrcField{CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCID, Filter: "0"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.REQTYPE, Filter: "1"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.DIRECTION, Filter: "2"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.TENANT, Filter: "3"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.CATEGORY, Filter: "4"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCOUNT, Filter: "5"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.SUBJECT, Filter: "6"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.DESTINATION, Filter: "7"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.SETUP_TIME, Filter: "8"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ANSWER_TIME, Filter: "9"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.USAGE, Filter: "10"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr1", Filter: "11"},
CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr2", Filter: "12"}}
cdrFlds := []*CdrcField{
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCID, Filter: "0"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.REQTYPE, Filter: "1"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.DIRECTION, Filter: "2"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.TENANT, Filter: "3"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.CATEGORY, Filter: "4"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCOUNT, Filter: "5"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.SUBJECT, Filter: "6"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.DESTINATION, Filter: "7"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.SETUP_TIME, Filter: "8"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ANSWER_TIME, Filter: "9"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.USAGE, Filter: "10"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr1", Filter: "11"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr2", Filter: "12"}}
for _, fld := range cdrFlds {
fld.PopulateRSRFIeld()
fld.PopulateRSRField()
}
expectCdrc.CdrFields = cdrFlds
if !reflect.DeepEqual(expectCdrc, cdrcfg) {
t.Errorf("Expecting: %v, received: %v", expectCdrc, cdrcfg)
if !reflect.DeepEqual(expectCdrc, cdrcfgs["CDRC-CSV1"]) {
t.Errorf("Expecting: %v, received: %v", expectCdrc, cdrcfgs["CDRC-CSV1"])
}
}
func TestCdrRSRFields(t *testing.T) {
cdrcfgs := cfgDocCdrc.GetCdrcCfgs("CDRC-CSV1")
if cdrcfgs == nil {
t.Error("No config instance returned")
}
eRSRFields := map[string]*utils.RSRField{
utils.ACCID: &utils.RSRField{Id: "0"},
utils.REQTYPE: &utils.RSRField{Id: "1"},
utils.DIRECTION: &utils.RSRField{Id: "2"},
utils.TENANT: &utils.RSRField{Id: "3"},
utils.CATEGORY: &utils.RSRField{Id: "4"},
utils.ACCOUNT: &utils.RSRField{Id: "5"},
utils.SUBJECT: &utils.RSRField{Id: "6"},
utils.DESTINATION: &utils.RSRField{Id: "7"},
utils.SETUP_TIME: &utils.RSRField{Id: "8"},
utils.ANSWER_TIME: &utils.RSRField{Id: "9"},
utils.USAGE: &utils.RSRField{Id: "10"},
"extr1": &utils.RSRField{Id: "11"},
"extr2": &utils.RSRField{Id: "12"},
}
if rsrFields := cdrcfgs["CDRC-CSV1"].CdrRSRFields(); !reflect.DeepEqual(rsrFields, eRSRFields) {
t.Errorf("Expecting: %v, received: %v", eRSRFields, rsrFields)
}
}

View File

@@ -93,19 +93,17 @@ func TestParseXmlConfig(t *testing.T) {
}
func TestGetCdreFWCfg(t *testing.T) {
cdreFWCfg, err := cfgDoc.GetCdreFWCfg("CDRE-FW1")
if err != nil {
t.Error(err)
} else if cdreFWCfg == nil {
cdreFWCfg := cfgDoc.GetCdreFWCfgs("CDRE-FW1")
if cdreFWCfg == nil {
t.Error("Could not parse CdreFw instance")
}
if len(cdreFWCfg.Header.Fields) != 8 {
t.Error("Unexpected number of header fields parsed", len(cdreFWCfg.Header.Fields))
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.Content.Fields) != 20 {
t.Error("Unexpected number of content fields parsed", len(cdreFWCfg.Content.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.Trailer.Fields) != 9 {
t.Error("Unexpected number of trailer fields parsed", len(cdreFWCfg.Trailer.Fields))
if len(cdreFWCfg["CDRE-FW1"].Trailer.Fields) != 9 {
t.Error("Unexpected number of trailer fields parsed", len(cdreFWCfg["CDRE-FW1"].Trailer.Fields))
}
}

View File

@@ -106,7 +106,7 @@ func (xmlCfg *CgrXmlCfgDocument) cacheCdrcCfgs() error {
}
// Cache rsr fields
for _, fld := range cdrcCfg.CdrFields {
if err := fld.PopulateRSRFIeld(); err != nil {
if err := fld.PopulateRSRField(); err != nil {
return fmt.Errorf("Populating field %s, error: %s", fld.Id, err.Error())
}
}
@@ -115,18 +115,26 @@ func (xmlCfg *CgrXmlCfgDocument) cacheCdrcCfgs() error {
return nil
}
func (xmlCfg *CgrXmlCfgDocument) GetCdreFWCfg(instName string) (*CgrXmlCdreFwCfg, error) {
if cfg, hasIt := xmlCfg.cdrefws[instName]; !hasIt {
return nil, nil
} else {
return cfg, nil
// Return instances or filtered instance of cdrefw configuration
func (xmlCfg *CgrXmlCfgDocument) GetCdreFWCfgs(instName string) map[string]*CgrXmlCdreFwCfg {
if len(instName) != 0 {
if cfg, hasIt := xmlCfg.cdrefws[instName]; !hasIt {
return nil
} else {
return map[string]*CgrXmlCdreFwCfg{instName: cfg}
}
}
return xmlCfg.cdrefws
}
func (xmlCfg *CgrXmlCfgDocument) GetCdrcCfg(instName string) (*CgrXmlCdrcCfg, error) {
if cfg, hasIt := xmlCfg.cdrcs[instName]; !hasIt {
return nil, nil
} else {
return cfg, nil
// 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

@@ -32,7 +32,7 @@
# 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_method = *middle # Rounding method for floats/costs: <*up|*middle|*down>
# rounding_decimals = 4 # Number of decimals to round float/costs at
# rounding_decimals = 10 # Number of decimals to round float/costs at
# xmlcfg_path = # Path towards additional config defined in xml file
[balancer]
@@ -61,7 +61,6 @@
[cdrc]
# enabled = false # Enable CDR client functionality
# cdrs = internal # Address where to reach CDR server. <internal|127.0.0.1:2080>
# cdrs_method = http_cgr # Mechanism to use when posting CDRs on server <http_cgr>
# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
# cdr_type = csv # CDR file format <csv|freeswitch_csv>.
# cdr_in_dir = /var/log/cgrates/cdrc/in # Absolute path towards the directory where the CDRs are stored.

View File

@@ -58,7 +58,6 @@ export_dir = /tmp/cgrates/cdre # Path where the exported CDRs will be placed
[cdrc]
enabled = true # Enable CDR client functionality
# cdrs = internal # Address where to reach CDR server. <internal|127.0.0.1:2080>
# cdrs_method = http_cgr # Mechanism to use when posting CDRs on server <http_cgr>
# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify
cdr_type = freeswitch_csv # CDR file format <csv>.
cdr_in_dir = /tmp/cgrates/cdr/cdrc/in # Absolute path towards the directory where the CDRs are stored.