Adding xml configuration for cdrc

This commit is contained in:
DanB
2014-05-20 13:41:02 +02:00
parent 5aef6595a2
commit cb33f01695
8 changed files with 286 additions and 57 deletions

50
config/xmlcdrc.go Normal file
View 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
View 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
View 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
}

View File

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

View File

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

View File

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

View File

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

View File

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