Refactoring configuration for DerivedCharging

This commit is contained in:
DanB
2014-04-22 13:13:39 +02:00
parent d3e5555b5c
commit bba4a878f0
10 changed files with 340 additions and 329 deletions

View File

@@ -123,17 +123,6 @@ type CGRConfig struct {
SMRaterReconnects int // Number of reconnect attempts to rater
SMDebitInterval int // the period to be debited in advanced during a call (in seconds)
SMMaxCallDuration time.Duration // The maximum duration of a call
SMRunIds []string // Identifiers of additional sessions control.
SMReqTypeFields []string // Name of request type fields to be used during additional sessions control <""|*default|field_name>.
SMDirectionFields []string // Name of direction fields to be used during additional sessions control <""|*default|field_name>.
SMTenantFields []string // Name of tenant fields to be used during additional sessions control <""|*default|field_name>.
SMTORFields []string // Name of tor fields to be used during additional sessions control <""|*default|field_name>.
SMAccountFields []string // Name of account fields to be used during additional sessions control <""|*default|field_name>.
SMSubjectFields []string // Name of fields to be used during additional sessions control <""|*default|field_name>.
SMDestFields []string // Name of destination fields to be used during additional sessions control <""|*default|field_name>.
SMSetupTimeFields []string // Name of setup_time fields to be used during additional sessions control <""|*default|field_name>.
SMAnswerTimeFields []string // Name of answer_time fields to be used during additional sessions control <""|*default|field_name>.
SMDurationFields []string // Name of duration fields to be used during additional sessions control <""|*default|field_name>.
MediatorEnabled bool // Starts Mediator service: <true|false>.
MediatorRater string // Address where to reach the Rater: <internal|x.y.z.y:1234>
MediatorRaterReconnects int // Number of reconnects to rater before giving up.
@@ -148,7 +137,7 @@ type CGRConfig struct {
MediatorSetupTimeFields []string // Name of setup_time fields to be used during mediation. Use index numbers in case of .csv cdrs.
MediatorAnswerTimeFields []string // Name of answer_time fields to be used during mediation. Use index numbers in case of .csv cdrs.
MediatorDurationFields []string // Name of duration fields to be used during mediation. Use index numbers in case of .csv cdrs.
DerivedChargers DerivedChargers // System wide pseudosessions which will be executed in case of no particular ones defined per account
DerivedChargers DerivedChargers // System wide derived chargers, added to the account level ones
FreeswitchServer string // freeswitch address host:port
FreeswitchPass string // FS socket password
FreeswitchReconnects int // number of times to attempt reconnect after connect fails
@@ -228,17 +217,6 @@ func (self *CGRConfig) setDefaults() error {
self.MediatorEnabled = false
self.MediatorRater = "internal"
self.MediatorRaterReconnects = 3
self.MediatorRunIds = []string{}
self.MediatorSubjectFields = []string{}
self.MediatorReqTypeFields = []string{}
self.MediatorDirectionFields = []string{}
self.MediatorTenantFields = []string{}
self.MediatorTORFields = []string{}
self.MediatorAccountFields = []string{}
self.MediatorDestFields = []string{}
self.MediatorSetupTimeFields = []string{}
self.MediatorAnswerTimeFields = []string{}
self.MediatorDurationFields = []string{}
self.DerivedChargers = make(DerivedChargers, 0)
self.SMEnabled = false
self.SMSwitchType = FS
@@ -246,17 +224,6 @@ func (self *CGRConfig) setDefaults() error {
self.SMRaterReconnects = 3
self.SMDebitInterval = 10
self.SMMaxCallDuration = time.Duration(3) * time.Hour
self.SMRunIds = []string{}
self.SMReqTypeFields = []string{}
self.SMDirectionFields = []string{}
self.SMTenantFields = []string{}
self.SMTORFields = []string{}
self.SMAccountFields = []string{}
self.SMSubjectFields = []string{}
self.SMDestFields = []string{}
self.SMSetupTimeFields = []string{}
self.SMAnswerTimeFields = []string{}
self.SMDurationFields = []string{}
self.FreeswitchServer = "127.0.0.1:8021"
self.FreeswitchPass = "ClueCon"
self.FreeswitchReconnects = 5
@@ -298,32 +265,6 @@ func (self *CGRConfig) checkConfigSanity() error {
return errors.New("Need XmlTemplate for fixed_width cdr export")
}
}
// SessionManager should have same fields config length for session emulation
if len(self.SMReqTypeFields) != len(self.SMRunIds) ||
len(self.SMDirectionFields) != len(self.SMRunIds) ||
len(self.SMTenantFields) != len(self.SMRunIds) ||
len(self.SMTORFields) != len(self.SMRunIds) ||
len(self.SMAccountFields) != len(self.SMRunIds) ||
len(self.SMSubjectFields) != len(self.SMRunIds) ||
len(self.SMDestFields) != len(self.SMRunIds) ||
len(self.SMSetupTimeFields) != len(self.SMRunIds) ||
len(self.SMAnswerTimeFields) != len(self.SMRunIds) ||
len(self.SMDurationFields) != len(self.SMRunIds) {
return errors.New("<ConfigSanity> Inconsistent fields length for SessionManager session emulation")
}
// Mediator needs to have consistent extra fields definition
if len(self.MediatorReqTypeFields) != len(self.MediatorRunIds) ||
len(self.MediatorDirectionFields) != len(self.MediatorRunIds) ||
len(self.MediatorTenantFields) != len(self.MediatorRunIds) ||
len(self.MediatorTORFields) != len(self.MediatorRunIds) ||
len(self.MediatorAccountFields) != len(self.MediatorRunIds) ||
len(self.MediatorSubjectFields) != len(self.MediatorRunIds) ||
len(self.MediatorDestFields) != len(self.MediatorRunIds) ||
len(self.MediatorSetupTimeFields) != len(self.MediatorRunIds) ||
len(self.MediatorAnswerTimeFields) != len(self.MediatorRunIds) ||
len(self.MediatorDurationFields) != len(self.MediatorRunIds) {
return errors.New("<ConfigSanity> Inconsistent fields length for Mediator extra fields")
}
return nil
}
@@ -585,7 +526,8 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
cfg.CdrcDurationField, _ = c.GetString("cdrc", "duration_field")
}
if hasOpt = c.HasOption("cdrc", "extra_fields"); hasOpt {
if cfg.CdrcExtraFields, err = ConfigSlice(c, "cdrc", "extra_fields"); err != nil {
eFldsStr, _ := c.GetString("cdrc", "extra_fields")
if cfg.CdrcExtraFields, err = ConfigSlice(eFldsStr); err != nil {
return nil, err
}
}
@@ -598,61 +540,6 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
if hasOpt = c.HasOption("mediator", "rater_reconnects"); hasOpt {
cfg.MediatorRaterReconnects, _ = c.GetInt("mediator", "rater_reconnects")
}
if hasOpt = c.HasOption("mediator", "run_ids"); hasOpt {
if cfg.MediatorRunIds, err = ConfigSlice(c, "mediator", "run_ids"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "subject_fields"); hasOpt {
if cfg.MediatorSubjectFields, err = ConfigSlice(c, "mediator", "subject_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "reqtype_fields"); hasOpt {
if cfg.MediatorReqTypeFields, err = ConfigSlice(c, "mediator", "reqtype_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "direction_fields"); hasOpt {
if cfg.MediatorDirectionFields, err = ConfigSlice(c, "mediator", "direction_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "tenant_fields"); hasOpt {
if cfg.MediatorTenantFields, err = ConfigSlice(c, "mediator", "tenant_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "tor_fields"); hasOpt {
if cfg.MediatorTORFields, err = ConfigSlice(c, "mediator", "tor_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "account_fields"); hasOpt {
if cfg.MediatorAccountFields, err = ConfigSlice(c, "mediator", "account_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "destination_fields"); hasOpt {
if cfg.MediatorDestFields, err = ConfigSlice(c, "mediator", "destination_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "setup_time_fields"); hasOpt {
if cfg.MediatorSetupTimeFields, err = ConfigSlice(c, "mediator", "setup_time_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "answer_time_fields"); hasOpt {
if cfg.MediatorAnswerTimeFields, err = ConfigSlice(c, "mediator", "answer_time_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("mediator", "duration_fields"); hasOpt {
if cfg.MediatorDurationFields, err = ConfigSlice(c, "mediator", "duration_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "enabled"); hasOpt {
cfg.SMEnabled, _ = c.GetBool("session_manager", "enabled")
}
@@ -674,61 +561,6 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "run_ids"); hasOpt {
if cfg.SMRunIds, err = ConfigSlice(c, "session_manager", "run_ids"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "reqtype_fields"); hasOpt {
if cfg.SMReqTypeFields, err = ConfigSlice(c, "session_manager", "reqtype_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "direction_fields"); hasOpt {
if cfg.SMDirectionFields, err = ConfigSlice(c, "session_manager", "direction_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "tenant_fields"); hasOpt {
if cfg.SMTenantFields, err = ConfigSlice(c, "session_manager", "tenant_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "tor_fields"); hasOpt {
if cfg.SMTORFields, err = ConfigSlice(c, "session_manager", "tor_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "account_fields"); hasOpt {
if cfg.SMAccountFields, err = ConfigSlice(c, "session_manager", "account_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "subject_fields"); hasOpt {
if cfg.SMSubjectFields, err = ConfigSlice(c, "session_manager", "subject_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "destination_fields"); hasOpt {
if cfg.SMDestFields, err = ConfigSlice(c, "session_manager", "destination_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "setup_time_fields"); hasOpt {
if cfg.SMSetupTimeFields, err = ConfigSlice(c, "session_manager", "setup_time_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "answer_time_fields"); hasOpt {
if cfg.SMAnswerTimeFields, err = ConfigSlice(c, "session_manager", "answer_time_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("session_manager", "duration_fields"); hasOpt {
if cfg.SMDurationFields, err = ConfigSlice(c, "session_manager", "duration_fields"); err != nil {
return nil, err
}
}
if hasOpt = c.HasOption("freeswitch", "server"); hasOpt {
cfg.FreeswitchServer, _ = c.GetString("freeswitch", "server")
}
@@ -738,6 +570,9 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
if hasOpt = c.HasOption("freeswitch", "reconnects"); hasOpt {
cfg.FreeswitchReconnects, _ = c.GetInt("freeswitch", "reconnects")
}
if cfg.DerivedChargers, err = ParseCfgDerivedCharging(c); err != nil {
return nil, err
}
if hasOpt = c.HasOption("history_agent", "enabled"); hasOpt {
cfg.HistoryAgentEnabled, _ = c.GetBool("history_agent", "enabled")
}

View File

@@ -110,38 +110,16 @@ func TestDefaults(t *testing.T) {
eCfg.MediatorEnabled = false
eCfg.MediatorRater = "internal"
eCfg.MediatorRaterReconnects = 3
eCfg.MediatorRunIds = []string{}
eCfg.MediatorSubjectFields = []string{}
eCfg.MediatorReqTypeFields = []string{}
eCfg.MediatorDirectionFields = []string{}
eCfg.MediatorTenantFields = []string{}
eCfg.MediatorTORFields = []string{}
eCfg.MediatorAccountFields = []string{}
eCfg.MediatorDestFields = []string{}
eCfg.MediatorSetupTimeFields = []string{}
eCfg.MediatorAnswerTimeFields = []string{}
eCfg.DerivedChargers = make(DerivedChargers, 0)
eCfg.MediatorDurationFields = []string{}
eCfg.SMEnabled = false
eCfg.SMSwitchType = FS
eCfg.SMRater = "internal"
eCfg.SMRaterReconnects = 3
eCfg.SMDebitInterval = 10
eCfg.SMMaxCallDuration = time.Duration(3) * time.Hour
eCfg.SMRunIds = []string{}
eCfg.SMReqTypeFields = []string{}
eCfg.SMDirectionFields = []string{}
eCfg.SMTenantFields = []string{}
eCfg.SMTORFields = []string{}
eCfg.SMAccountFields = []string{}
eCfg.SMSubjectFields = []string{}
eCfg.SMDestFields = []string{}
eCfg.SMSetupTimeFields = []string{}
eCfg.SMAnswerTimeFields = []string{}
eCfg.SMDurationFields = []string{}
eCfg.FreeswitchServer = "127.0.0.1:8021"
eCfg.FreeswitchPass = "ClueCon"
eCfg.FreeswitchReconnects = 5
eCfg.DerivedChargers = make(DerivedChargers, 0)
eCfg.HistoryAgentEnabled = false
eCfg.HistoryServer = "internal"
eCfg.HistoryServerEnabled = false
@@ -184,10 +162,6 @@ func TestSanityCheck(t *testing.T) {
if err := cfg.checkConfigSanity(); err != nil {
t.Error("Invalid defaults: ", err)
}
cfg.SMSubjectFields = []string{"sample1", "sample2", "sample3"}
if err := cfg.checkConfigSanity(); err == nil {
t.Error("Failed to detect config insanity")
}
cfg = &CGRConfig{}
cfg.CdreCdrFormat = utils.CDRE_FIXED_WIDTH
if err := cfg.checkConfigSanity(); err == nil {
@@ -269,37 +243,17 @@ func TestConfigFromFile(t *testing.T) {
eCfg.MediatorEnabled = true
eCfg.MediatorRater = "test"
eCfg.MediatorRaterReconnects = 99
eCfg.MediatorRunIds = []string{"test"}
eCfg.MediatorSubjectFields = []string{"test"}
eCfg.MediatorReqTypeFields = []string{"test"}
eCfg.MediatorDirectionFields = []string{"test"}
eCfg.MediatorTenantFields = []string{"test"}
eCfg.MediatorTORFields = []string{"test"}
eCfg.MediatorAccountFields = []string{"test"}
eCfg.MediatorDestFields = []string{"test"}
eCfg.MediatorSetupTimeFields = []string{"test"}
eCfg.MediatorAnswerTimeFields = []string{"test"}
eCfg.MediatorDurationFields = []string{"test"}
eCfg.SMEnabled = true
eCfg.SMSwitchType = "test"
eCfg.SMRater = "test"
eCfg.SMRaterReconnects = 99
eCfg.SMDebitInterval = 99
eCfg.SMMaxCallDuration = time.Duration(99) * time.Second
eCfg.SMRunIds = []string{"test"}
eCfg.SMReqTypeFields = []string{"test"}
eCfg.SMDirectionFields = []string{"test"}
eCfg.SMTenantFields = []string{"test"}
eCfg.SMTORFields = []string{"test"}
eCfg.SMAccountFields = []string{"test"}
eCfg.SMSubjectFields = []string{"test"}
eCfg.SMDestFields = []string{"test"}
eCfg.SMSetupTimeFields = []string{"test"}
eCfg.SMAnswerTimeFields = []string{"test"}
eCfg.SMDurationFields = []string{"test"}
eCfg.FreeswitchServer = "test"
eCfg.FreeswitchPass = "test"
eCfg.FreeswitchReconnects = 99
eCfg.DerivedChargers = DerivedChargers{&DerivedCharger{RunId: "test", ReqTypeField: "test", DirectionField: "test", TenantField: "test",
TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}}
eCfg.HistoryAgentEnabled = true
eCfg.HistoryServer = "test"
eCfg.HistoryServerEnabled = true

View File

@@ -19,22 +19,103 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"code.google.com/p/goconf/conf"
"errors"
"github.com/cgrates/cgrates/utils"
"strings"
)
// Wraps regexp compiling in case of rsr fields
func NewDerivedCharger(runId, reqTypeFld, dirFld, tenantFld, totFld, acntFld, subjFld, dstFld, sTimeFld, aTimeFld, durFld string) (dc *DerivedCharger, err error) {
if len(runId) == 0 {
return nil, errors.New("Empty run id field")
}
dc = &DerivedCharger{RunId: runId}
dc.ReqTypeField = reqTypeFld
if strings.HasPrefix(dc.ReqTypeField, utils.REGEXP_PREFIX) {
if dc.rsrReqTypeField, err = utils.NewRSRField(dc.ReqTypeField); err != nil {
return nil, err
}
}
dc.DirectionField = dirFld
if strings.HasPrefix(dc.DirectionField, utils.REGEXP_PREFIX) {
if dc.rsrDirectionField, err = utils.NewRSRField(dc.DirectionField); err != nil {
return nil, err
}
}
dc.TenantField = tenantFld
if strings.HasPrefix(dc.TenantField, utils.REGEXP_PREFIX) {
if dc.rsrTenantField, err = utils.NewRSRField(dc.TenantField); err != nil {
return nil, err
}
}
dc.TorField = totFld
if strings.HasPrefix(dc.TorField, utils.REGEXP_PREFIX) {
if dc.rsrTorField, err = utils.NewRSRField(dc.TorField); err != nil {
return nil, err
}
}
dc.AccountField = acntFld
if strings.HasPrefix(dc.AccountField, utils.REGEXP_PREFIX) {
if dc.rsrAccountField, err = utils.NewRSRField(dc.AccountField); err != nil {
return nil, err
}
}
dc.SubjectField = subjFld
if strings.HasPrefix(dc.SubjectField, utils.REGEXP_PREFIX) {
if dc.rsrSubjectField, err = utils.NewRSRField(dc.SubjectField); err != nil {
return nil, err
}
}
dc.DestinationField = dstFld
if strings.HasPrefix(dc.DestinationField, utils.REGEXP_PREFIX) {
if dc.rsrDestinationField, err = utils.NewRSRField(dc.DestinationField); err != nil {
return nil, err
}
}
dc.SetupTimeField = sTimeFld
if strings.HasPrefix(dc.SetupTimeField, utils.REGEXP_PREFIX) {
if dc.rsrSetupTimeField, err = utils.NewRSRField(dc.SetupTimeField); err != nil {
return nil, err
}
}
dc.AnswerTimeField = aTimeFld
if strings.HasPrefix(dc.AnswerTimeField, utils.REGEXP_PREFIX) {
if dc.rsrAnswerTimeField, err = utils.NewRSRField(dc.AnswerTimeField); err != nil {
return nil, err
}
}
dc.DurationField = durFld
if strings.HasPrefix(dc.DurationField, utils.REGEXP_PREFIX) {
if dc.rsrDurationField, err = utils.NewRSRField(dc.DurationField); err != nil {
return nil, err
}
}
return dc, nil
}
type DerivedCharger struct {
RunId string // Unique runId in the chain
ReqTypeField string // Field containing request type info, number in case of csv source, '^' as prefix in case of static values
DirectionField string // Field containing direction info
TenantField string // Field containing tenant info
TorField string // Field containing tor info
AccountField string // Field containing account information
SubjectField string // Field containing subject information
DestinationField string // Field containing destination information
SetupTimeField string // Field containing setup time information
AnswerTimeField string // Field containing answer time information
DurationField string // Field containing duration information
RunId string // Unique runId in the chain
ReqTypeField string // Field containing request type info, number in case of csv source, '^' as prefix in case of static values
DirectionField string // Field containing direction info
TenantField string // Field containing tenant info
TorField string // Field containing tor info
AccountField string // Field containing account information
SubjectField string // Field containing subject information
DestinationField string // Field containing destination information
SetupTimeField string // Field containing setup time information
AnswerTimeField string // Field containing answer time information
DurationField string // Field containing duration information
rsrReqTypeField *utils.RSRField // Storage for compiled Regexp in case of RSRFields
rsrDirectionField *utils.RSRField
rsrTenantField *utils.RSRField
rsrTorField *utils.RSRField
rsrAccountField *utils.RSRField
rsrSubjectField *utils.RSRField
rsrDestinationField *utils.RSRField
rsrSetupTimeField *utils.RSRField
rsrAnswerTimeField *utils.RSRField
rsrDurationField *utils.RSRField
}
type DerivedChargers []*DerivedCharger
@@ -51,3 +132,78 @@ func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) {
}
return append(dcs, dc), nil
}
// Parse the configuration file and returns DerivedChargers instance if no errors
func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs DerivedChargers, err error) {
var runIds, 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", "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", "tor_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", "duration_fields")
if durFlds, err = ConfigSlice(cfgVal); err != nil {
return nil, err
}
// We need all to be the same length
if 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(DerivedChargers, 0)
for runIdx, runId := range runIds {
dc, err := NewDerivedCharger(runId, 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
}

View File

@@ -20,6 +20,7 @@ package config
import (
"github.com/cgrates/cgrates/utils"
"reflect"
"testing"
)
@@ -43,3 +44,89 @@ func TestAppendDerivedChargers(t *testing.T) {
t.Error("Failed to detect duplicate runid")
}
}
func TestNewDerivedCharger(t *testing.T) {
edc1 := &DerivedCharger{
RunId: "test1",
ReqTypeField: "reqtype1",
DirectionField: "direction1",
TenantField: "tenant1",
TorField: "tor1",
AccountField: "account1",
SubjectField: "subject1",
DestinationField: "destination1",
SetupTimeField: "setuptime1",
AnswerTimeField: "answertime1",
DurationField: "duration1",
}
if dc1, err := NewDerivedCharger("test1", "reqtype1", "direction1", "tenant1", "tor1", "account1", "subject1", "destination1",
"setuptime1", "answertime1", "duration1"); err != nil {
t.Error("Unexpected error", err.Error)
} else if !reflect.DeepEqual(edc1, dc1) {
t.Errorf("Expecting: %v, received: %v", edc1, dc1)
}
edc2 := &DerivedCharger{
RunId: "test2",
ReqTypeField: "~reqtype2:s/sip:(.+)/$1/",
DirectionField: "~direction2:s/sip:(.+)/$1/",
TenantField: "~tenant2:s/sip:(.+)/$1/",
TorField: "~tor2:s/sip:(.+)/$1/",
AccountField: "~account2:s/sip:(.+)/$1/",
SubjectField: "~subject2:s/sip:(.+)/$1/",
DestinationField: "~destination2:s/sip:(.+)/$1/",
SetupTimeField: "~setuptime2:s/sip:(.+)/$1/",
AnswerTimeField: "~answertime2:s/sip:(.+)/$1/",
DurationField: "~duration2:s/sip:(.+)/$1/",
}
edc2.rsrReqTypeField, _ = utils.NewRSRField("~reqtype2:s/sip:(.+)/$1/")
edc2.rsrDirectionField, _ = utils.NewRSRField("~direction2:s/sip:(.+)/$1/")
edc2.rsrTenantField, _ = utils.NewRSRField("~tenant2:s/sip:(.+)/$1/")
edc2.rsrTorField, _ = utils.NewRSRField("~tor2:s/sip:(.+)/$1/")
edc2.rsrAccountField, _ = utils.NewRSRField("~account2:s/sip:(.+)/$1/")
edc2.rsrSubjectField, _ = utils.NewRSRField("~subject2:s/sip:(.+)/$1/")
edc2.rsrDestinationField, _ = utils.NewRSRField("~destination2:s/sip:(.+)/$1/")
edc2.rsrSetupTimeField, _ = utils.NewRSRField("~setuptime2:s/sip:(.+)/$1/")
edc2.rsrAnswerTimeField, _ = utils.NewRSRField("~answertime2:s/sip:(.+)/$1/")
edc2.rsrDurationField, _ = utils.NewRSRField("~duration2:s/sip:(.+)/$1/")
if dc2, err := NewDerivedCharger("test2",
"~reqtype2:s/sip:(.+)/$1/",
"~direction2:s/sip:(.+)/$1/",
"~tenant2:s/sip:(.+)/$1/",
"~tor2:s/sip:(.+)/$1/",
"~account2:s/sip:(.+)/$1/",
"~subject2:s/sip:(.+)/$1/",
"~destination2:s/sip:(.+)/$1/",
"~setuptime2:s/sip:(.+)/$1/",
"~answertime2:s/sip:(.+)/$1/",
"~duration2:s/sip:(.+)/$1/"); err != nil {
t.Error("Unexpected error", err.Error)
} else if !reflect.DeepEqual(edc2, dc2) {
t.Errorf("Expecting: %v, received: %v", edc2, dc2)
}
}
func TestParseCfgDerivedCharging(t *testing.T) {
eFieldsCfg := []byte(`[derived_charging]
run_ids = run1, run2
reqtype_fields = test1, test2
direction_fields = test1, test2
tenant_fields = test1, test2
tor_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
duration_fields = test1, test2
`)
edcs := DerivedChargers{
&DerivedCharger{RunId: "run1", ReqTypeField: "test1", DirectionField: "test1", TenantField: "test1", TorField: "test1",
AccountField: "test1", SubjectField: "test1", DestinationField: "test1", SetupTimeField: "test1", AnswerTimeField: "test1", DurationField: "test1"},
&DerivedCharger{RunId: "run2", ReqTypeField: "test2", DirectionField: "test2", TenantField: "test2", TorField: "test2",
AccountField: "test2", SubjectField: "test2", DestinationField: "test2", SetupTimeField: "test2", AnswerTimeField: "test2", DurationField: "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)
}
}

View File

@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"code.google.com/p/goconf/conf"
"errors"
"strings"
@@ -27,20 +26,17 @@ import (
)
// Adds support for slice values in config
func ConfigSlice(c *conf.ConfigFile, section, valName string) ([]string, error) {
sliceStr, errGet := c.GetString(section, valName)
if errGet != nil {
return nil, errGet
}
cfgValStrs := strings.Split(sliceStr, ",") // If need arrises, we can make the separator configurable
func ConfigSlice(cfgVal string) ([]string, error) {
cfgValStrs := strings.Split(cfgVal, ",") // If need arrises, we can make the separator configurable
if len(cfgValStrs) == 1 && cfgValStrs[0] == "" { // Prevents returning iterable with empty value
return []string{}, nil
}
for _, elm := range cfgValStrs {
for idx, elm := range cfgValStrs {
if elm == "" { //One empty element is presented when splitting empty string
return nil, errors.New("Empty values in config slice")
}
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
}

View File

@@ -81,17 +81,6 @@ extra_fields = test # Field identifiers of the fields to add in extra fields s
enabled = true # Starts Mediator service: <true|false>.
rater = test # Address where to reach the Rater: <internal|x.y.z.y:1234>
rater_reconnects = 99 # Number of reconnects to rater before giving up.
run_ids = test # Identifiers for each mediation run on CDRs
subject_fields = test # Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs.
reqtype_fields = test # Name of request type fields to be used during mediation. Use index number in case of .csv cdrs.
direction_fields = test # Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs.
tenant_fields = test # Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs.
tor_fields = test # Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs.
account_fields = test # Name of account fields to be used during mediation. Use index numbers in case of .csv cdrs.
destination_fields = test # Name of destination fields to be used during mediation. Use index numbers in case of .csv cdrs.
setup_time_fields = test # Name of setup_time fields to be used during mediation. Use index numbers in case of .csv cdrs.
answer_time_fields = test # Name of answer_time fields to be used during mediation. Use index numbers in case of .csv cdrs.
duration_fields = test # Name of duration fields to be used during mediation. Use index numbers in case of .csv cdrs.
[session_manager]
enabled = true # Starts SessionManager service: <true|false>.
@@ -100,23 +89,25 @@ rater = test # Address where to reach the Rater.
rater_reconnects = 99 # Number of reconnects to rater before giving up.
debit_interval = 99 # Interval to perform debits on.
max_call_duration = 99 # Maximum call duration a prepaid call can last
run_ids = test # Identifiers of additional sessions control.
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>.
tor_fields = test # Name of tor 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>.
duration_fields = test # Name of duration fields to be used during additional sessions control <""|*default|field_name>.
[freeswitch]
server = test # Adress where to connect to FreeSWITCH socket.
passwd = test # FreeSWITCH socket password.
reconnects = 99 # Number of attempts on connect failure.
[derived_charging]
run_ids = test # Identifiers of additional sessions control.
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>.
tor_fields = test # Name of tor 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>.
duration_fields = test # Name of duration fields to be used during additional sessions control <""|*default|field_name>.
[history_server]
enabled = true # Starts History service: <true|false>.
history_dir = test # Location on disk where to store history files.

View File

@@ -6,34 +6,34 @@
[global]
# ratingdb_type = redis # Rating subsystem database: <redis>.
# ratingdb_host = 127.0.0.1 # Rating subsystem database host address.
# 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.
# 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_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_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>
# 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 logdb.
# 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_passwd = CGRateS.org # Password to use when connecting to stordb.
# 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
# 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_tor = call # Default Type of Record to consider when missing from requests.
# default_tenant = cgrates.org # Default Tenant 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_method = *middle # Rounding method for floats/costs: <*up|*middle|*down>
# rounding_decimals = 4 # Number of decimals to round float/costs at
# xmlcfg_path = # Path towards additional config defined in xml file
# xmlcfg_path = # Path towards additional config defined in xml file
[balancer]
# enabled = false # Start Balancer service: <true|false>.
@@ -51,88 +51,79 @@
# mediator = # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
[cdre]
# cdr_format = csv # Exported CDRs format <csv>
# mask_destination_id = # Destination id containing called addresses to be masked on export
# mask_length = 0 # Length of the destination suffix to be masked
# cost_shift_digits = 0 # Shift cost on export with the number of digits digits defined here (eg: convert from Eur to cent).
# cdr_format = csv # Exported CDRs format <csv>
# mask_destination_id = # Destination id containing called addresses to be masked on export
# mask_length = 0 # Length of the destination suffix to be masked
# cost_shift_digits = 0 # Shift cost on export with the number of digits digits defined here (eg: convert from Eur to cent).
# export_dir = /var/log/cgrates/cdrexport/csv # Path where the exported CDRs will be placed
# export_template = cgrid,mediation_runid,accid,cdrhost,reqtype,direction,tenant,tor,account,subject,destination,setup_time,answer_time,duration,cost
# Exported fields template <""|fld1,fld2|*xml:instance_name>
# 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>
# 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>.
# 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.
# 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 CGRS database.
# accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs.
# reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs.
# direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs.
# tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs.
# tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs.
# account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs.
# subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs.
# destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs.
# setup_time_field = 8 # Setup time field identifier. Use index numbers in case of .csv cdrs.
# answer_time_field = 9 # Answer time field identifier. Use index numbers in case of .csv cdrs.
# duration_field = 10 # Duration 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>]
# cdr_source_id = freeswitch_csv # Free form field, tag identifying the source of the CDRs within CGRS database.
# accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs.
# reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs.
# direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs.
# tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs.
# tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs.
# account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs.
# subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs.
# destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs.
# setup_time_field = 8 # Setup time field identifier. Use index numbers in case of .csv cdrs.
# answer_time_field = 9 # Answer time field identifier. Use index numbers in case of .csv cdrs.
# duration_field = 10 # Duration 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>.
# rater = internal # Address where to reach the Rater: <internal|x.y.z.y:1234>
# rater_reconnects = 3 # Number of reconnects to rater before giving up.
# run_ids = # Identifiers of each extra mediation to run on CDRs
# reqtype_fields = # Name of request type fields to be used during extra mediation. Use index number in case of .csv cdrs.
# direction_fields = # Name of direction fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# tenant_fields = # Name of tenant fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# tor_fields = # Name of tor fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# account_fields = # Name of account fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# subject_fields = # Name of fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# destination_fields = # Name of destination fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# setup_time_fields = # Name of setup_time fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# answer_time_fields = # Name of answer_time fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# duration_fields = # Name of duration fields to be used during extra mediation. Use index numbers in case of .csv cdrs.
# rater_reconnects = 3 # Number of reconnects to rater before giving up.
[session_manager]
# enabled = false # Starts SessionManager service: <true|false>.
# switch_type = freeswitch # Defines the type of switch behind: <freeswitch>.
# switch_type = freeswitch # Defines the type of switch behind: <freeswitch>.
# rater = internal # Address where to reach the Rater.
# rater_reconnects = 3 # Number of reconnects to rater before giving up.
# debit_interval = 10 # Interval to perform debits on.
# max_call_duration = 3h # Maximum call duration a prepaid call can last
# rater_reconnects = 3 # Number of reconnects to rater before giving up.
# debit_interval = 10 # Interval to perform debits on.
# 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.
[derived_charging]
# run_ids = # Identifiers of additional sessions control.
# 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>.
# 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>.
# tor_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>.
# duration_fields = # Name of duration fields to be used during additional sessions control <""|*default|field_name>.
[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.
# 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>.
# duration_fields = # Name of duration fields to be used during additional sessions control <""|*default|field_name>.
[history_server]
# enabled = false # Starts History service: <true|false>.
# 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
# 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
# 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

@@ -1,6 +1,6 @@
--
-- Table structure for table `rater_cdrs`
-- Table structure for table `rated_cdrs`
--
DROP TABLE IF EXISTS `rated_cdrs`;
CREATE TABLE `rated_cdrs` (

View File

@@ -66,7 +66,7 @@ const (
COMMENT_CHAR = '#'
CSV_SEP = ','
FALLBACK_SEP = ';'
REGEXP_SEP = "~"
REGEXP_PREFIX = "~"
JSON = "json"
MSGPACK = "msgpack"
CSV_LOAD = "CSVLOAD"
@@ -99,6 +99,7 @@ const (
CDRE = "cdre"
MASK_CHAR = "*"
CONCATENATED_KEY_SEP = ":"
META_DEFAULT = "*default"
)
var (

View File

@@ -43,7 +43,7 @@ func NewRSRField(fldStr string) (*RSRField, error) {
if len(fldStr) == 0 {
return nil, nil
}
if !strings.HasPrefix(fldStr, REGEXP_SEP) {
if !strings.HasPrefix(fldStr, REGEXP_PREFIX) {
return &RSRField{Id: fldStr}, nil
}
if fldId, reSrcRepl, err := ParseSearchReplaceFromFieldRule(fldStr); err != nil {