merged master

This commit is contained in:
Radu Ioan Fericean
2014-03-07 12:17:45 +02:00
24 changed files with 209 additions and 291 deletions

View File

@@ -90,6 +90,7 @@ func (self *Cdrc) parseFieldsConfig() error {
utils.ACCOUNT: self.cgrCfg.CdrcAccountField,
utils.SUBJECT: self.cgrCfg.CdrcSubjectField,
utils.DESTINATION: self.cgrCfg.CdrcDestinationField,
utils.SETUP_TIME: self.cgrCfg.CdrcSetupTimeField,
utils.ANSWER_TIME: self.cgrCfg.CdrcAnswerTimeField,
utils.DURATION: self.cgrCfg.CdrcDurationField,
}
@@ -153,6 +154,10 @@ func (self *Cdrc) recordAsStoredCdr(record []string) (*utils.StoredCdr, error) {
ratedCdr.Subject = fieldVal
case utils.DESTINATION:
ratedCdr.Destination = fieldVal
case utils.SETUP_TIME:
if ratedCdr.SetupTime, err = utils.ParseTimeDetectLayout(fieldVal); err != nil {
return nil, fmt.Errorf("Cannot parse answer time field, err: %s", err.Error())
}
case utils.ANSWER_TIME:
if ratedCdr.AnswerTime, err = utils.ParseTimeDetectLayout(fieldVal); err != nil {
return nil, fmt.Errorf("Cannot parse answer time field, err: %s", err.Error())

View File

@@ -58,7 +58,7 @@ func TestParseFieldsConfig(t *testing.T) {
func TestRecordAsStoredCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cgrConfig.CdrcExtraFields = []string{"supplier:10"}
cgrConfig.CdrcExtraFields = []string{"supplier:11"}
cdrc := &Cdrc{cgrCfg: cgrConfig}
if err := cdrc.parseFieldsConfig(); err != nil {
t.Error("Failed parsing default fieldIndexesFromConfig", err)
@@ -68,7 +68,8 @@ func TestRecordAsStoredCdr(t *testing.T) {
if err == nil {
t.Error("Failed to corectly detect missing fields from record")
}
cdrRow = []string{"acc1", "prepaid", "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1"}
cdrRow = []string{"acc1", "prepaid", "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62",
"supplier1", "172.16.1.1"}
rtCdr, err := cdrc.recordAsStoredCdr(cdrRow)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
@@ -84,6 +85,7 @@ func TestRecordAsStoredCdr(t *testing.T) {
Account: cdrRow[5],
Subject: cdrRow[6],
Destination: cdrRow[7],
SetupTime: time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC),
AnswerTime: time.Date(2013, 2, 3, 19, 54, 0, 0, time.UTC),
Duration: time.Duration(62) * time.Second,
ExtraFields: map[string]string{"supplier": "supplier1"},

View File

@@ -38,7 +38,7 @@ func NewCsvCdrWriter(writer io.Writer, roundDecimals int, extraFields []string)
func (dcw *CsvCdrWriter) Write(cdr *utils.StoredCdr) error {
primaryFields := []string{cdr.CgrId, cdr.MediationRunId, cdr.AccId, cdr.CdrHost, cdr.ReqType, cdr.Direction, cdr.Tenant, cdr.TOR, cdr.Account, cdr.Subject,
cdr.Destination, cdr.AnswerTime.String(), strconv.Itoa(int(cdr.Duration)), strconv.FormatFloat(cdr.Cost, 'f', dcw.roundDecimals, 64)}
cdr.Destination, cdr.SetupTime.String(), cdr.AnswerTime.String(), strconv.Itoa(int(cdr.Duration)), strconv.FormatFloat(cdr.Cost, 'f', dcw.roundDecimals, 64)}
if len(dcw.extraFields) == 0 {
dcw.extraFields = utils.MapKeys(cdr.ExtraFields)
sort.Strings(dcw.extraFields) // Controlled order in case of dynamic extra fields

View File

@@ -30,12 +30,13 @@ func TestCsvCdrWriter(t *testing.T) {
writer := &bytes.Buffer{}
csvCdrWriter := NewCsvCdrWriter(writer, 4, []string{"extra3", "extra1"})
ratedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0).UTC(), Duration: 10, MediationRunId: utils.DEFAULT_RUNID,
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10, MediationRunId: utils.DEFAULT_RUNID,
ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01,
}
csvCdrWriter.Write(ratedCdr)
csvCdrWriter.Close()
expected := "b18944ef4dc618569f24c27b9872827a242bad0c,default,dsafdsaf,192.168.1.1,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07 08:42:26 +0000 UTC,10,1.0100,val_extra3,val_extra1"
expected := "b18944ef4dc618569f24c27b9872827a242bad0c,default,dsafdsaf,192.168.1.1,rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07 08:42:25 +0000 UTC,2013-11-07 08:42:26 +0000 UTC,10,1.0100,val_extra3,val_extra1"
result := strings.TrimSpace(writer.String())
if result != expected {
t.Errorf("Expected %s received %s.", expected, result)

View File

@@ -41,6 +41,7 @@ const (
FS_CSTMID = "cgr_tenant"
FS_CALL_DEST_NR = "dialed_extension"
FS_PARK_TIME = "start_epoch"
FS_SETUP_TIME = "start_epoch"
FS_ANSWER_TIME = "answer_epoch"
FS_HANGUP_TIME = "end_epoch"
FS_DURATION = "billsec"
@@ -114,6 +115,12 @@ func (fsCdr FSCdr) GetExtraFields() map[string]string {
}
return extraFields
}
func (fsCdr FSCdr) GetSetupTime() (t time.Time, err error) {
//ToDo: Make sure we work with UTC instead of local time
at, err := strconv.ParseInt(fsCdr[FS_SETUP_TIME], 0, 64)
t = time.Unix(at, 0)
return
}
func (fsCdr FSCdr) GetAnswerTime() (t time.Time, err error) {
//ToDo: Make sure we work with UTC instead of local time
at, err := strconv.ParseInt(fsCdr[FS_ANSWER_TIME], 0, 64)
@@ -163,13 +170,13 @@ func (fsCdr FSCdr) Restore(input string) error {
}
// Used in extra mediation
func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.StoredCdr, error) {
func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*utils.StoredCdr, error) {
if utils.IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", utils.ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
var err error
var hasKey bool
var aTimeStr, durStr string
var sTimeStr, aTimeStr, durStr string
rtCdr := new(utils.StoredCdr)
rtCdr.MediationRunId = runId
rtCdr.Cost = -1.0 // Default for non-rated CDR
@@ -223,6 +230,16 @@ func (fsCdr FSCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFl
} else if rtCdr.Destination, hasKey = fsCdr[destFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, destFld))
}
if sTimeStr, hasKey = fsCdr[setupTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(setupTimeFld, utils.STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, setupTimeFld))
} else {
if strings.HasPrefix(setupTimeFld, utils.STATIC_VALUE_PREFIX) {
sTimeStr = setupTimeFld[1:]
}
if rtCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr); err != nil && fieldsMandatory {
return nil, err
}
}
if aTimeStr, hasKey = fsCdr[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, utils.STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, answerTimeFld))
} else {

View File

@@ -75,6 +75,11 @@ func TestCDRFields(t *testing.T) {
if fsCdr.GetReqType() != utils.RATED {
t.Error("Error parsing cdr: ", fsCdr)
}
setupTime, _ := fsCdr.GetSetupTime()
expectedSTime, _ := time.Parse(time.RFC3339, "2013-08-04T09:50:54Z")
if setupTime.UTC() != expectedSTime {
t.Error("Error parsing cdr: ", fsCdr)
}
answerTime, _ := fsCdr.GetAnswerTime()
expectedATime, _ := time.Parse(time.RFC3339, "2013-08-04T09:50:56Z")
if answerTime.UTC() != expectedATime {
@@ -100,7 +105,7 @@ func TestFsCdrAsStoredCdr(t *testing.T) {
if err != nil {
t.Errorf("Error loading cdr: %v", err)
}
rtCdrOut, err := fsCdr.AsStoredCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination",
rtCdrOut, err := fsCdr.AsStoredCdr("wholesale_run", "^"+utils.RATED, "^*out", "cgr_tenant", "cgr_tor", "cgr_account", "cgr_subject", "cgr_destination", "start_epoch",
"answer_epoch", "billsec", []string{"effective_caller_id_number"}, true)
if err != nil {
t.Error("Unexpected error received", err)
@@ -108,25 +113,27 @@ func TestFsCdrAsStoredCdr(t *testing.T) {
expctRatedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f",
CdrHost: "127.0.0.1", CdrSource: FS_CDR_SOURCE, ReqType: utils.RATED,
Direction: "*out", Tenant: "ipbx.itsyscom.com", TOR: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963",
SetupTime: time.Date(2013, 8, 4, 9, 50, 54, 0, time.UTC).Local(),
AnswerTime: time.Date(2013, 8, 4, 9, 50, 56, 0, time.UTC).Local(), Duration: time.Duration(4) * time.Second,
ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
rtCdrOut2, err := fsCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "cgr_destination",
"^2013-12-07T08:42:26Z", "^12s", []string{"effective_caller_id_number"}, true)
"^2013-12-07T08:42:24Z", "^2013-12-07T08:42:26Z", "^12s", []string{"effective_caller_id_number"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &utils.StoredCdr{CgrId: utils.FSCgrId("01df56f4-d99a-4ef6-b7fe-b924b2415b7f"), AccId: "01df56f4-d99a-4ef6-b7fe-b924b2415b7f", CdrHost: "127.0.0.1",
CdrSource: FS_CDR_SOURCE, ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "+4986517174963",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"effective_caller_id_number": "+4986517174960"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2)
}
_, err = fsCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
_, err = fsCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}

View File

@@ -106,6 +106,7 @@ type CGRConfig struct {
CdrcAccountField string // Account field identifier. Use index numbers in case of .csv cdrs.
CdrcSubjectField string // Subject field identifier. Use index numbers in case of .csv CDRs.
CdrcDestinationField string // Destination field identifier. Use index numbers in case of .csv cdrs.
CdrcSetupTimeField string // Setup time field identifier. Use index numbers in case of .csv cdrs.
CdrcAnswerTimeField string // Answer time field identifier. Use index numbers in case of .csv cdrs.
CdrcDurationField string // Duration field identifier. Use index numbers in case of .csv cdrs.
CdrcExtraFields []string // Field identifiers of the fields to add in extra fields section, special format in case of .csv "field1:index1,field2:index2"
@@ -115,6 +116,7 @@ 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>.
@@ -122,7 +124,8 @@ type CGRConfig struct {
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>.
SMAnswerTimeFields []string // Name of time_answer 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>
@@ -135,7 +138,8 @@ type CGRConfig struct {
MediatorAccountFields []string // Name of account fields to be used during mediation. Use index numbers in case of .csv cdrs.
MediatorSubjectFields []string // Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs.
MediatorDestFields []string // Name of destination fields to be used during mediation. Use index numbers in case of .csv cdrs.
MediatorAnswerTimeFields []string // Name of time_start fields to be used during mediation. Use index numbers in case of .csv cdrs.
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.
FreeswitchServer string // freeswitch address host:port
FreeswitchPass string // FS socket password
@@ -206,8 +210,9 @@ func (self *CGRConfig) setDefaults() error {
self.CdrcAccountField = "5"
self.CdrcSubjectField = "6"
self.CdrcDestinationField = "7"
self.CdrcAnswerTimeField = "8"
self.CdrcDurationField = "9"
self.CdrcSetupTimeField = "8"
self.CdrcAnswerTimeField = "9"
self.CdrcDurationField = "10"
self.CdrcExtraFields = []string{}
self.MediatorEnabled = false
self.MediatorRater = "internal"
@@ -220,6 +225,7 @@ func (self *CGRConfig) setDefaults() error {
self.MediatorTORFields = []string{}
self.MediatorAccountFields = []string{}
self.MediatorDestFields = []string{}
self.MediatorSetupTimeFields = []string{}
self.MediatorAnswerTimeFields = []string{}
self.MediatorDurationFields = []string{}
self.SMEnabled = false
@@ -228,6 +234,7 @@ 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{}
@@ -235,6 +242,7 @@ func (self *CGRConfig) setDefaults() error {
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"
@@ -449,6 +457,9 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
if hasOpt = c.HasOption("cdrc", "destination_field"); hasOpt {
cfg.CdrcDestinationField, _ = c.GetString("cdrc", "destination_field")
}
if hasOpt = c.HasOption("cdrc", "setup_time_field"); hasOpt {
cfg.CdrcSetupTimeField, _ = c.GetString("cdrc", "setup_time_field")
}
if hasOpt = c.HasOption("cdrc", "answer_time_field"); hasOpt {
cfg.CdrcAnswerTimeField, _ = c.GetString("cdrc", "answer_time_field")
}
@@ -509,6 +520,11 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
return nil, errParse
}
}
if hasOpt = c.HasOption("mediator", "setup_time_fields"); hasOpt {
if cfg.MediatorSetupTimeFields, errParse = ConfigSlice(c, "mediator", "setup_time_fields"); errParse != nil {
return nil, errParse
}
}
if hasOpt = c.HasOption("mediator", "answer_time_fields"); hasOpt {
if cfg.MediatorAnswerTimeFields, errParse = ConfigSlice(c, "mediator", "answer_time_fields"); errParse != nil {
return nil, errParse
@@ -540,7 +556,11 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
return nil, errParse
}
}
if hasOpt = c.HasOption("session_manager", "run_ids"); hasOpt {
if cfg.SMRunIds, errParse = ConfigSlice(c, "session_manager", "run_ids"); errParse != nil {
return nil, errParse
}
}
if hasOpt = c.HasOption("session_manager", "reqtype_fields"); hasOpt {
if cfg.SMReqTypeFields, errParse = ConfigSlice(c, "session_manager", "reqtype_fields"); errParse != nil {
return nil, errParse
@@ -576,6 +596,11 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
return nil, errParse
}
}
if hasOpt = c.HasOption("session_manager", "setup_time_fields"); hasOpt {
if cfg.SMSetupTimeFields, errParse = ConfigSlice(c, "session_manager", "setup_time_fields"); errParse != nil {
return nil, errParse
}
}
if hasOpt = c.HasOption("session_manager", "answer_time_fields"); hasOpt {
if cfg.SMAnswerTimeFields, errParse = ConfigSlice(c, "session_manager", "answer_time_fields"); errParse != nil {
return nil, errParse

View File

@@ -99,8 +99,9 @@ func TestDefaults(t *testing.T) {
eCfg.CdrcAccountField = "5"
eCfg.CdrcSubjectField = "6"
eCfg.CdrcDestinationField = "7"
eCfg.CdrcAnswerTimeField = "8"
eCfg.CdrcDurationField = "9"
eCfg.CdrcSetupTimeField = "8"
eCfg.CdrcAnswerTimeField = "9"
eCfg.CdrcDurationField = "10"
eCfg.CdrcExtraFields = []string{}
eCfg.MediatorEnabled = false
eCfg.MediatorRater = "internal"
@@ -113,6 +114,7 @@ func TestDefaults(t *testing.T) {
eCfg.MediatorTORFields = []string{}
eCfg.MediatorAccountFields = []string{}
eCfg.MediatorDestFields = []string{}
eCfg.MediatorSetupTimeFields = []string{}
eCfg.MediatorAnswerTimeFields = []string{}
eCfg.MediatorDurationFields = []string{}
eCfg.SMEnabled = false
@@ -121,13 +123,15 @@ func TestDefaults(t *testing.T) {
eCfg.SMRaterReconnects = 3
eCfg.SMDebitInterval = 10
eCfg.SMMaxCallDuration = time.Duration(3) * time.Hour
eCfg.SMRunIds = []string{}
eCfg.SMReqTypeFields = []string{}
eCfg.SMDirectionFields = []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"
@@ -232,6 +236,7 @@ func TestConfigFromFile(t *testing.T) {
eCfg.CdrcAccountField = "test"
eCfg.CdrcSubjectField = "test"
eCfg.CdrcDestinationField = "test"
eCfg.CdrcSetupTimeField = "test"
eCfg.CdrcAnswerTimeField = "test"
eCfg.CdrcDurationField = "test"
eCfg.CdrcExtraFields = []string{"test"}
@@ -246,6 +251,7 @@ func TestConfigFromFile(t *testing.T) {
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
@@ -254,13 +260,15 @@ func TestConfigFromFile(t *testing.T) {
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.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"

View File

@@ -2,34 +2,34 @@
#
[global]
ratingdb_type = test # Rating subsystem database: <redis>.
ratingdb_host = test # Rating subsystem database host address.
ratingdb_port = test # Rating subsystem port to reach the database.
ratingdb_name = test # Rating subsystem database name to connect to.
ratingdb_user = test # Rating subsystem username to use when connecting to database.
ratingdb_passwd = test # Rating subsystem password to use when connecting to database.
accountdb_type = test # Accounting subsystem database: <redis>.
accountdb_host = test # Accounting subsystem database host address.
accountdb_port = test # Accounting subsystem port to reach the database.
accountdb_name = test # Accounting subsystem database name to connect to.
accountdb_user = test # Accounting subsystem username to use when connecting to database.
accountdb_passwd = test # Accounting subsystem password to use when connecting to database.
stordb_type = test # Log/stored database type to use: <same|postgres|mongo|redis>
stordb_host = test # The host to connect to. Values that start with / are for UNIX domain sockets.
stordb_port = test # The port to reach the logdb.
stordb_name = test # The name of the log database to connect to.
stordb_user = test # Username to use when connecting to logdb.
stordb_passwd = test # Password to use when connecting to logdb.
dbdata_encoding = test # The encoding used to store object data in strings: <msgpack|json>
rpc_json_listen = test # RPC JSON listening address
rpc_gob_listen = test # RPC GOB listening address
http_listen = test # HTTP listening address
default_reqtype = test # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>.
default_tor = test # Default Type of Record to consider when missing from requests.
default_tenant = test # Default Tenant to consider when missing from requests.
default_subject = test # Default rating Subject to consider when missing from requests.
rounding_method = test # Rounding method for floats/costs: <up|middle|down>
rounding_decimals = 99 # Number of decimals to round floats/costs at
ratingdb_type = test # Rating subsystem database: <redis>.
ratingdb_host = test # Rating subsystem database host address.
ratingdb_port = test # Rating subsystem port to reach the database.
ratingdb_name = test # Rating subsystem database name to connect to.
ratingdb_user = test # Rating subsystem username to use when connecting to database.
ratingdb_passwd = test # Rating subsystem password to use when connecting to database.
accountdb_type = test # Accounting subsystem database: <redis>.
accountdb_host = test # Accounting subsystem database host address.
accountdb_port = test # Accounting subsystem port to reach the database.
accountdb_name = test # Accounting subsystem database name to connect to.
accountdb_user = test # Accounting subsystem username to use when connecting to database.
accountdb_passwd = test # Accounting subsystem password to use when connecting to database.
stordb_type = test # Log/stored database type to use: <same|postgres|mongo|redis>
stordb_host = test # The host to connect to. Values that start with / are for UNIX domain sockets.
stordb_port = test # The port to reach the logdb.
stordb_name = test # The name of the log database to connect to.
stordb_user = test # Username to use when connecting to logdb.
stordb_passwd = test # Password to use when connecting to logdb.
dbdata_encoding = test # The encoding used to store object data in strings: <msgpack|json>
rpc_json_listen = test # RPC JSON listening address
rpc_gob_listen = test # RPC GOB listening address
http_listen = test # HTTP listening address
default_reqtype = test # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>.
default_tor = test # Default Type of Record to consider when missing from requests.
default_tenant = test # Default Tenant to consider when missing from requests.
default_subject = test # Default rating Subject to consider when missing from requests.
rounding_method = test # Rounding method for floats/costs: <up|middle|down>
rounding_decimals = 99 # Number of decimals to round floats/costs at
[balancer]
@@ -37,7 +37,7 @@ enabled = true # Start Balancer service: <true|false>.
[rater]
enabled = true # Enable Rater service: <true|false>.
balancer = test # Register to Balancer as worker: <enabled|disabled>.
balancer = test # Register to Balancer as worker: <enabled|disabled>.
[scheduler]
enabled = true # Starts Scheduler service: <true|false>.
@@ -48,9 +48,9 @@ extra_fields = test # Extra fields to store in CDRs
mediator = test # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
[cdre]
cdr_format = test # Exported CDRs format <csv>
extra_fields = test # List of extra fields to be exported out in CDRs
export_dir = test # Path where the exported CDRs will be placed
cdr_format = test # Exported CDRs format <csv>
extra_fields = test # List of extra fields to be exported out in CDRs
export_dir = test # Path where the exported CDRs will be placed
[cdrc]
enabled = true # Enable CDR client functionality
@@ -69,23 +69,25 @@ tor_field = test # Type of Record field identifier. Use index numbers in case
account_field = test # Account field identifier. Use index numbers in case of .csv cdrs.
subject_field = test # Subject field identifier. Use index numbers in case of .csv CDRs.
destination_field = test # Destination field identifier. Use index numbers in case of .csv cdrs.
setup_time_field = test # Answer time field identifier. Use index numbers in case of .csv cdrs.
answer_time_field = test # Answer time field identifier. Use index numbers in case of .csv cdrs.
duration_field = test # Duration field identifier. Use index numbers in case of .csv cdrs.
extra_fields = test # Field identifiers of the fields to add in extra fields section, special format in case of .csv "index1:field1,index2:field2"
[mediator]
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.
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.
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.
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.
answer_time_fields = test # Name of time_answer 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]
@@ -95,6 +97,7 @@ 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>.
@@ -102,25 +105,26 @@ tor_fields = test # Name of tor fields to be used during additional sessions c
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>.
answer_time_fields = test # Name of time_answer 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.
server = test # Adress where to connect to FreeSWITCH socket.
passwd = test # FreeSWITCH socket password.
reconnects = 99 # Number of attempts on connect failure.
[history_server]
enabled = true # Starts History service: <true|false>.
history_dir = test # Location on disk where to store history files.
save_interval = 99 # Timeout duration between saves
enabled = true # Starts History service: <true|false>.
history_dir = test # Location on disk where to store history files.
save_interval = 99 # Timeout duration between saves
[history_agent]
enabled = true # Starts History as a client: <true|false>.
server = test # Address where to reach the master history server: <internal|x.y.z.y:1234>
enabled = true # Starts History as a client: <true|false>.
server = test # Address where to reach the master history server: <internal|x.y.z.y:1234>
[mailer]
server = test # The server to use when sending emails out
auth_user = test # Authenticate to email server using this user
auth_passwd = test # Authenticate to email server with this password
from_address = test # From address used when sending emails out
server = test # The server to use when sending emails out
auth_user = test # Authenticate to email server using this user
auth_passwd = test # Authenticate to email server with this password
from_address = test # From address used when sending emails out

View File

@@ -71,8 +71,9 @@
# 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.
# answer_time_field = 8 # Answer time field identifier. Use index numbers in case of .csv cdrs.
# duration_field = 9 # Duration 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]
@@ -87,7 +88,8 @@
# 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.
# answer_time_fields = # Name of time_answer 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.
[session_manager]
@@ -97,6 +99,7 @@
# 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
# 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>.
# tenant_fields = # Name of tenant fields to be used during additional sessions control <""|*default|field_name>.
@@ -104,7 +107,8 @@
# 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>.
# answer_time_fields = # Name of time_answer 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>.

View File

@@ -4,127 +4,32 @@
# This is what you get when you load CGRateS with an empty configuration file.
# [global] must exist in all files, rest of the configuration is inter-changeable.
[global]
# ratingdb_type = redis # Rating subsystem database: <redis>.
# 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.
# accountdb_type = redis # Accounting subsystem database: <redis>.
# 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_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.
# 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
# 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_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
[balancer]
# enabled = false # Start Balancer service: <true|false>.
[rater]
enabled = true # Enable RaterCDRSExportPath service: <true|false>.
# balancer = # Register to Balancer as worker: <""|internal|127.0.0.1:2013>.
[scheduler]
enabled = true # Starts Scheduler service: <true|false>.
[cdrs]
enabled = true # Start the CDR Server service: <true|false>.
# extra_fields = # Extra fields to store in CDRs for non-generic CDRs
mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
[cdre]
# cdr_format = csv # Exported CDRs format <csv>
# extra_fields = # List of extra fields to be exported out in CDRs
export_dir = /tmp/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed
[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 = /tmp/cgrates/cdr/cdrc/in # Absolute path towards the directory where the CDRs are stored.
cdr_out_dir =/tmp/cgrates/cdr/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.
# answer_time_field = 8 # Answer time field identifier. Use index numbers in case of .csv cdrs.
# duration_field = 9 # 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 = true # Starts Mediator service: <true|false>.
rater = 127.0.0.1:2012 # 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.
# answer_time_fields = # Name of time_answer 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.
[session_manager]
# enabled = false # Starts SessionManager service: <true|false>.
# 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
# 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>.
# 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>.
# answer_time_fields = # Name of time_answer 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.
[history_server]
enabled = true # Starts History service: <true|false>.
history_dir = /tmp/cgrates/history # Location on disk where to store history files.
# save_interval = 1s # Interval to save changed cache into .git archive
[history_agent]
enabled = true # 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
# from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out

View File

@@ -3,117 +3,18 @@
# Used in mediator_local_test
# Starts rater, cdrs and mediator connecting over internal channel
[global]
# ratingdb_type = redis # Rating subsystem database: <redis>.
# 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.
# accountdb_type = redis # Accounting subsystem database: <redis>.
# 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_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.
# 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
# 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_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
[balancer]
# enabled = false # Start Balancer service: <true|false>.
[rater]
enabled = true # Enable RaterCDRSExportPath service: <true|false>.
# balancer = # Register to Balancer as worker: <""|internal|127.0.0.1:2013>.
[scheduler]
# enabled = false # Starts Scheduler service: <true|false>.
[cdrs]
enabled = true # Start the CDR Server service: <true|false>.
# extra_fields = # Extra fields to store in CDRs in case of non generic CDRs
mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
[cdre]
# cdr_format = csv # Exported CDRs format <csv>
# extra_fields = # List of extra fields to be exported out in CDRs
export_dir = /tmp/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed
[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/cdr/cdrc/in # Absolute path towards the directory where the CDRs are stored.
# cdr_out_dir = /var/log/cgrates/cdr/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.
# answer_time_field = 8 # Answer time field identifier. Use index numbers in case of .csv cdrs.
# duration_field = 9 # 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 = true # 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.
# answer_time_fields = # Name of time_answer 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.
[session_manager]
# enabled = false # Starts SessionManager service: <true|false>.
# 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
[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.
[history_server]
# 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
[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
# from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out

View File

@@ -13,6 +13,7 @@ CREATE TABLE cdrs_primary (
account varchar(128) NOT NULL,
subject varchar(128) NOT NULL,
destination varchar(128) NOT NULL,
setup_time datetime NOT NULL,
answer_time datetime NOT NULL,
duration bigint NOT NULL,
PRIMARY KEY (tbid),

View File

@@ -514,8 +514,9 @@ func (self *SQLStorage) LogError(uuid, source, runid, errstr string) (err error)
func (self *SQLStorage) SetCdr(cdr utils.RawCDR) (err error) {
// map[account:1001 direction:out orig_ip:172.16.1.1 tor:call accid:accid23 answer_time:2013-02-03 19:54:00 cdrsource:freeswitch_csv destination:+4986517174963 duration:62 reqtype:prepaid subject:1001 supplier:supplier1 tenant:cgrates.org]
startTime, _ := cdr.GetAnswerTime() // Ignore errors, we want to store the cdr no matter what
_, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s VALUES (NULL,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', %d)",
setupTime, _ := cdr.GetSetupTime() // Ignore errors, we want to store the cdr no matter what
answerTime, _ := cdr.GetAnswerTime() // Ignore errors, we want to store the cdr no matter what
_, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s VALUES (NULL,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', %d)",
utils.TBL_CDRS_PRIMARY,
cdr.GetCgrId(),
cdr.GetAccId(),
@@ -528,7 +529,8 @@ func (self *SQLStorage) SetCdr(cdr utils.RawCDR) (err error) {
cdr.GetAccount(),
cdr.GetSubject(),
cdr.GetDestination(),
startTime,
setupTime,
answerTime,
cdr.GetDuration(),
))
if err != nil {
@@ -569,7 +571,7 @@ func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string
// ignoreRated - do not consider cdrs which were already rated, including here the ones with errors
func (self *SQLStorage) GetStoredCdrs(timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
var cdrs []*utils.StoredCdr
q := fmt.Sprintf("SELECT %s.cgrid,accid,cdrhost,cdrsource,reqtype,direction,tenant,tor,account,%s.subject,destination,answer_time,duration,extra_fields,runid,cost FROM %s LEFT JOIN %s ON %s.cgrid=%s.cgrid LEFT JOIN %s ON %s.cgrid=%s.cgrid", utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_RATED_CDRS, utils.TBL_CDRS_PRIMARY, utils.TBL_RATED_CDRS)
q := fmt.Sprintf("SELECT %s.cgrid,accid,cdrhost,cdrsource,reqtype,direction,tenant,tor,account,%s.subject,destination,setup_time,answer_time,duration,extra_fields,runid,cost FROM %s LEFT JOIN %s ON %s.cgrid=%s.cgrid LEFT JOIN %s ON %s.cgrid=%s.cgrid", utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_RATED_CDRS, utils.TBL_CDRS_PRIMARY, utils.TBL_RATED_CDRS)
fltr := ""
if !timeStart.IsZero() {
if len(fltr) != 0 {
@@ -609,12 +611,12 @@ func (self *SQLStorage) GetStoredCdrs(timeStart, timeEnd time.Time, ignoreErr, i
for rows.Next() {
var cgrid, accid, cdrhost, cdrsrc, reqtype, direction, tenant, tor, account, subject, destination string
var extraFields []byte
var answerTime time.Time
var setupTime, answerTime time.Time
var duration int64
var runid sql.NullString // So we can export unmediated CDRs
var cost sql.NullFloat64 // So we can export unmediated CDRs
var extraFieldsMp map[string]string
if err := rows.Scan(&cgrid, &accid, &cdrhost, &cdrsrc, &reqtype, &direction, &tenant, &tor, &account, &subject, &destination, &answerTime, &duration,
if err := rows.Scan(&cgrid, &accid, &cdrhost, &cdrsrc, &reqtype, &direction, &tenant, &tor, &account, &subject, &destination, &setupTime, &answerTime, &duration,
&extraFields, &runid, &cost); err != nil {
return nil, err
}
@@ -623,7 +625,7 @@ func (self *SQLStorage) GetStoredCdrs(timeStart, timeEnd time.Time, ignoreErr, i
}
storCdr := &utils.StoredCdr{
CgrId: cgrid, AccId: accid, CdrHost: cdrhost, CdrSource: cdrsrc, ReqType: reqtype, Direction: direction, Tenant: tenant,
TOR: tor, Account: account, Subject: subject, Destination: destination, AnswerTime: answerTime, Duration: time.Duration(duration),
TOR: tor, Account: account, Subject: subject, Destination: destination, SetupTime: setupTime, AnswerTime: answerTime, Duration: time.Duration(duration),
ExtraFields: extraFieldsMp, MediationRunId: runid.String, Cost: cost.Float64,
}
cdrs = append(cdrs, storCdr)

View File

@@ -52,7 +52,7 @@ type Mediator struct {
func (self *Mediator) parseConfig() error {
cfgVals := [][]string{self.cgrCfg.MediatorSubjectFields, self.cgrCfg.MediatorReqTypeFields, self.cgrCfg.MediatorDirectionFields,
self.cgrCfg.MediatorTenantFields, self.cgrCfg.MediatorTORFields, self.cgrCfg.MediatorAccountFields, self.cgrCfg.MediatorDestFields,
self.cgrCfg.MediatorAnswerTimeFields, self.cgrCfg.MediatorDurationFields}
self.cgrCfg.MediatorSetupTimeFields, self.cgrCfg.MediatorAnswerTimeFields, self.cgrCfg.MediatorDurationFields}
// All other configured fields must match the length of reference fields
for iCfgVal := range cfgVals {
@@ -138,7 +138,8 @@ func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error {
for runIdx, runId := range self.cgrCfg.MediatorRunIds {
forkedCdr, err := dbcdr.AsStoredCdr(self.cgrCfg.MediatorRunIds[runIdx], self.cgrCfg.MediatorReqTypeFields[runIdx], self.cgrCfg.MediatorDirectionFields[runIdx],
self.cgrCfg.MediatorTenantFields[runIdx], self.cgrCfg.MediatorTORFields[runIdx], self.cgrCfg.MediatorAccountFields[runIdx],
self.cgrCfg.MediatorSubjectFields[runIdx], self.cgrCfg.MediatorDestFields[runIdx], self.cgrCfg.MediatorAnswerTimeFields[runIdx],
self.cgrCfg.MediatorSubjectFields[runIdx], self.cgrCfg.MediatorDestFields[runIdx],
self.cgrCfg.MediatorSetupTimeFields[runIdx], self.cgrCfg.MediatorAnswerTimeFields[runIdx],
self.cgrCfg.MediatorDurationFields[runIdx], []string{}, true)
if err != nil { // Errors on fork, cannot calculate further, write that into db for later analysis
self.cdrDb.SetRatedCdr(&utils.StoredCdr{CgrId: dbcdr.GetCgrId(), MediationRunId: runId, Cost: -1.0}, err.Error()) // Cannot fork CDR, important just runid and error

View File

@@ -34,7 +34,7 @@ type Event interface {
GetTOR(string) string
GetTenant(string) string
GetReqType(string) string
GetStartTime(string) (time.Time, error)
GetAnswerTime(string) (time.Time, error)
GetEndTime() (time.Time, error)
MissingParameter() bool
}

View File

@@ -42,7 +42,8 @@ const (
CSTMID = "variable_cgr_tenant"
CALL_DEST_NR = "Caller-Destination-Number"
PARK_TIME = "Caller-Profile-Created-Time"
START_TIME = "Caller-Channel-Answered-Time"
SETUP_TIME = "Caller-Channel-Created-Time"
ANSWER_TIME = "Caller-Channel-Answered-Time"
END_TIME = "Caller-Channel-Hangup-Time"
NAME = "Event-Name"
HEARTBEAT = "HEARTBEAT"
@@ -120,7 +121,12 @@ func (fsev FSEvent) MissingParameter() bool {
strings.TrimSpace(fsev.GetTenant("")) == "" ||
strings.TrimSpace(fsev.GetCallDestNr("")) == ""
}
func (fsev FSEvent) GetStartTime(field string) (t time.Time, err error) {
func (fsev FSEvent) GetSetupTime(field string) (t time.Time, err error) {
st, err := strconv.ParseInt(fsev[field], 0, 64)
t = time.Unix(0, st*1000)
return
}
func (fsev FSEvent) GetAnswerTime(field string) (t time.Time, err error) {
st, err := strconv.ParseInt(fsev[field], 0, 64)
t = time.Unix(0, st*1000)
return

View File

@@ -151,7 +151,7 @@ func (sm *FSSessionManager) OnHeartBeat(ev Event) {
func (sm *FSSessionManager) OnChannelPark(ev Event) {
//engine.Logger.Info("freeswitch park")
startTime, err := ev.GetStartTime(PARK_TIME)
startTime, err := ev.GetAnswerTime(PARK_TIME)
if err != nil {
engine.Logger.Err("Error parsing answer event start time, using time.Now!")
startTime = time.Now()
@@ -213,7 +213,7 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
}
defer s.Close(ev) // Stop loop and save the costs deducted so far to database
if ev.GetReqType("") == utils.POSTPAID {
startTime, err := ev.GetStartTime(START_TIME)
startTime, err := ev.GetAnswerTime(ANSWER_TIME)
if err != nil {
engine.Logger.Crit("Error parsing postpaid call start time from event")
return

View File

@@ -42,7 +42,7 @@ func NewSession(ev Event, sm SessionManager) (s *Session) {
if ev.GetReqType("") != utils.PREPAID && ev.GetReqType("") != utils.POSTPAID {
return
}
startTime, err := ev.GetStartTime(START_TIME)
startTime, err := ev.GetAnswerTime(ANSWER_TIME)
if err != nil {
engine.Logger.Err("Error parsing answer event start time, using time.Now!")
startTime = time.Now()

View File

@@ -95,6 +95,9 @@ func (cgrCdr CgrCdr) GetExtraFields() map[string]string {
}
return extraFields
}
func (cgrCdr CgrCdr) GetSetupTime() (t time.Time, err error) {
return ParseTimeDetectLayout(cgrCdr[SETUP_TIME])
}
func (cgrCdr CgrCdr) GetAnswerTime() (t time.Time, err error) {
return ParseTimeDetectLayout(cgrCdr[ANSWER_TIME])
}
@@ -107,13 +110,13 @@ func (cgrCdr CgrCdr) GetDuration() time.Duration {
// Used in mediation, fieldsMandatory marks whether missing field out of request represents error or can be ignored
// If the fields in parameters start with ^ their value is considered instead of dynamically retrieving it from CDR
func (cgrCdr CgrCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
func (cgrCdr CgrCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
if IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
var err error
var hasKey bool
var aTimeStr, durStr string
var sTimeStr, aTimeStr, durStr string
rtCdr := new(StoredCdr)
rtCdr.MediationRunId = runId
rtCdr.Cost = -1.0 // Default for non-rated CDR
@@ -167,6 +170,16 @@ func (cgrCdr CgrCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, tor
} else if rtCdr.Destination, hasKey = cgrCdr[destFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, destFld))
}
if sTimeStr, hasKey = cgrCdr[setupTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(setupTimeFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, setupTimeFld))
} else {
if strings.HasPrefix(setupTimeFld, STATIC_VALUE_PREFIX) {
sTimeStr = setupTimeFld[1:]
}
if rtCdr.SetupTime, err = ParseTimeDetectLayout(sTimeStr); err != nil && fieldsMandatory {
return nil, err
}
}
if aTimeStr, hasKey = cgrCdr[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, answerTimeFld))
} else {

View File

@@ -80,30 +80,35 @@ func TestCgrCdrFields(t *testing.T) {
func TestCgrCdrAsStoredCdr(t *testing.T) {
cgrCdr := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
rtCdrOut, err := cgrCdr.AsStoredCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
rtCdrOut, err := cgrCdr.AsStoredCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration",
[]string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr := &StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", AnswerTime: time.Unix(1383813746, 0).UTC(),
Direction: "*out", Tenant: "cgrates.org", TOR: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Unix(1383813744, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
rtCdrOut2, err := cgrCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
rtCdrOut2, err := cgrCdr.AsStoredCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination",
"^2013-12-07T08:42:24Z", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &StoredCdr{CgrId: FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "postpaid",
Direction: "*in", Tenant: "cgrates.com", TOR: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2)
}
_, err = cgrCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "answer_time", "duration", []string{"field_extr1", "fieldextr2"}, true)
_, err = cgrCdr.AsStoredCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration",
[]string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}

View File

@@ -78,6 +78,7 @@ const (
ACCOUNT = "account"
SUBJECT = "subject"
DESTINATION = "destination"
SETUP_TIME = "setup_time"
ANSWER_TIME = "answer_time"
DURATION = "duration"
DEFAULT_RUNID = "default"

View File

@@ -37,8 +37,9 @@ type RawCDR interface {
GetTOR() string
GetTenant() string
GetReqType() string
GetAnswerTime() (time.Time, error)
GetSetupTime() (time.Time, error) // Time when the call was set-up
GetAnswerTime() (time.Time, error) // Time when the call was answered
GetDuration() time.Duration
GetExtraFields() map[string]string //Stores extra CDR Fields
AsStoredCdr(string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR
GetExtraFields() map[string]string //Stores extra CDR Fields
AsStoredCdr(string, string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR
}

View File

@@ -38,6 +38,9 @@ func NewStoredCdrFromRawCDR(rawcdr RawCDR) (*StoredCdr, error) {
rtCdr.Account = rawcdr.GetAccount()
rtCdr.Subject = rawcdr.GetSubject()
rtCdr.Destination = rawcdr.GetDestination()
if rtCdr.SetupTime, err = rawcdr.GetSetupTime(); err != nil {
return nil, err
}
if rtCdr.AnswerTime, err = rawcdr.GetAnswerTime(); err != nil {
return nil, err
}
@@ -61,6 +64,7 @@ type StoredCdr struct {
Account string
Subject string
Destination string
SetupTime time.Time
AnswerTime time.Time
Duration time.Duration
ExtraFields map[string]string
@@ -114,6 +118,10 @@ func (storedCdr *StoredCdr) GetReqType() string {
return storedCdr.ReqType
}
func (storedCdr *StoredCdr) GetSetupTime() (time.Time, error) {
return storedCdr.SetupTime, nil
}
func (storedCdr *StoredCdr) GetAnswerTime() (time.Time, error) {
return storedCdr.AnswerTime, nil
}
@@ -126,7 +134,7 @@ func (storedCdr *StoredCdr) GetExtraFields() map[string]string {
return storedCdr.ExtraFields
}
func (storedCdr *StoredCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
func (storedCdr *StoredCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
return storedCdr, nil
}
@@ -143,6 +151,7 @@ func (storedCdr *StoredCdr) AsRawCdrHttpForm() url.Values {
v.Set(ACCOUNT, storedCdr.Account)
v.Set(SUBJECT, storedCdr.Subject)
v.Set(DESTINATION, storedCdr.Destination)
v.Set(SETUP_TIME, storedCdr.SetupTime.String())
v.Set(ANSWER_TIME, storedCdr.AnswerTime.String())
v.Set(DURATION, strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
for fld, val := range storedCdr.ExtraFields {