From 64c1cd67f0088158d71f31e3ad3e747d2ff17668 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 25 Jul 2013 12:16:15 +0300 Subject: [PATCH 1/2] implemented global rounding method and precision for get cost --- cmd/cgr-engine/cgr-engine.go | 3 ++- engine/calldesc.go | 40 ++++++++++++------------------------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 02a52c356..7dd2e999a 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -26,8 +26,8 @@ import ( "github.com/cgrates/cgrates/balancer2go" "github.com/cgrates/cgrates/cdrs" "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/mediator" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/mediator" "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/sessionmanager" "github.com/cgrates/cgrates/utils" @@ -252,6 +252,7 @@ func main() { } defer loggerDb.Close() engine.SetStorageLogger(loggerDb) + engine.SetRoundingMethodAndDecimals(cfg.RaterRoundingMethod, cfg.RaterRoundingDecimals) if cfg.SMDebitInterval > 0 { if dp, err := time.ParseDuration(fmt.Sprintf("%vs", cfg.SMDebitInterval)); err == nil { diff --git a/engine/calldesc.go b/engine/calldesc.go index 1cdbe480c..532b658bb 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -24,7 +24,6 @@ import ( "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" "log/syslog" - "math" "strings" "time" ) @@ -51,27 +50,12 @@ var ( storageGetter, _ = NewMapStorage() //storageGetter, _ = NewMongoStorage(db_server, "27017", "cgrates_test", "", "") //storageGetter, _ = NewRedisStorage(db_server+":6379", 11, "") - storageLogger = storageGetter - debitPeriod = 10 * time.Second + storageLogger = storageGetter + debitPeriod = 10 * time.Second + roundingMethod = "*middle" + roundingDecimals = 4 ) -/* -Utility function for rounding a float to a certain number of decimals (not present in math). -*/ -func round(val float64, prec int) float64 { - - var rounder float64 - intermed := val * math.Pow(10, float64(prec)) - - if val >= 0.5 { - rounder = math.Ceil(intermed) - } else { - rounder = math.Floor(intermed) - } - - return rounder / math.Pow(10, float64(prec)) -} - /* The input stucture that contains call information. */ @@ -102,9 +86,7 @@ func (cd *CallDescriptor) GetUserBalanceKey() string { return fmt.Sprintf("%s:%s:%s", cd.Direction, cd.Tenant, subj) } -/* -Gets and caches the user balance information. -*/ +// Gets and caches the user balance information. func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) { if cd.userBalance == nil { cd.userBalance, err = storageGetter.GetUserBalance(cd.GetUserBalanceKey()) @@ -112,13 +94,17 @@ func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) { return cd.userBalance, err } -/* -Exported method to set the storage getter. -*/ +// Exported method to set the storage getter. func SetDataStorage(sg DataStorage) { storageGetter = sg } +// Sets the global rounding method and decimal precision for GetCost method +func SetRoundingMethodAndDecimals(rm string, rd int) { + roundingMethod = rm + roundingDecimals = rd +} + /* Sets the database for logging (can be de same as storage getter or different db) */ @@ -285,7 +271,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { } cost += ts.getCost(cd) } - + cost = utils.Round(cost, roundingDecimals, roundingMethod) cc := &CallCost{ Direction: cd.Direction, TOR: cd.TOR, From ff9f8c82afb125c68d578cb0569d17cad4c764ce Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 25 Jul 2013 13:51:22 +0300 Subject: [PATCH 2/2] started work on generic cdr --- cdrs/cdrs.go | 26 ++++++- cdrs/fscdr.go | 60 ++++++++-------- cdrs/gencdr.go | 163 ++++++++++++++++++++++++++++++++++++++++++ config/config.go | 147 +++++++++++++++++++------------------ config/config_test.go | 98 ++++++++++++------------- config/test_data.txt | 1 + 6 files changed, 343 insertions(+), 152 deletions(-) create mode 100644 cdrs/gencdr.go diff --git a/cdrs/cdrs.go b/cdrs/cdrs.go index 1d6262c4d..75a3a92d4 100644 --- a/cdrs/cdrs.go +++ b/cdrs/cdrs.go @@ -21,8 +21,8 @@ package cdrs import ( "fmt" "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/mediator" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/mediator" "io/ioutil" "net/http" ) @@ -33,7 +33,7 @@ var ( medi *mediator.Mediator ) -func cdrHandler(w http.ResponseWriter, r *http.Request) { +func fsCdrHandler(w http.ResponseWriter, r *http.Request) { body, _ := ioutil.ReadAll(r.Body) if fsCdr, err := new(FSCdr).New(body); err == nil { storage.SetCdr(fsCdr) @@ -50,6 +50,23 @@ func cdrHandler(w http.ResponseWriter, r *http.Request) { } } +func genCdrHandler(w http.ResponseWriter, r *http.Request) { + body, _ := ioutil.ReadAll(r.Body) + if genCdr, err := new(GenCdr).New(body); err == nil { + storage.SetCdr(genCdr) + if cfg.CDRSMediator == "internal" { + errMedi := medi.MediateDBCDR(genCdr, storage) + if errMedi != nil { + engine.Logger.Err(fmt.Sprintf("Could not run mediation on CDR: %s", errMedi.Error())) + } + } else { + //TODO: use the connection to mediator + } + } else { + engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %v", err)) + } +} + type CDRS struct{} func New(s engine.DataStorage, m *mediator.Mediator, c *config.CGRConfig) *CDRS { @@ -61,7 +78,10 @@ func New(s engine.DataStorage, m *mediator.Mediator, c *config.CGRConfig) *CDRS func (cdrs *CDRS) StartCapturingCDRs() { if cfg.CDRSfsJSONEnabled { - http.HandleFunc("/freeswitch_json", cdrHandler) + http.HandleFunc("/freeswitch_json", fsCdrHandler) + } + if cfg.CDRSgenJSONEnabled { + http.HandleFunc("/generic_json", genCdrHandler) } http.ListenAndServe(cfg.CDRSListen, nil) } diff --git a/cdrs/fscdr.go b/cdrs/fscdr.go index 6e69814da..3f0a116cf 100644 --- a/cdrs/fscdr.go +++ b/cdrs/fscdr.go @@ -28,23 +28,23 @@ import ( const ( // Freswitch event property names - CDR_MAP = "variables" - DIRECTION = "direction" - ORIG_ID = "sip_call_id" //- originator_id - match cdrs - SUBJECT = "cgr_subject" - ACCOUNT = "cgr_account" - DESTINATION = "cgr_destination" - REQTYPE = "cgr_reqtype" //prepaid or postpaid - TOR = "cgr_tor" - UUID = "uuid" // -Unique ID for this call leg - CSTMID = "cgr_cstmid" - CALL_DEST_NR = "dialed_extension" - PARK_TIME = "start_epoch" - ANSWER_TIME = "answer_epoch" - HANGUP_TIME = "end_epoch" - DURATION = "billsec" - USERNAME = "user_name" - FS_IP = "sip_local_network_addr" + FS_CDR_MAP = "variables" + FS_DIRECTION = "direction" + FS_ORIG_ID = "sip_call_id" //- originator_id - match cdrs + FS_SUBJECT = "cgr_subject" + FS_ACCOUNT = "cgr_account" + FS_DESTINATION = "cgr_destination" + FS_REQTYPE = "cgr_reqtype" //prepaid or postpaid + FS_TOR = "cgr_tor" + FS_UUID = "uuid" // -Unique ID for this call leg + FS_CSTMID = "cgr_cstmid" + FS_CALL_DEST_NR = "dialed_extension" + FS_PARK_TIME = "start_epoch" + FS_ANSWER_TIME = "answer_epoch" + FS_HANGUP_TIME = "end_epoch" + FS_DURATION = "billsec" + FS_USERNAME = "user_name" + FS_IP = "sip_local_network_addr" ) type FSCdr map[string]string @@ -54,7 +54,7 @@ func (fsCdr FSCdr) New(body []byte) (utils.CDR, error) { var tmp map[string]interface{} var err error if err = json.Unmarshal(body, &tmp); err == nil { - if variables, ok := tmp[CDR_MAP]; ok { + if variables, ok := tmp[FS_CDR_MAP]; ok { if variables, ok := variables.(map[string]interface{}); ok { for k, v := range variables { fsCdr[k] = v.(string) @@ -67,10 +67,10 @@ func (fsCdr FSCdr) New(body []byte) (utils.CDR, error) { } func (fsCdr FSCdr) GetCgrId() string { - return utils.FSCgrId(fsCdr[UUID]) + return utils.FSCgrId(fsCdr[FS_UUID]) } func (fsCdr FSCdr) GetAccId() string { - return fsCdr[UUID] + return fsCdr[FS_UUID] } func (fsCdr FSCdr) GetCdrHost() string { return fsCdr[FS_IP] @@ -80,29 +80,29 @@ func (fsCdr FSCdr) GetDirection() string { return "OUT" } func (fsCdr FSCdr) GetOrigId() string { - return fsCdr[ORIG_ID] + return fsCdr[FS_ORIG_ID] } func (fsCdr FSCdr) GetSubject() string { - return utils.FirstNonEmpty(fsCdr[SUBJECT], fsCdr[USERNAME]) + return utils.FirstNonEmpty(fsCdr[FS_SUBJECT], fsCdr[FS_USERNAME]) } func (fsCdr FSCdr) GetAccount() string { - return utils.FirstNonEmpty(fsCdr[ACCOUNT], fsCdr[USERNAME]) + return utils.FirstNonEmpty(fsCdr[FS_ACCOUNT], fsCdr[FS_USERNAME]) } // Charging destination number func (fsCdr FSCdr) GetDestination() string { - return utils.FirstNonEmpty(fsCdr[DESTINATION], fsCdr[CALL_DEST_NR]) + return utils.FirstNonEmpty(fsCdr[FS_DESTINATION], fsCdr[FS_CALL_DEST_NR]) } func (fsCdr FSCdr) GetTOR() string { - return utils.FirstNonEmpty(fsCdr[TOR], cfg.DefaultTOR) + return utils.FirstNonEmpty(fsCdr[FS_TOR], cfg.DefaultTOR) } func (fsCdr FSCdr) GetTenant() string { - return utils.FirstNonEmpty(fsCdr[CSTMID], cfg.DefaultTenant) + return utils.FirstNonEmpty(fsCdr[FS_CSTMID], cfg.DefaultTenant) } func (fsCdr FSCdr) GetReqType() string { - return utils.FirstNonEmpty(fsCdr[REQTYPE], cfg.DefaultReqType) + return utils.FirstNonEmpty(fsCdr[FS_REQTYPE], cfg.DefaultReqType) } func (fsCdr FSCdr) GetExtraFields() map[string]string { extraFields := make(map[string]string, len(cfg.CDRSExtraFields)) @@ -115,19 +115,19 @@ func (fsCdr FSCdr) GetFallbackSubj() string { return cfg.DefaultSubject } func (fsCdr FSCdr) GetAnswerTime() (t time.Time, err error) { - st, err := strconv.ParseInt(fsCdr[ANSWER_TIME], 0, 64) + st, err := strconv.ParseInt(fsCdr[FS_ANSWER_TIME], 0, 64) t = time.Unix(0, st*1000) return } func (fsCdr FSCdr) GetHangupTime() (t time.Time, err error) { - st, err := strconv.ParseInt(fsCdr[HANGUP_TIME], 0, 64) + st, err := strconv.ParseInt(fsCdr[FS_HANGUP_TIME], 0, 64) t = time.Unix(0, st*1000) return } // Extracts duration as considered by the telecom switch func (fsCdr FSCdr) GetDuration() int64 { - dur, _ := strconv.ParseInt(fsCdr[DURATION], 0, 64) + dur, _ := strconv.ParseInt(fsCdr[FS_DURATION], 0, 64) return dur } diff --git a/cdrs/gencdr.go b/cdrs/gencdr.go new file mode 100644 index 000000000..8039337b3 --- /dev/null +++ b/cdrs/gencdr.go @@ -0,0 +1,163 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package cdrs + +import ( + "encoding/json" + "errors" + "github.com/cgrates/cgrates/utils" + "strconv" + "time" +) + +const ( + CDR_MAP = "variables" + DIRECTION = "direction" + ORIG_ID = "id" + SUBJECT = "subject" + ACCOUNT = "account" + DESTINATION = "destination" + REQTYPE = "reqtype" //prepaid or postpaid + TOR = "tor" + UUID = "uuid" // -Unique ID for this call leg + CSTMID = "cstmid" + CALL_DEST_NR = "dialed_extension" + PARK_TIME = "start_epoch" + ANSWER_TIME = "time_answer" + HANGUP_TIME = "time_hangup" + DURATION = "duration" + USERNAME = "user_name" + IP = "sip_local_network_addr" +) + +type GenCdr map[string]string + +func (genCdr GenCdr) New(body []byte) (utils.CDR, error) { + genCdr = make(map[string]string) + var tmp map[string]interface{} + var err error + if err = json.Unmarshal(body, &tmp); err == nil { + if variables, ok := tmp[CDR_MAP]; ok { + if variables, ok := variables.(map[string]interface{}); ok { + for k, v := range variables { + genCdr[k] = v.(string) + } + } + return genCdr, nil + } + } + return nil, err +} + +func (genCdr GenCdr) GetCgrId() string { + return utils.FSCgrId(genCdr[UUID]) +} +func (genCdr GenCdr) GetAccId() string { + return genCdr[UUID] +} +func (genCdr GenCdr) GetCdrHost() string { + return genCdr[FS_IP] +} +func (genCdr GenCdr) GetDirection() string { + //TODO: implement direction + return "OUT" +} +func (genCdr GenCdr) GetOrigId() string { + return genCdr[ORIG_ID] +} +func (genCdr GenCdr) GetSubject() string { + return utils.FirstNonEmpty(genCdr[SUBJECT], genCdr[USERNAME]) +} +func (genCdr GenCdr) GetAccount() string { + return utils.FirstNonEmpty(genCdr[ACCOUNT], genCdr[USERNAME]) +} + +// Charging destination number +func (genCdr GenCdr) GetDestination() string { + return utils.FirstNonEmpty(genCdr[DESTINATION], genCdr[CALL_DEST_NR]) +} + +func (genCdr GenCdr) GetTOR() string { + return utils.FirstNonEmpty(genCdr[TOR], cfg.DefaultTOR) +} + +func (genCdr GenCdr) GetTenant() string { + return utils.FirstNonEmpty(genCdr[CSTMID], cfg.DefaultTenant) +} +func (genCdr GenCdr) GetReqType() string { + return utils.FirstNonEmpty(genCdr[REQTYPE], cfg.DefaultReqType) +} +func (genCdr GenCdr) GetExtraFields() map[string]string { + extraFields := make(map[string]string, len(cfg.CDRSExtraFields)) + for _, field := range cfg.CDRSExtraFields { + extraFields[field] = genCdr[field] + } + return extraFields +} +func (genCdr GenCdr) GetFallbackSubj() string { + return cfg.DefaultSubject +} +func (genCdr GenCdr) GetAnswerTime() (t time.Time, err error) { + st, err := strconv.ParseInt(genCdr[ANSWER_TIME], 0, 64) + t = time.Unix(0, st*1000) + return +} +func (genCdr GenCdr) GetHangupTime() (t time.Time, err error) { + st, err := strconv.ParseInt(genCdr[HANGUP_TIME], 0, 64) + t = time.Unix(0, st*1000) + return +} + +// Extracts duration as considered by the telecom switch +func (genCdr GenCdr) GetDuration() int64 { + dur, _ := strconv.ParseInt(genCdr[DURATION], 0, 64) + return dur +} + +func (genCdr GenCdr) Store() (result string, err error) { + result += genCdr.GetCgrId() + "|" + result += genCdr.GetAccId() + "|" + result += genCdr.GetCdrHost() + "|" + result += genCdr.GetDirection() + "|" + result += genCdr.GetOrigId() + "|" + result += genCdr.GetSubject() + "|" + result += genCdr.GetAccount() + "|" + result += genCdr.GetDestination() + "|" + result += genCdr.GetTOR() + "|" + result += genCdr.GetAccId() + "|" + result += genCdr.GetTenant() + "|" + result += genCdr.GetReqType() + "|" + st, err := genCdr.GetAnswerTime() + if err != nil { + return "", err + } + result += strconv.FormatInt(st.UnixNano(), 10) + "|" + et, err := genCdr.GetHangupTime() + if err != nil { + return "", err + } + result += strconv.FormatInt(et.UnixNano(), 10) + "|" + result += strconv.FormatInt(genCdr.GetDuration(), 10) + "|" + result += genCdr.GetFallbackSubj() + "|" + return +} + +func (genCdr GenCdr) Restore(input string) error { + return errors.New("Not implemented") +} diff --git a/config/config.go b/config/config.go index 6368b7b85..9b6c2bca8 100644 --- a/config/config.go +++ b/config/config.go @@ -26,80 +26,81 @@ import ( ) const ( - DISABLED = "disabled" - INTERNAL = "internal" - JSON = "json" - GOB = "gob" - POSTGRES = "postgres" - MONGO = "mongo" - REDIS = "redis" - SAME = "same" - FS = "freeswitch" - PREPAID = "prepaid" - POSTPAID = "postpaid" + DISABLED = "disabled" + INTERNAL = "internal" + JSON = "json" + GOB = "gob" + POSTGRES = "postgres" + MONGO = "mongo" + REDIS = "redis" + SAME = "same" + FS = "freeswitch" + PREPAID = "prepaid" + POSTPAID = "postpaid" PSEUDOPREPAID = "pseudoprepaid" - RATED = "rated" + RATED = "rated" ) // Holds system configuration, defaults are overwritten with values from config file if found type CGRConfig struct { - DataDBType string - DataDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - DataDBPort string // The port to bind to. - DataDBName string // The name of the database to connect to. - DataDBUser string // The user to sign in as. - DataDBPass string // The user's password. + DataDBType string + DataDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + DataDBPort string // The port to bind to. + DataDBName string // The name of the database to connect to. + DataDBUser string // The user to sign in as. + DataDBPass string // The user's password. StorDBType string // Should reflect the database type used to store logs StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. StorDBPort string // The port to bind to. StorDBName string // The name of the database to connect to. StorDBUser string // The user to sign in as. StorDBPass string // The user's password. - RPCEncoding string // RPC encoding used on APIs: . - DefaultReqType string // Use this request type if not defined on top - DefaultTOR string // set default type of record - DefaultTenant string // set default tenant - DefaultSubject string // set default rating subject, useful in case of fallback - RaterEnabled bool // start standalone server (no balancer) - RaterBalancer string // balancer address host:port - RaterListen string // listening address host:port - RaterRoundingMethod string // Rounding method for the end price: - RaterRoundingDecimals int // Number of decimals to round end prices at - BalancerEnabled bool - BalancerListen string // Json RPC server address - SchedulerEnabled bool - CDRSListen string // CDRS's listening interface: . - CDRSfsJSONEnabled bool // Enable the handler for FreeSWITCH JSON CDRs: . - CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal> - CDRSExtraFields []string //Extra fields to store in CDRs - SMEnabled bool - SMSwitchType string - SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer - SMRaterReconnects int // Number of reconnect attempts to rater - SMDebitInterval int // the period to be debited in advanced during a call (in seconds) - MediatorEnabled bool // Starts Mediator service: . - MediatorListen string // Mediator's listening interface: . - MediatorRater string // Address where to reach the Rater: - MediatorRaterReconnects int // Number of reconnects to rater before giving up. - MediatorCDRType string // CDR type . - MediatorAccIdField string // Name of field identifying accounting id used during mediation. Use index number in case of .csv cdrs. - MediatorSubjectFields []string // Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorReqTypeFields []string // Name of request type fields to be used during mediation. Use index number in case of .csv cdrs. - MediatorDirectionFields []string // Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorTenantFields []string // Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorTORFields []string // Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorAccountFields []string // Name of account 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. - MediatorTimeAnswerFields []string // Name of time_start 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. - MediatorCDRInDir string // Absolute path towards the directory where the CDRs are kept (file stored CDRs). - MediatorCDROutDir string // Absolute path towards the directory where processed CDRs will be exported (file stored CDRs). - FreeswitchServer string // freeswitch address host:port - FreeswitchPass string // FS socket password - FreeswitchReconnects int // number of times to attempt reconnect after connect fails + RPCEncoding string // RPC encoding used on APIs: . + DefaultReqType string // Use this request type if not defined on top + DefaultTOR string // set default type of record + DefaultTenant string // set default tenant + DefaultSubject string // set default rating subject, useful in case of fallback + RaterEnabled bool // start standalone server (no balancer) + RaterBalancer string // balancer address host:port + RaterListen string // listening address host:port + RaterRoundingMethod string // Rounding method for the end price: + RaterRoundingDecimals int // Number of decimals to round end prices at + BalancerEnabled bool + BalancerListen string // Json RPC server address + SchedulerEnabled bool + CDRSListen string // CDRS's listening interface: . + CDRSfsJSONEnabled bool // Enable the handler for FreeSWITCH JSON CDRs: . + CDRSgenJSONEnabled bool // Enable the handler for Generic JSON CDRs: . + CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal> + CDRSExtraFields []string //Extra fields to store in CDRs + SMEnabled bool + SMSwitchType string + SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer + SMRaterReconnects int // Number of reconnect attempts to rater + SMDebitInterval int // the period to be debited in advanced during a call (in seconds) + MediatorEnabled bool // Starts Mediator service: . + MediatorListen string // Mediator's listening interface: . + MediatorRater string // Address where to reach the Rater: + MediatorRaterReconnects int // Number of reconnects to rater before giving up. + MediatorCDRType string // CDR type . + MediatorAccIdField string // Name of field identifying accounting id used during mediation. Use index number in case of .csv cdrs. + MediatorSubjectFields []string // Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorReqTypeFields []string // Name of request type fields to be used during mediation. Use index number in case of .csv cdrs. + MediatorDirectionFields []string // Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorTenantFields []string // Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorTORFields []string // Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorAccountFields []string // Name of account 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. + MediatorTimeAnswerFields []string // Name of time_start 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. + MediatorCDRInDir string // Absolute path towards the directory where the CDRs are kept (file stored CDRs). + MediatorCDROutDir string // Absolute path towards the directory where processed CDRs will be exported (file stored CDRs). + FreeswitchServer string // freeswitch address host:port + FreeswitchPass string // FS socket password + FreeswitchReconnects int // number of times to attempt reconnect after connect fails } -func ( self *CGRConfig ) setDefaults() error { +func (self *CGRConfig) setDefaults() error { self.DataDBType = REDIS self.DataDBHost = "127.0.0.1" self.DataDBPort = "6379" @@ -127,6 +128,7 @@ func ( self *CGRConfig ) setDefaults() error { self.SchedulerEnabled = false self.CDRSListen = "127.0.0.1:2022" self.CDRSfsJSONEnabled = false + self.CDRSgenJSONEnabled = false self.CDRSMediator = INTERNAL self.CDRSExtraFields = []string{} self.MediatorEnabled = false @@ -267,11 +269,14 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { if hasOpt = c.HasOption("cdrs", "freeswitch_json_enabled"); hasOpt { cfg.CDRSfsJSONEnabled, _ = c.GetBool("cdrs", "freeswitch_json_enabled") } + if hasOpt = c.HasOption("cdrs", "generic_json_enabled"); hasOpt { + cfg.CDRSgenJSONEnabled, _ = c.GetBool("cdrs", "generic_json_enabled") + } if hasOpt = c.HasOption("cdrs", "mediator"); hasOpt { cfg.CDRSMediator, _ = c.GetString("cdrs", "mediator") } if hasOpt = c.HasOption("cdrs", "extra_fields"); hasOpt { - if cfg.CDRSExtraFields, errParse = ConfigSlice( c, "cdrs", "extra_fields"); errParse!=nil { + if cfg.CDRSExtraFields, errParse = ConfigSlice(c, "cdrs", "extra_fields"); errParse != nil { return nil, errParse } } @@ -294,47 +299,47 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { cfg.MediatorAccIdField, _ = c.GetString("mediator", "accid_field") } if hasOpt = c.HasOption("mediator", "subject_fields"); hasOpt { - if cfg.MediatorSubjectFields, errParse = ConfigSlice( c, "mediator", "subject_fields"); errParse!=nil { + if cfg.MediatorSubjectFields, errParse = ConfigSlice(c, "mediator", "subject_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "reqtype_fields"); hasOpt { - if cfg.MediatorReqTypeFields, errParse = ConfigSlice( c, "mediator", "reqtype_fields"); errParse!=nil { + if cfg.MediatorReqTypeFields, errParse = ConfigSlice(c, "mediator", "reqtype_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "direction_fields"); hasOpt { - if cfg.MediatorDirectionFields, errParse = ConfigSlice( c, "mediator", "direction_fields"); errParse!=nil { + if cfg.MediatorDirectionFields, errParse = ConfigSlice(c, "mediator", "direction_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "tenant_fields"); hasOpt { - if cfg.MediatorTenantFields, errParse = ConfigSlice( c, "mediator", "tenant_fields"); errParse!=nil { + if cfg.MediatorTenantFields, errParse = ConfigSlice(c, "mediator", "tenant_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "tor_fields"); hasOpt { - if cfg.MediatorTORFields, errParse = ConfigSlice( c, "mediator", "tor_fields"); errParse!=nil { + if cfg.MediatorTORFields, errParse = ConfigSlice(c, "mediator", "tor_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "account_fields"); hasOpt { - if cfg.MediatorAccountFields, errParse = ConfigSlice( c, "mediator", "account_fields"); errParse!=nil { + if cfg.MediatorAccountFields, errParse = ConfigSlice(c, "mediator", "account_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "destination_fields"); hasOpt { - if cfg.MediatorDestFields, errParse = ConfigSlice( c, "mediator", "destination_fields"); errParse!=nil { + if cfg.MediatorDestFields, errParse = ConfigSlice(c, "mediator", "destination_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "time_answer_fields"); hasOpt { - if cfg.MediatorTimeAnswerFields, errParse = ConfigSlice( c, "mediator", "time_answer_fields"); errParse!=nil { + if cfg.MediatorTimeAnswerFields, errParse = ConfigSlice(c, "mediator", "time_answer_fields"); errParse != nil { return nil, errParse } } if hasOpt = c.HasOption("mediator", "duration_fields"); hasOpt { - if cfg.MediatorDurationFields, errParse = ConfigSlice( c, "mediator", "duration_fields"); errParse!=nil { + if cfg.MediatorDurationFields, errParse = ConfigSlice(c, "mediator", "duration_fields"); errParse != nil { return nil, errParse } } diff --git a/config/config_test.go b/config/config_test.go index 00a6d37ce..0911bccbf 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -20,53 +20,54 @@ package config import ( "fmt" - "testing" - "reflect" "github.com/cgrates/cgrates/utils" + "reflect" + "testing" ) // Make sure defaults did not change by mistake func TestDefaults(t *testing.T) { cfg := &CGRConfig{} errSet := cfg.setDefaults() - if errSet != nil { + if errSet != nil { t.Log(fmt.Sprintf("Coud not set defaults: %s!", errSet.Error())) t.FailNow() } eCfg := &CGRConfig{} - eCfg.DataDBType = REDIS - eCfg.DataDBHost = "127.0.0.1" - eCfg.DataDBPort = "6379" - eCfg.DataDBName = "10" - eCfg.DataDBUser = "" - eCfg.DataDBPass = "" + eCfg.DataDBType = REDIS + eCfg.DataDBHost = "127.0.0.1" + eCfg.DataDBPort = "6379" + eCfg.DataDBName = "10" + eCfg.DataDBUser = "" + eCfg.DataDBPass = "" eCfg.StorDBType = utils.MYSQL - eCfg.StorDBHost = "localhost" - eCfg.StorDBPort = "3306" - eCfg.StorDBName = "cgrates" - eCfg.StorDBUser = "cgrates" - eCfg.StorDBPass = "CGRateS.org" + eCfg.StorDBHost = "localhost" + eCfg.StorDBPort = "3306" + eCfg.StorDBName = "cgrates" + eCfg.StorDBUser = "cgrates" + eCfg.StorDBPass = "CGRateS.org" eCfg.RPCEncoding = JSON eCfg.DefaultReqType = RATED - eCfg.DefaultTOR = "0" - eCfg.DefaultTenant = "0" - eCfg.DefaultSubject = "0" - eCfg.RaterEnabled = false - eCfg.RaterBalancer = DISABLED - eCfg.RaterListen = "127.0.0.1:2012" + eCfg.DefaultTOR = "0" + eCfg.DefaultTenant = "0" + eCfg.DefaultSubject = "0" + eCfg.RaterEnabled = false + eCfg.RaterBalancer = DISABLED + eCfg.RaterListen = "127.0.0.1:2012" eCfg.RaterRoundingMethod = utils.ROUNDING_MIDDLE eCfg.RaterRoundingDecimals = 4 - eCfg.BalancerEnabled = false - eCfg.BalancerListen = "127.0.0.1:2013" - eCfg.SchedulerEnabled = false - eCfg.CDRSListen = "127.0.0.1:2022" - eCfg.CDRSfsJSONEnabled = false + eCfg.BalancerEnabled = false + eCfg.BalancerListen = "127.0.0.1:2013" + eCfg.SchedulerEnabled = false + eCfg.CDRSListen = "127.0.0.1:2022" + eCfg.CDRSfsJSONEnabled = false + eCfg.CDRSgenJSONEnabled = false eCfg.CDRSMediator = INTERNAL eCfg.CDRSExtraFields = []string{} - eCfg.MediatorEnabled = false - eCfg.MediatorListen = "127.0.0.1:2032" - eCfg.MediatorRater = "127.0.0.1:2012" - eCfg.MediatorRaterReconnects = 3 + eCfg.MediatorEnabled = false + eCfg.MediatorListen = "127.0.0.1:2032" + eCfg.MediatorRater = "127.0.0.1:2012" + eCfg.MediatorRaterReconnects = 3 eCfg.MediatorCDRType = "freeswitch_http_json" eCfg.MediatorAccIdField = "accid" eCfg.MediatorSubjectFields = []string{"subject"} @@ -78,17 +79,17 @@ func TestDefaults(t *testing.T) { eCfg.MediatorDestFields = []string{"destination"} eCfg.MediatorTimeAnswerFields = []string{"time_answer"} eCfg.MediatorDurationFields = []string{"duration"} - eCfg.MediatorCDRInDir = "/var/log/freeswitch/cdr-csv" + eCfg.MediatorCDRInDir = "/var/log/freeswitch/cdr-csv" eCfg.MediatorCDROutDir = "/var/log/cgrates/cdr/out/freeswitch/csv" - eCfg.SMEnabled = false - eCfg.SMSwitchType = FS - eCfg.SMRater = "127.0.0.1:2012" - eCfg.SMRaterReconnects = 3 - eCfg.SMDebitInterval = 10 - eCfg.FreeswitchServer = "127.0.0.1:8021" - eCfg.FreeswitchPass = "ClueCon" - eCfg.FreeswitchReconnects = 5 - if !reflect.DeepEqual(cfg ,eCfg ){ + eCfg.SMEnabled = false + eCfg.SMSwitchType = FS + eCfg.SMRater = "127.0.0.1:2012" + eCfg.SMRaterReconnects = 3 + eCfg.SMDebitInterval = 10 + eCfg.FreeswitchServer = "127.0.0.1:8021" + eCfg.FreeswitchPass = "ClueCon" + eCfg.FreeswitchReconnects = 5 + if !reflect.DeepEqual(cfg, eCfg) { t.Log(eCfg) t.Log(cfg) t.Error("Defaults different than expected!") @@ -99,22 +100,22 @@ func TestDefaults(t *testing.T) { func TestDefaultsSanity(t *testing.T) { cfg := &CGRConfig{} errSet := cfg.setDefaults() - if errSet != nil { + if errSet != nil { t.Log(fmt.Sprintf("Coud not set defaults: %s!", errSet.Error())) t.FailNow() } - if (cfg.RaterListen != INTERNAL && - (cfg.RaterListen == cfg.BalancerListen || + if (cfg.RaterListen != INTERNAL && + (cfg.RaterListen == cfg.BalancerListen || cfg.RaterListen == cfg.CDRSListen || - cfg.RaterListen == cfg.MediatorListen )) || + cfg.RaterListen == cfg.MediatorListen)) || (cfg.BalancerListen != INTERNAL && (cfg.BalancerListen == cfg.CDRSListen || - cfg.BalancerListen == cfg.MediatorListen ))|| + cfg.BalancerListen == cfg.MediatorListen)) || (cfg.CDRSListen != INTERNAL && cfg.CDRSListen == cfg.MediatorListen) { - t.Error("Listen defaults on the same port!") + t.Error("Listen defaults on the same port!") } } -// Load config from file and make sure we have all set +// Load config from file and make sure we have all set func TestConfigFromFile(t *testing.T) { cfgPth := "test_data.txt" cfg, err := NewCGRConfig(&cfgPth) @@ -151,6 +152,7 @@ func TestConfigFromFile(t *testing.T) { eCfg.SchedulerEnabled = true eCfg.CDRSListen = "test" eCfg.CDRSfsJSONEnabled = true + eCfg.CDRSgenJSONEnabled = true eCfg.CDRSMediator = "test" eCfg.CDRSExtraFields = []string{"test"} eCfg.MediatorEnabled = true @@ -178,9 +180,9 @@ func TestConfigFromFile(t *testing.T) { eCfg.FreeswitchServer = "test" eCfg.FreeswitchPass = "test" eCfg.FreeswitchReconnects = 99 - if !reflect.DeepEqual(cfg ,eCfg ){ + if !reflect.DeepEqual(cfg, eCfg) { t.Log(eCfg) t.Log(cfg) t.Error("Loading of configuration from file failed!") } -} +} diff --git a/config/test_data.txt b/config/test_data.txt index 9461ce9a3..b8ed219d9 100644 --- a/config/test_data.txt +++ b/config/test_data.txt @@ -38,6 +38,7 @@ rounding_decimals = 99 # Number of decimals to round prices at [cdrs] listen=test # CDRS's listening interface: . freeswitch_json_enabled=true # Enable the handler for FreeSWITCH JSON CDRs: . +generic_json_enabled=true # Enable the handler for generic JSON CDRs: . mediator = test # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> extra_fields = test # Extra fields to store in CDRs