mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-12 02:26:26 +05:00
Merge branch 'master' into load
This commit is contained in:
28
data/conf/samples/fscsv/cgrates.json
Normal file
28
data/conf/samples/fscsv/cgrates.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
|
||||
"listen": {
|
||||
"rpc_json": ":2012", // RPC JSON listening address
|
||||
"rpc_gob": ":2013", // RPC GOB listening address
|
||||
"http": ":2080", // HTTP listening address
|
||||
},
|
||||
|
||||
"rater": {
|
||||
"enabled": true, // enable Rater service: <true|false>
|
||||
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
|
||||
},
|
||||
|
||||
"scheduler": {
|
||||
"enabled": true, // start Scheduler service: <true|false>
|
||||
},
|
||||
|
||||
"cdrs": {
|
||||
"enabled": true, // start the CDR Server service: <true|false>
|
||||
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
|
||||
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
|
||||
},
|
||||
|
||||
"cdr_stats": {
|
||||
"enabled": true, // starts the cdrstats service: <true|false>
|
||||
},
|
||||
|
||||
}
|
||||
27
data/conf/samples/fscsv/freeswitch_csvcdr.json
Normal file
27
data/conf/samples/fscsv/freeswitch_csvcdr.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
// Contains CDRC template for FreeSWITCH CDR
|
||||
|
||||
"cdrc": {
|
||||
"CDRC-CSV2": {
|
||||
"enabled": true, // enable CDR client functionality
|
||||
"cdr_in_dir": "/tmp/cgrates/cdrc_fs/in", // absolute path towards the directory where the CDRs are stored
|
||||
"cdr_out_dir": "/tmp/cgrates/cdrc_fs/out", // absolute path towards the directory where processed CDRs will be moved
|
||||
"cdr_source_id": "fs_csv", // free form field, tag identifying the source of the CDRs within CDRS database
|
||||
"cdr_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
|
||||
{"tag": "tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "^*voice", "mandatory": true},
|
||||
{"tag": "accid", "cdr_field_id": "accid", "type": "cdrfield", "value": "10", "mandatory": true},
|
||||
{"tag": "reqtype", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "^rated", "mandatory": true},
|
||||
{"tag": "direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "^*out", "mandatory": true},
|
||||
{"tag": "tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "^cgrates.org", "mandatory": true},
|
||||
{"tag": "category", "cdr_field_id": "category", "type": "cdrfield", "value": "^call", "mandatory": true},
|
||||
{"tag": "account", "cdr_field_id": "account", "type": "cdrfield", "value": "12", "mandatory": true},
|
||||
{"tag": "subject", "cdr_field_id": "subject", "type": "cdrfield", "value": "12", "mandatory": true},
|
||||
{"tag": "destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "2", "mandatory": true},
|
||||
{"tag": "setup_time", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "4", "mandatory": true},
|
||||
{"tag": "answer_time", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "5", "mandatory": true},
|
||||
{"tag": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "~8:s/^(\\d+)$/${1}s/", "mandatory": true},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
@@ -29,6 +29,12 @@ event_route[dialog:end] {
|
||||
route(CGR_CALL_END);
|
||||
}
|
||||
|
||||
# Called by Kamailio on local disconnect
|
||||
event_route[tm:local-request] {
|
||||
route(CGR_CALL_END);
|
||||
}
|
||||
|
||||
|
||||
# Send AUTH_REQUEST to CGRateS
|
||||
route[CGRATES_AUTH_REQUEST] {
|
||||
# Auth INVITEs with CGRateS
|
||||
@@ -106,6 +112,7 @@ route[CGR_CALL_END] {
|
||||
\"cgr_destination\":\"$dlg_var(cgrDestination)\",
|
||||
\"cgr_answertime\":\"$dlg(start_ts)\",
|
||||
\"cgr_duration\":\"$var(callDur)\",
|
||||
\"cgr_supplier\":\"$dlg_var(cgrSupplier)\"}");
|
||||
\"cgr_supplier\":\"$dlg_var(cgrSupplier)\",
|
||||
\"cgr_disconnectcause\":\"$T_reply_code\"}");
|
||||
}
|
||||
|
||||
|
||||
@@ -187,18 +187,15 @@ request_route {
|
||||
|
||||
# Here will land requests after processing them with CGRateS. Call RELAY or other routes following this route
|
||||
route[CGRATES_AUTH_REPLY] {
|
||||
xlog("CGRATES_AUTH_REPLY reply, got CgrError: $var(CgrError)");
|
||||
if $var(CgrError) != "" {
|
||||
xlog("CGR_AUTH_ERROR: $var(CgrError)");
|
||||
sl_send_reply("503","CGR_ERROR");
|
||||
exit;
|
||||
}
|
||||
xlog("CGRATES_AUTH_REPLY, CgrMaxSessionTime: $var(CgrMaxSessionTime)");
|
||||
if $var(CgrMaxSessionTime) != -1 && !dlg_set_timeout("$var(CgrMaxSessionTime)") {
|
||||
sl_send_reply("503","CGR_MAX_SESSION_TIME_ERROR");
|
||||
exit;
|
||||
}
|
||||
xlog("CGRATES_AUTH_REPLY, CgrSuppliers: $var(CgrSuppliers)");
|
||||
if $var(CgrSuppliers) != "" { # Enforce the supplier variable to the first one received from CGRateS, more for testing purposes
|
||||
$dlg_var(cgrSupplier) = $(var(CgrSuppliers){s.select,0,,});
|
||||
}
|
||||
|
||||
@@ -304,35 +304,46 @@ func TestTutKamCallsCall1007To1002(t *testing.T) {
|
||||
}
|
||||
|
||||
/*
|
||||
// Should hangup at 62 seconds
|
||||
func TestTutKamCallsCall1006To1007(t *testing.T) {
|
||||
// Should hangup at 62 seconds, disconnect from SM
|
||||
func TestTutKamCallsCall1007To1007(t *testing.T) {
|
||||
if !*testCalls {
|
||||
return
|
||||
}
|
||||
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1",
|
||||
"sip:127.0.0.1:5060", time.Duration(90)*time.Second, 5077); err != nil {
|
||||
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1",
|
||||
"sip:127.0.0.1:5060", time.Duration(75)*time.Second, 5077); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Should hangup at 62 seconds, disconnect from Kamailio
|
||||
func TestTutKamCallsCall1003To1007(t *testing.T) {
|
||||
if !*testCalls {
|
||||
return
|
||||
}
|
||||
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1",
|
||||
"sip:127.0.0.1:5060", time.Duration(73)*time.Second, 5078); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Call from 1001 (prepaid) to 1007, should not cost more than 62 which is MaxCallCost
|
||||
func TestTutKamCallsCall1001To1007(t *testing.T) {
|
||||
if !*testCalls {
|
||||
return
|
||||
}
|
||||
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1",
|
||||
"sip:127.0.0.1:5060", time.Duration(100)*time.Second, 5079); err != nil {
|
||||
"sip:127.0.0.1:5060", time.Duration(70)*time.Second, 5079); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Make sure account was debited properly
|
||||
func TestTutKamCallsAccount1001(t *testing.T) {
|
||||
if !*testCalls {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration(70) * time.Second) // Allow calls to finish before start querying the results
|
||||
time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results
|
||||
var reply *engine.Account
|
||||
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001", Direction: "*out"}
|
||||
if err := tutKamCallsRpc.Call("ApierV1.GetAccount", attrs, &reply); err != nil {
|
||||
@@ -353,7 +364,7 @@ func TestTutKamCallsCdrs(t *testing.T) {
|
||||
req := utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{utils.META_DEFAULT}}
|
||||
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if len(reply) != 1 {
|
||||
} else if len(reply) != 2 {
|
||||
t.Error("Unexpected number of CDRs returned: ", len(reply))
|
||||
} else {
|
||||
if reply[0].CdrSource != "KAMAILIO_CGR_CALL_END" {
|
||||
@@ -363,7 +374,7 @@ func TestTutKamCallsCdrs(t *testing.T) {
|
||||
t.Errorf("Unexpected ReqType for CDR: %+v", reply[0])
|
||||
}
|
||||
if reply[0].Usage != "67" { // Usage as seconds
|
||||
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
|
||||
t.Errorf("Unexpected Usage for 428CDR: %+v", reply[0])
|
||||
}
|
||||
if reply[0].Supplier != "suppl2" { // Usage as seconds
|
||||
t.Errorf("Unexpected Supplier for CDR: %+v", reply[0])
|
||||
@@ -372,7 +383,7 @@ func TestTutKamCallsCdrs(t *testing.T) {
|
||||
req = utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{"derived_run1"}, FilterOnDerived: true}
|
||||
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if len(reply) != 1 {
|
||||
} else if len(reply) != 2 {
|
||||
t.Error("Unexpected number of CDRs returned: ", len(reply))
|
||||
} else {
|
||||
if reply[0].ReqType != utils.META_RATED {
|
||||
@@ -503,43 +514,43 @@ func TestTutKamCallsCdrStatsAfter(t *testing.T) {
|
||||
return
|
||||
}
|
||||
var statMetrics map[string]float64
|
||||
eMetrics := map[string]float64{engine.ACC: 0.9707714286, engine.ACD: 64.2857142857, engine.ASR: 100}
|
||||
eMetrics := map[string]float64{engine.ACC: 0.9015222222, engine.ACD: 65.5555555556, engine.ASR: 100}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: utils.META_DEFAULT}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.ACC: 0.927, engine.ACD: 63.8333333333, engine.ASR: 100, engine.TCC: 5.562, engine.TCD: 383}
|
||||
eMetrics = map[string]float64{engine.ACC: 0.8829, engine.ACD: 64.7142857143, engine.ASR: 100, engine.TCC: 6.1803, engine.TCD: 453}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.TCC: 5.562, engine.TCD: 383, engine.ACC: 0.0217, engine.ACD: 67, engine.ASR: 100}
|
||||
eMetrics = map[string]float64{engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 0.32, engine.ACD: 68.5, engine.ASR: 100}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.ACD: 61, engine.ASR: 100, engine.TCC: 5.562, engine.TCD: 383, engine.ACC: 1.2334}
|
||||
eMetrics = map[string]float64{engine.ACD: 61, engine.ASR: 100, engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 1.2334}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.TCC: 5.562, engine.TCD: 383, engine.ACC: 1.2334, engine.ACD: -1, engine.ASR: -1}
|
||||
eMetrics = map[string]float64{engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 1.2334, engine.ACD: -1, engine.ASR: -1}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.ACC: 1.2334, engine.ACD: 62, engine.ASR: 100, engine.TCC: 3.7002, engine.TCD: 186}
|
||||
eMetrics = map[string]float64{engine.ACC: 1.00404, engine.ACD: 65.2, engine.ASR: 100, engine.TCC: 5.0202, engine.TCD: 326}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
|
||||
}
|
||||
eMetrics = map[string]float64{engine.ACD: 67, engine.ASR: 100, engine.TCC: 1.2551, engine.TCD: 134, engine.ACC: 0.62755}
|
||||
eMetrics = map[string]float64{engine.ACD: 67, engine.ASR: 100, engine.TCC: 1.2534, engine.TCD: 134, engine.ACC: 0.6267}
|
||||
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil {
|
||||
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
|
||||
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
|
||||
|
||||
@@ -51,7 +51,7 @@ const (
|
||||
)
|
||||
|
||||
var primaryFields = []string{EVENT, CALLID, FROM_TAG, HASH_ENTRY, HASH_ID, CGR_ACCOUNT, CGR_SUBJECT, CGR_DESTINATION,
|
||||
CGR_CATEGORY, CGR_TENANT, CGR_REQTYPE, CGR_ANSWERTIME, CGR_SETUPTIME, CGR_STOPTIME, CGR_DURATION}
|
||||
CGR_CATEGORY, CGR_TENANT, CGR_REQTYPE, CGR_ANSWERTIME, CGR_SETUPTIME, CGR_STOPTIME, CGR_DURATION, utils.CGR_SUPPLIER, utils.CGR_DISCONNECT_CAUSE}
|
||||
|
||||
type KamAuthReply struct {
|
||||
Event string // Kamailio will use this to differentiate between requests and replies
|
||||
@@ -326,8 +326,10 @@ func (kev KamEvent) AsStoredCdr() *engine.StoredCdr {
|
||||
storCdr.AnswerTime, _ = kev.GetAnswerTime(utils.META_DEFAULT)
|
||||
storCdr.Usage, _ = kev.GetDuration(utils.META_DEFAULT)
|
||||
storCdr.Supplier = kev.GetSupplier(utils.META_DEFAULT)
|
||||
storCdr.DisconnectCause = kev.GetDisconnectCause(utils.META_DEFAULT)
|
||||
storCdr.ExtraFields = kev.GetExtraFields()
|
||||
storCdr.Cost = -1
|
||||
|
||||
return storCdr
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestNewKamEvent(t *testing.T) {
|
||||
"cgr_answertime":"1419839310",
|
||||
"cgr_duration":"3",
|
||||
"cgr_supplier":"supplier2",
|
||||
"cgr_disconnect_cause": "200"}`
|
||||
"cgr_disconnectcause": "200"}`
|
||||
eKamEv := KamEvent{"event": "CGR_CALL_END", "callid": "46c01a5c249b469e76333fc6bfa87f6a@0:0:0:0:0:0:0:0", "from_tag": "bf71ad59", "to_tag": "7351fecf",
|
||||
"cgr_reqtype": utils.META_POSTPAID, "cgr_account": "1001", "cgr_destination": "1002", "cgr_answertime": "1419839310", "cgr_duration": "3", utils.CGR_SUPPLIER: "supplier2",
|
||||
utils.CGR_DISCONNECT_CAUSE: "200"}
|
||||
|
||||
@@ -185,7 +185,7 @@ const (
|
||||
CONFIG_DIR = "/etc/cgrates/"
|
||||
CGR_SUPPLIER = "cgr_supplier"
|
||||
DISCONNECT_CAUSE = "disconnect_cause"
|
||||
CGR_DISCONNECT_CAUSE = "cgr_disconnect_cause"
|
||||
CGR_DISCONNECT_CAUSE = "cgr_disconnectcause"
|
||||
CGR_COMPUTELCR = "cgr_computelcr"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user