mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Fork cdrc over multiple folders based on xml configuration
This commit is contained in:
@@ -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 {
|
||||
|
||||
53
cdrc/cdrc.go
53
cdrc/cdrc.go
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user