mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-22 15:48:44 +05:00
Diameter ASR implementation
This commit is contained in:
@@ -87,6 +87,15 @@ func TestDiamItMaxConn(t *testing.T) {
|
||||
t.Run(diamConfigDIR, testDiamItKillEngine)
|
||||
}
|
||||
|
||||
func TestDiamItSessionDisconnect(t *testing.T) {
|
||||
diamConfigDIR = "diamagent"
|
||||
for _, stest := range sTestsDiam[:7] {
|
||||
t.Run(diamConfigDIR, stest)
|
||||
}
|
||||
t.Run(diamConfigDIR, testDiamInitWithSessionDisconnect)
|
||||
t.Run(diamConfigDIR, testDiamItKillEngine)
|
||||
}
|
||||
|
||||
func testDiamItInitCfg(t *testing.T) {
|
||||
daCfgPath = path.Join(*dataDir, "conf", "samples", diamConfigDIR)
|
||||
// Init config first
|
||||
@@ -150,7 +159,7 @@ func testDiamItTPFromFolder(t *testing.T) {
|
||||
if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
time.Sleep(time.Duration(1000) * time.Millisecond) // Give time for scheduler to execute topups
|
||||
time.Sleep(time.Duration(1 * time.Second)) // Give time for scheduler to execute topups
|
||||
}
|
||||
|
||||
func testDiamItDryRun(t *testing.T) {
|
||||
@@ -721,6 +730,97 @@ func testDiamItCCRSMS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testDiamInitWithSessionDisconnect(t *testing.T) {
|
||||
attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org",
|
||||
Account: "testDiamInitWithSessionDisconnect",
|
||||
BalanceType: utils.VOICE,
|
||||
BalanceID: utils.StringPointer("testDiamInitWithSessionDisconnect"),
|
||||
Value: utils.Float64Pointer(1 * float64(time.Second)),
|
||||
RatingSubject: utils.StringPointer("*zero1ms")}
|
||||
var reply string
|
||||
if err := apierRpc.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else if reply != utils.OK {
|
||||
t.Errorf("Received: %s", reply)
|
||||
}
|
||||
sessID := "bb97be2b9f37c2be9614fff71c8b1d08bdisconnect"
|
||||
m := diam.NewRequest(diam.CreditControl, 4, nil)
|
||||
m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(sessID))
|
||||
m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("192.168.1.1"))
|
||||
m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
|
||||
m.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
|
||||
m.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1))
|
||||
m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1))
|
||||
m.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA"))
|
||||
m.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
|
||||
m.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("testSessionDisconnect"))
|
||||
m.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2018, 10, 4, 14, 42, 20, 0, time.UTC)))
|
||||
m.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("testDiamInitWithSessionDisconnect"))
|
||||
m.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Type
|
||||
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("testDiamInitWithSessionDisconnect")), // Subscription-Id-Data
|
||||
}})
|
||||
m.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0))
|
||||
m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(300))}})
|
||||
m.NewAVP(avp.UsedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(0))}})
|
||||
m.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
|
||||
AVP: []*diam.AVP{
|
||||
diam.NewAVP(831, avp.Mbit, 10415, datatype.UTF8String("1001")), // Calling-Party-Address
|
||||
diam.NewAVP(832, avp.Mbit, 10415, datatype.UTF8String("1002")), // Called-Party-Address
|
||||
diam.NewAVP(20327, avp.Mbit, 2011, datatype.UTF8String("1002")), // Real-Called-Number
|
||||
diam.NewAVP(20339, avp.Mbit, 2011, datatype.Unsigned32(0)), // Charge-Flow-Type
|
||||
diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("")), // Calling-Vlr-Number
|
||||
diam.NewAVP(20303, avp.Mbit, 2011, datatype.UTF8String("")), // Calling-CellID-Or-SAI
|
||||
diam.NewAVP(20313, avp.Mbit, 2011, datatype.UTF8String("")), // Bearer-Capability
|
||||
diam.NewAVP(20321, avp.Mbit, 2011, datatype.UTF8String("bb97be2b9f37c2be9614fff71c8b1d08bdisconnect")), // Call-Reference-Number
|
||||
diam.NewAVP(20322, avp.Mbit, 2011, datatype.UTF8String("")), // MSC-Address
|
||||
diam.NewAVP(20324, avp.Mbit, 2011, datatype.Unsigned32(0)), // Time-Zone
|
||||
diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("")), // Called-Party-NP
|
||||
diam.NewAVP(20386, avp.Mbit, 2011, datatype.UTF8String("")), // SSP-Time
|
||||
},
|
||||
}),
|
||||
}})
|
||||
if err := diamClnt.SendMessage(m); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
msg := diamClnt.ReceivedMessage(rplyTimeout)
|
||||
if msg == nil {
|
||||
t.Fatal("No message returned")
|
||||
}
|
||||
// Result-Code
|
||||
eVal := "2001"
|
||||
if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(avps) == 0 {
|
||||
t.Error("Missing AVP")
|
||||
} else if val, err := diamAVPAsString(avps[0]); err != nil {
|
||||
t.Error(err)
|
||||
} else if val != eVal {
|
||||
t.Errorf("expecting: %s, received: <%s>", eVal, val)
|
||||
}
|
||||
time.Sleep(time.Duration(2000 * time.Millisecond))
|
||||
msg = diamClnt.ReceivedMessage(rplyTimeout)
|
||||
if msg == nil {
|
||||
t.Fatal("No message returned")
|
||||
}
|
||||
if avps, err := msg.FindAVPsWithPath([]interface{}{"Session-Id"}, dict.UndefinedVendorID); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(avps) == 0 {
|
||||
t.Error("Missing AVP")
|
||||
} else if val, err := diamAVPAsString(avps[0]); err != nil {
|
||||
t.Error(err)
|
||||
} else if val != sessID {
|
||||
t.Errorf("expecting: %s, received: <%s>", sessID, val)
|
||||
}
|
||||
}
|
||||
|
||||
func testDiamItKillEngine(t *testing.T) {
|
||||
if err := engine.KillEngine(1000); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -162,7 +162,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
|
||||
return
|
||||
}
|
||||
// cache message for ASR
|
||||
if da.cgrCfg.DiameterAgentCfg().ASRTempalte != "" {
|
||||
if da.cgrCfg.DiameterAgentCfg().ASRTemplate != "" {
|
||||
sessID, err := diamDP.FieldAsString([]string{"Session-Id"})
|
||||
if err != nil {
|
||||
utils.Logger.Warning(
|
||||
@@ -172,7 +172,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
|
||||
}
|
||||
// cache message data needed for building up the ASR
|
||||
engine.Cache.Set(utils.CacheDiameterMessages, sessID, &diamMsgData{c, m, reqVars},
|
||||
nil, false, utils.NonTransactional)
|
||||
nil, true, utils.NonTransactional)
|
||||
}
|
||||
// handle MaxActiveReqs
|
||||
if da.cgrCfg.DiameterAgentCfg().MaxActiveReqs != -1 {
|
||||
@@ -396,14 +396,14 @@ func (da *DiameterAgent) V1DisconnectSession(args utils.AttrDisconnectSession, r
|
||||
dmd.vars, nil, nil,
|
||||
da.cgrCfg.GeneralCfg().DefaultTenant,
|
||||
da.cgrCfg.GeneralCfg().DefaultTimezone, da.filterS)
|
||||
nM, err := aReq.AsNavigableMap(da.cgrCfg.DiameterAgentCfg().Templates[da.cgrCfg.DiameterAgentCfg().ASRTempalte])
|
||||
nM, err := aReq.AsNavigableMap(da.cgrCfg.DiameterAgentCfg().Templates[da.cgrCfg.DiameterAgentCfg().ASRTemplate])
|
||||
if err != nil {
|
||||
utils.Logger.Warning(
|
||||
fmt.Sprintf("<%s> cannot disconnect session with OriginID: <%s>, err: %s",
|
||||
utils.DiameterAgent, ssID, err.Error()))
|
||||
return utils.ErrServerError
|
||||
}
|
||||
m := diam.NewMessage(dmd.m.Header.CommandCode, 0, dmd.m.Header.ApplicationID, 0, 0, dmd.m.Dictionary())
|
||||
m := diam.NewRequest(dmd.m.Header.CommandCode, dmd.m.Header.ApplicationID, dmd.m.Dictionary())
|
||||
if err = updateDiamMsgFromNavMap(m, nM, da.cgrCfg.GeneralCfg().DefaultTimezone); err != nil {
|
||||
utils.Logger.Warning(
|
||||
fmt.Sprintf("<%s> cannot disconnect session with OriginID: <%s>, err: %s",
|
||||
|
||||
@@ -401,22 +401,22 @@ const CGRATES_CFG_JSON = `
|
||||
"value": "~*req.CC-Request-Number", "mandatory": true},
|
||||
],
|
||||
"*asr": [
|
||||
{"tag": "SessionId", "field_id": "Session-Id", "type": "*composed",
|
||||
"value": "~*req.OriginID", "mandatory": true},
|
||||
{"tag": "OriginHost", "field_id": "Origin-Host", "type": "*composed",
|
||||
"value": "~*vars.DestinationHost", "mandatory": true},
|
||||
{"tag": "OriginRealm", "field_id": "Origin-Realm", "type": "*composed",
|
||||
"value": "~*vars.DestinationRealm", "mandatory": true},
|
||||
{"tag": "DestinationRealm", "field_id": "Destination-Realm", "type": "*composed",
|
||||
"value": "~*vars.OriginRealm", "mandatory": true},
|
||||
{"tag": "DestinationHost", "field_id": "Destination-Host", "type": "*composed",
|
||||
"value": "~*vars.OriginHost", "mandatory": true},
|
||||
{"tag": "AuthApplicationId", "field_id": "Auth-Application-Id", "type": "*composed",
|
||||
{"tag": "SessionId", "field_id": "Session-Id", "type": "*variable",
|
||||
"value": "~*req.Session-Id", "mandatory": true},
|
||||
{"tag": "OriginHost", "field_id": "Origin-Host", "type": "*variable",
|
||||
"value": "~*req.Destination-Host", "mandatory": true},
|
||||
{"tag": "OriginRealm", "field_id": "Origin-Realm", "type": "*variable",
|
||||
"value": "~*req.Destination-Realm", "mandatory": true},
|
||||
{"tag": "DestinationRealm", "field_id": "Destination-Realm", "type": "*variable",
|
||||
"value": "~*req.Origin-Realm", "mandatory": true},
|
||||
{"tag": "DestinationHost", "field_id": "Destination-Host", "type": "*variable",
|
||||
"value": "~*req.Origin-Host", "mandatory": true},
|
||||
{"tag": "AuthApplicationId", "field_id": "Auth-Application-Id", "type": "*variable",
|
||||
"value": "~*vars.*appid", "mandatory": true},
|
||||
{"tag": "UserName", "field_id": "User-Name", "type": "*composed",
|
||||
"value": "~*req.Account", "mandatory": true},
|
||||
{"tag": "OriginStateID", "field_id": "Origin-State-Id", "type": "*composed",
|
||||
"value": "~*vars.OriginStateID", "mandatory": true},
|
||||
{"tag": "UserName", "field_id": "User-Name", "type": "*variable",
|
||||
"value": "~*req.User-Name", "mandatory": true},
|
||||
{"tag": "OriginStateID", "field_id": "Origin-State-Id", "type": "*constant",
|
||||
"value": "1"},
|
||||
]
|
||||
},
|
||||
"request_processors": [],
|
||||
|
||||
@@ -645,44 +645,43 @@ func TestDiameterAgentJsonCfg(t *testing.T) {
|
||||
utils.MetaASR: {
|
||||
{Tag: utils.StringPointer("SessionId"),
|
||||
Field_id: utils.StringPointer("Session-Id"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*req.OriginID"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.Session-Id"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("OriginHost"),
|
||||
Field_id: utils.StringPointer("Origin-Host"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*vars.DestinationHost"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.Destination-Host"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("OriginRealm"),
|
||||
Field_id: utils.StringPointer("Origin-Realm"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*vars.DestinationRealm"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.Destination-Realm"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("DestinationRealm"),
|
||||
Field_id: utils.StringPointer("Destination-Realm"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*vars.OriginRealm"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.Origin-Realm"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("DestinationHost"),
|
||||
Field_id: utils.StringPointer("Destination-Host"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*vars.OriginHost"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.Origin-Host"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("AuthApplicationId"),
|
||||
Field_id: utils.StringPointer("Auth-Application-Id"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*vars.*appid"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("UserName"),
|
||||
Field_id: utils.StringPointer("User-Name"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*req.Account"),
|
||||
Type: utils.StringPointer(utils.MetaVariable),
|
||||
Value: utils.StringPointer("~*req.User-Name"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
{Tag: utils.StringPointer("OriginStateID"),
|
||||
Field_id: utils.StringPointer("Origin-State-Id"),
|
||||
Type: utils.StringPointer(utils.META_COMPOSED),
|
||||
Value: utils.StringPointer("~*vars.OriginStateID"),
|
||||
Mandatory: utils.BoolPointer(true)},
|
||||
Field_id: utils.StringPointer("Origin-State-Id"),
|
||||
Type: utils.StringPointer(utils.META_CONSTANT),
|
||||
Value: utils.StringPointer("1")},
|
||||
},
|
||||
},
|
||||
Request_processors: &[]*DARequestProcessorJsnCfg{},
|
||||
|
||||
@@ -33,7 +33,7 @@ type DiameterAgentCfg struct {
|
||||
VendorId int
|
||||
ProductName string
|
||||
MaxActiveReqs int // limit the maximum number of requests processed
|
||||
ASRTempalte string
|
||||
ASRTemplate string
|
||||
Templates map[string][]*FCTemplate
|
||||
RequestProcessors []*DARequestProcessor
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg, separa
|
||||
da.MaxActiveReqs = *jsnCfg.Max_active_requests
|
||||
}
|
||||
if jsnCfg.Asr_template != nil {
|
||||
da.ASRTempalte = *jsnCfg.Asr_template
|
||||
da.ASRTemplate = *jsnCfg.Asr_template
|
||||
}
|
||||
if jsnCfg.Templates != nil {
|
||||
if da.Templates == nil {
|
||||
|
||||
@@ -67,9 +67,7 @@
|
||||
|
||||
"diameter_agent": {
|
||||
"enabled": true,
|
||||
"sessions_conns": [
|
||||
{"address": "127.0.0.1:2012","transport":"*json"}
|
||||
],
|
||||
"asr_template": "*asr",
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
46
data/conf/samples/diamagent/tests.json
Normal file
46
data/conf/samples/diamagent/tests.json
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
{
|
||||
|
||||
"diameter_agent": {
|
||||
"request_processors": [
|
||||
{
|
||||
"id": "TestSessionDisconnect",
|
||||
"filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:1",
|
||||
"*prefix:*req.Service-Context-Id:testSessionDisconnect"],
|
||||
"flags": ["*initiate", "*accounts"],
|
||||
"request_fields":[
|
||||
{"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*voice"},
|
||||
{"tag": "OriginID", "field_id": "OriginID", "type": "*variable",
|
||||
"value": "~*req.Session-Id", "mandatory": true},
|
||||
{"tag": "OriginHost", "field_id": "OriginHost", "type": "*variable",
|
||||
"value": "~*req.Origin-Host", "mandatory": true},
|
||||
{"tag": "RequestType", "field_id": "RequestType",
|
||||
"type": "*constant", "value": "*prepaid"},
|
||||
{"tag": "Category", "field_id": "Category",
|
||||
"type": "*constant", "value": "call"},
|
||||
{"tag": "Account", "field_id": "Account", "type": "*variable",
|
||||
"value": "~*req.Subscription-Id.Subscription-Id-Data", "mandatory": true},
|
||||
{"tag": "Subject", "field_id": "Subject", "type": "*variable",
|
||||
"value": "~*req.Service-Information.IN-Information.Calling-Party-Address", "mandatory": true},
|
||||
{"tag": "Destination", "field_id": "Destination", "type": "*variable",
|
||||
"value": "~*req.Service-Information.IN-Information.Real-Called-Number", "mandatory": true},
|
||||
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*variable",
|
||||
"value": "~*req.Event-Timestamp", "mandatory": true},
|
||||
{"tag": "Usage", "field_id": "Usage", "type": "*variable",
|
||||
"value": "~*req.Requested-Service-Unit.CC-Time:s/(.*)/${1}s/", "mandatory": true},
|
||||
{"tag": "DebitInterval", "field_id": "CGRDebitInterval",
|
||||
"type": "*constant", "value": "1s"},
|
||||
],
|
||||
"reply_fields":[
|
||||
{"tag": "CCATemplate", "type": "*template", "value": "*cca"},
|
||||
{"tag": "ResultCode", "filters": ["*notempty:*cgrep.Error:"],
|
||||
"field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true},
|
||||
{"tag": "GrantedUnits", "field_id": "Granted-Service-Unit.CC-Time",
|
||||
"filters": ["*gte:*cgrep.MaxUsage:0s"],
|
||||
"type": "*variable", "value": "~*cgrep.MaxUsage{*duration_seconds&*round:0}", "mandatory": true},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user