mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Adding xml configuration for cdrc
This commit is contained in:
50
config/xmlcdrc.go
Normal file
50
config/xmlcdrc.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type CdrcField struct {
|
||||
XMLName xml.Name `xml:"field"`
|
||||
Id string `xml:"id,attr"`
|
||||
Filter string `xml:"filter,attr"`
|
||||
RSRField *utils.RSRField
|
||||
}
|
||||
|
||||
func (cdrcFld *CdrcField) PopulateRSRFIeld() (err error) {
|
||||
if cdrcFld.RSRField, err = utils.NewRSRField(cdrcFld.Filter); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
116
config/xmlcdrc_test.go
Normal file
116
config/xmlcdrc_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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 TestPopulateRSRFIeld(t *testing.T) {
|
||||
cdrcField := CdrcField{Id: "TEST1", Filter: `~effective_caller_id_number:s/(\d+)/+$1/`}
|
||||
if err := cdrcField.PopulateRSRFIeld(); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if cdrcField.RSRField == nil {
|
||||
t.Error("Failed loading the RSRField")
|
||||
}
|
||||
cdrcField = CdrcField{Id: "TEST2", Filter: `1`}
|
||||
if err := cdrcField.PopulateRSRFIeld(); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if cdrcField.RSRField == nil {
|
||||
t.Error("Failed loading the RSRField")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseXmlCdrcConfig(t *testing.T) {
|
||||
cfgXmlStr := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="cgrates/xml">
|
||||
<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>
|
||||
<cdr_out_dir>/var/log/cgrates/cdrc/out</cdr_out_dir>
|
||||
<cdr_source_id>freeswitch_csv</cdr_source_id>
|
||||
<fields>
|
||||
<field id="accid" filter="0" />
|
||||
<field id="reqtype" filter="1" />
|
||||
<field id="direction" filter="2" />
|
||||
<field id="tenant" filter="3" />
|
||||
<field id="category" filter="4" />
|
||||
<field id="account" filter="5" />
|
||||
<field id="subject" filter="6" />
|
||||
<field id="destination" filter="7" />
|
||||
<field id="setup_time" filter="8" />
|
||||
<field id="answer_time" filter="9" />
|
||||
<field id="usage" filter="10" />
|
||||
<field id="extr1" filter="11" />
|
||||
<field id="extr2" filter="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 TestGetCdrcCfg(t *testing.T) {
|
||||
cdrcfg, err := cfgDocCdrc.GetCdrcCfg("CDRC-CSV1")
|
||||
if err != nil {
|
||||
t.Error("Unexpected error: ", err)
|
||||
} else if cdrcfg == nil {
|
||||
t.Error("No config instance returned")
|
||||
}
|
||||
expectCdrc := &CgrXmlCdrcCfg{Enabled: true, CdrsAddress: "internal", CdrsMethod: "http_cgr", 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"}}
|
||||
for _, fld := range cdrFlds {
|
||||
fld.PopulateRSRFIeld()
|
||||
}
|
||||
expectCdrc.CdrFields = cdrFlds
|
||||
if !reflect.DeepEqual(expectCdrc, cdrcfg) {
|
||||
t.Errorf("Expecting: %v, received: %v", expectCdrc, cdrcfg)
|
||||
}
|
||||
}
|
||||
61
config/xmlcdre.go
Normal file
61
config/xmlcdre.go
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// The CdrExporter Fixed Width configuration instance
|
||||
type CgrXmlCdreFwCfg struct {
|
||||
Header *CgrXmlCfgCdrHeader `xml:"header"`
|
||||
Content *CgrXmlCfgCdrContent `xml:"content"`
|
||||
Trailer *CgrXmlCfgCdrTrailer `xml:"trailer"`
|
||||
}
|
||||
|
||||
// CDR header
|
||||
type CgrXmlCfgCdrHeader struct {
|
||||
XMLName xml.Name `xml:"header"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR content
|
||||
type CgrXmlCfgCdrContent struct {
|
||||
XMLName xml.Name `xml:"content"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR trailer
|
||||
type CgrXmlCfgCdrTrailer struct {
|
||||
XMLName xml.Name `xml:"trailer"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR field
|
||||
type CgrXmlCfgCdrField struct {
|
||||
XMLName xml.Name `xml:"field"`
|
||||
Name string `xml:"name,attr"`
|
||||
Type string `xml:"type,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
|
||||
Mandatory bool `xml:"mandatory,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 ITsysCOM
|
||||
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
|
||||
@@ -87,15 +87,7 @@ func TestParseXmlConfig(t *testing.T) {
|
||||
} else if cfgDoc == nil {
|
||||
t.Fatal("Could not parse xml configuration document")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheCdreFWCfgs(t *testing.T) {
|
||||
if len(cfgDoc.cdrefws) != 0 {
|
||||
t.Error("Cache should be empty before caching")
|
||||
}
|
||||
if err := cfgDoc.cacheCdreFWCfgs(); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(cfgDoc.cdrefws) != 1 {
|
||||
if len(cfgDoc.cdrefws) != 1 {
|
||||
t.Error("Did not cache")
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 ITsysCOM
|
||||
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
|
||||
@@ -32,6 +32,9 @@ func ParseCgrXmlConfig(reader io.Reader) (*CgrXmlCfgDocument, error) {
|
||||
if err := decoder.Decode(xmlConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := xmlConfig.cacheAll(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xmlConfig, nil
|
||||
}
|
||||
|
||||
@@ -41,6 +44,7 @@ type CgrXmlCfgDocument struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Configurations []*CgrXmlConfiguration `xml:"configuration"`
|
||||
cdrefws map[string]*CgrXmlCdreFwCfg // Cache for processed fixed width config instances, key will be the id of the instance
|
||||
cdrcs map[string]*CgrXmlCdrcCfg
|
||||
}
|
||||
|
||||
// Storage for raw configuration
|
||||
@@ -52,42 +56,19 @@ type CgrXmlConfiguration struct {
|
||||
RawConfig []byte `xml:",innerxml"` // Used to store the configuration struct, as raw so we can store different types
|
||||
}
|
||||
|
||||
// The CdrExporter Fixed Width configuration instance
|
||||
type CgrXmlCdreFwCfg struct {
|
||||
Header *CgrXmlCfgCdrHeader `xml:"header"`
|
||||
Content *CgrXmlCfgCdrContent `xml:"content"`
|
||||
Trailer *CgrXmlCfgCdrTrailer `xml:"trailer"`
|
||||
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
|
||||
}
|
||||
|
||||
// CDR header
|
||||
type CgrXmlCfgCdrHeader struct {
|
||||
XMLName xml.Name `xml:"header"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR content
|
||||
type CgrXmlCfgCdrContent struct {
|
||||
XMLName xml.Name `xml:"content"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR trailer
|
||||
type CgrXmlCfgCdrTrailer struct {
|
||||
XMLName xml.Name `xml:"trailer"`
|
||||
Fields []*CgrXmlCfgCdrField `xml:"fields>field"`
|
||||
}
|
||||
|
||||
// CDR field
|
||||
type CgrXmlCfgCdrField struct {
|
||||
XMLName xml.Name `xml:"field"`
|
||||
Name string `xml:"name,attr"`
|
||||
Type string `xml:"type,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
|
||||
Mandatory bool `xml:"mandatory,attr"` // If field is mandatory, empty value will be considered as error and CDR will not be exported
|
||||
func (xmlCfg *CgrXmlCfgDocument) cacheAll() error {
|
||||
for _, cacheFunc := range []func() error{xmlCfg.cacheCdreFWCfgs, xmlCfg.cacheCdrcCfgs} {
|
||||
if err := cacheFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid building from raw config string always, so build cache here
|
||||
@@ -110,14 +91,42 @@ func (xmlCfg *CgrXmlCfgDocument) cacheCdreFWCfgs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (xmlCfg *CgrXmlCfgDocument) GetCdreFWCfg(instName string) (*CgrXmlCdreFwCfg, error) {
|
||||
if len(xmlCfg.cdrefws) == 0 { // First time, cache also
|
||||
if err := xmlCfg.cacheCdreFWCfgs(); err != nil {
|
||||
return nil, err
|
||||
// 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)
|
||||
}
|
||||
// Cache rsr fields
|
||||
for _, fld := range cdrcCfg.CdrFields {
|
||||
if err := fld.PopulateRSRFIeld(); err != nil {
|
||||
return fmt.Errorf("Populating field %s, error: %s", fld.Id, err.Error())
|
||||
}
|
||||
}
|
||||
xmlCfg.cdrcs[cfgInst.Id] = cdrcCfg
|
||||
}
|
||||
if cfg, hasIt := xmlCfg.cdrefws[instName]; hasIt {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (xmlCfg *CgrXmlCfgDocument) GetCdreFWCfg(instName string) (*CgrXmlCdreFwCfg, error) {
|
||||
if cfg, hasIt := xmlCfg.cdrefws[instName]; !hasIt {
|
||||
return nil, nil
|
||||
} else {
|
||||
return cfg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (xmlCfg *CgrXmlCfgDocument) GetCdrcCfg(instName string) (*CgrXmlCdrcCfg, error) {
|
||||
if cfg, hasIt := xmlCfg.cdrcs[instName]; !hasIt {
|
||||
return nil, nil
|
||||
} else {
|
||||
return cfg, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ const (
|
||||
CDRE_FIXED_WIDTH = "fwv"
|
||||
XML_PROFILE_PREFIX = "*xml:"
|
||||
CDRE = "cdre"
|
||||
CDRC = "cdrc"
|
||||
MASK_CHAR = "*"
|
||||
CONCATENATED_KEY_SEP = ":"
|
||||
META_DEFAULT = "*default"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 ITsysCOM
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 ITsysCOM
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user