mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-23 16:18:44 +05:00
Merge branch 'master' of https://github.com/cgrates/cgrates
This commit is contained in:
26
cdrs/cdrs.go
26
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
163
cdrs/gencdr.go
Normal file
163
cdrs/gencdr.go
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
147
config/config.go
147
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: <gob|json>.
|
||||
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: <up|middle|down>
|
||||
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: <x.y.z.y:1234>.
|
||||
CDRSfsJSONEnabled bool // Enable the handler for FreeSWITCH JSON CDRs: <enabled|disabled>.
|
||||
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: <true|false>.
|
||||
MediatorListen string // Mediator's listening interface: <internal>.
|
||||
MediatorRater string // Address where to reach the Rater: <internal|x.y.z.y:1234>
|
||||
MediatorRaterReconnects int // Number of reconnects to rater before giving up.
|
||||
MediatorCDRType string // CDR type <freeswitch_http_json|freeswitch_file_csv>.
|
||||
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: <gob|json>.
|
||||
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: <up|middle|down>
|
||||
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: <x.y.z.y:1234>.
|
||||
CDRSfsJSONEnabled bool // Enable the handler for FreeSWITCH JSON CDRs: <enabled|disabled>.
|
||||
CDRSgenJSONEnabled bool // Enable the handler for Generic JSON CDRs: <enabled|disabled>.
|
||||
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: <true|false>.
|
||||
MediatorListen string // Mediator's listening interface: <internal>.
|
||||
MediatorRater string // Address where to reach the Rater: <internal|x.y.z.y:1234>
|
||||
MediatorRaterReconnects int // Number of reconnects to rater before giving up.
|
||||
MediatorCDRType string // CDR type <freeswitch_http_json|freeswitch_file_csv>.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ rounding_decimals = 99 # Number of decimals to round prices at
|
||||
[cdrs]
|
||||
listen=test # CDRS's listening interface: <x.y.z.y:1234>.
|
||||
freeswitch_json_enabled=true # Enable the handler for FreeSWITCH JSON CDRs: <true|false>.
|
||||
generic_json_enabled=true # Enable the handler for generic JSON CDRs: <true|false>.
|
||||
mediator = test # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
|
||||
extra_fields = test # Extra fields to store in CDRs
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user