RADIUS integration tests for accounting start and stop with CDR generation

This commit is contained in:
DanB
2017-06-14 19:28:03 +02:00
parent 7ce514b712
commit 166ce87609
5 changed files with 188 additions and 14 deletions

View File

@@ -45,8 +45,14 @@ ATTRIBUTE Password 2 string
# Alias values
VALUE Framed-Protocol PPP 1
VALUE Service-Type Sip-Session 15 # Schulzrinne, acc, auth_radius
VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius
VALUE Sip-Method Invite 1
VALUE Sip-Method Bye 8
VALUE Acct-Status-Type Start 1
VALUE Acct-Status-Type Stop 2
# Vendors
VENDOR Cisco 9
VENDOR Microsoft 311

View File

@@ -89,10 +89,12 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e
}
}
if err != nil {
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> request: %s, error: %s", utils.ToJSON(req), err.Error()))
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> error: <%s> ignoring request: %s, process vars: %+v",
err.Error(), utils.ToJSON(req), procVars))
return nil, nil
} else if !processed {
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled for request: %s, ignoring request", utils.ToJSON(req)))
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled, ignoring request %s, process vars: %+v",
utils.ToJSON(req), procVars))
return nil, nil
}
return
@@ -126,10 +128,12 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e
}
}
if err != nil {
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> request: %s, error: %s", utils.ToJSON(req), err.Error()))
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> error: <%s> ignoring request: %s, process vars: %+v",
err.Error(), utils.ToJSON(req), procVars))
return nil, nil
} else if !processed {
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled for request: %s, ignoring request", utils.ToJSON(req)))
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled, ignoring request %s, process vars: %+v",
utils.ToJSON(req), procVars))
return nil, nil
}
return

View File

@@ -29,14 +29,15 @@ import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/sessionmanager"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/radigo"
)
var raCfgPath string
var raCfg *config.CGRConfig
var raSMGrpc *rpc.Client
var raAuthClnt, raAcctClnt *radigo.Client
var raRPC *rpc.Client
func TestRAitInitCfg(t *testing.T) {
raCfgPath = path.Join(*dataDir, "conf", "samples", "radagent")
@@ -74,7 +75,7 @@ func TestRAitStartEngine(t *testing.T) {
// Connect rpc client to rater
func TestRAitApierRpcConn(t *testing.T) {
var err error
raSMGrpc, err = jsonrpc.Dial("tcp", raCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
raRPC, err = jsonrpc.Dial("tcp", raCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
@@ -84,7 +85,7 @@ func TestRAitApierRpcConn(t *testing.T) {
func TestRAitTPFromFolder(t *testing.T) {
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
var loadInst utils.LoadInstance
if err := raSMGrpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
if err := raRPC.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
@@ -104,10 +105,10 @@ func TestRAitAuth(t *testing.T) {
if err := authReq.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil {
t.Error(err)
}
if err := authReq.AddAVPWithName("Acct-Session-Id", "2ca13afce9b2d76e15de7e1ec6568fc8@0:0:0:0:0:0:0:0", ""); err != nil {
if err := authReq.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil {
t.Error(err)
}
if err := authReq.AddAVPWithName("Sip-From-Tag", "7f30055f", ""); err != nil {
if err := authReq.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil {
t.Error(err)
}
if err := authReq.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil {
@@ -131,6 +132,157 @@ func TestRAitAuth(t *testing.T) {
}
}
func TestRAitAcctStart(t *testing.T) {
if raAcctClnt, err = radigo.NewClient("udp", "127.0.0.1:1813", "CGRateS.org", dictRad, 1, nil); err != nil {
t.Fatal(err)
}
req := raAcctClnt.NewRequest(radigo.AccountingRequest, 2) // emulates Kamailio packet for accounting start
if err := req.AddAVPWithName("Acct-Status-Type", "Start", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Service-Type", "Sip-Session", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-Response-Code", "200", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-Method", "Invite", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Event-Timestamp", "1497106115", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-To-Tag", "75c2f57b", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("User-Name", "1001", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Ascend-User-Acct-Time", "1497106115", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("NAS-Port-Id", "5060", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Acct-Delay-Time", "0", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil {
t.Error(err)
}
reply, err := raAcctClnt.SendRequest(req)
if err != nil {
t.Error(err)
}
if reply.Code != radigo.AccountingResponse {
t.Errorf("Received reply: %+v", reply)
}
if len(reply.AVPs) != 0 { // we don't expect AVPs to be populated
t.Errorf("Received AVPs: %+v", reply.AVPs)
}
// Make sure the sessin is managed by SMG
var aSessions []*sessionmanager.ActiveSession
if err := raRPC.Call("SMGenericV1.GetActiveSessions",
map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-51585361-75c2f57b"},
&aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 {
t.Errorf("Unexpected number of sessions received: %+v", aSessions)
} else if aSessions[0].Usage != time.Duration(10)*time.Second {
t.Errorf("Expecting 10s, received usage: %v", aSessions[0].Usage)
}
}
func TestRAitAcctStop(t *testing.T) {
req := raAcctClnt.NewRequest(radigo.AccountingRequest, 3) // emulates Kamailio packet for accounting start
if err := req.AddAVPWithName("Acct-Status-Type", "Stop", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Service-Type", "Sip-Session", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-Response-Code", "200", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-Method", "Bye", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Event-Timestamp", "1497106119", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Sip-To-Tag", "75c2f57b", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("User-Name", "1001", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Ascend-User-Acct-Time", "1497106115", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("NAS-Port-Id", "5060", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("Acct-Delay-Time", "0", ""); err != nil {
t.Error(err)
}
if err := req.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil {
t.Error(err)
}
reply, err := raAcctClnt.SendRequest(req)
if err != nil {
t.Error(err)
}
if reply.Code != radigo.AccountingResponse {
t.Errorf("Received reply: %+v", reply)
}
if len(reply.AVPs) != 0 { // we don't expect AVPs to be populated
t.Errorf("Received AVPs: %+v", reply.AVPs)
}
// Make sure the sessin was disconnected from SMG
var aSessions []*sessionmanager.ActiveSession
if err := raRPC.Call("SMGenericV1.GetActiveSessions",
map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-51585361-75c2f57b"},
&aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
time.Sleep(time.Duration(10 * time.Millisecond))
var cdrs []*engine.ExternalCDR
args := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, DestinationPrefixes: []string{"1002"}}
if err := raRPC.Call("ApierV2.GetCdrs", args, &cdrs); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(cdrs) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(cdrs))
} else {
if cdrs[0].Usage != "4" {
t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0])
}
if cdrs[0].CostSource != utils.SESSION_MANAGER_SOURCE {
t.Errorf("Unexpected CDR CostSource received for CDR: %v", cdrs[0])
}
if cdrs[0].Cost != 0.01 {
t.Errorf("Unexpected CDR Cost received for CDR: %v", cdrs[0])
}
}
}
func TestRAitStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)

View File

@@ -49,6 +49,9 @@
"cdrs": {
"enabled": true,
"rals_conns": [
{"address": "*internal"}
],
"cdrstats_conns": [
{"address": "*internal"}
],
@@ -112,14 +115,16 @@
"value": "*prepaid", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed",
"value": "Acct-Session-Id;^-;Sip-From-Tag;^-;Sip-To-Tag", "mandatory": true},
{"tag": "OriginHost", "field_id": "OriginHost", "type": "*composed",
"value": "NAS-IP-Address", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*composed",
"value": "User-Name", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*composed",
"value": "Called-Station-Id", "mandatory": true},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed",
"value": "Event-Timestamp", "mandatory": true},
"value": "Ascend-User-Acct-Time", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed",
"value": "Event-Timestamp", "mandatory": true},
"value": "Ascend-User-Acct-Time", "mandatory": true},
],
"reply_fields":[],
},
@@ -138,10 +143,10 @@
{"tag": "Destination", "field_id": "Destination", "type": "*composed",
"value": "Called-Station-Id", "mandatory": true},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed",
"value": "Event-Timestamp", "mandatory": true},
"value": "Ascend-User-Acct-Time", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed",
"value": "Event-Timestamp", "mandatory": true},
{"tag": "Usage", "field_id": "AnswerTime", "type": "*handler", "handler_id": "*usage_difference",
"value": "Ascend-User-Acct-Time", "mandatory": true},
{"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*usage_difference",
"value": "Event-Timestamp;^|;Ascend-User-Acct-Time", "mandatory": true},
],
"reply_fields":[],

View File

@@ -58,6 +58,11 @@ ATTRIBUTE Sip-Rpid 213 string # Proprietary, auth_radius
ATTRIBUTE SIP-AVP 225 string # Proprietary, avp_radius
### Acct-Status-Type Values ###
VALUE Acct-Status-Type Start 1
VALUE Acct-Status-Type Stop 2
VALUE Acct-Status-Type Alive 3
VALUE Acct-Status-Type Accounting-On 7
VALUE Acct-Status-Type Accounting-Off 8
VALUE Acct-Status-Type Failed 15 # RFC2866, acc
### Service-Type Values ###
@@ -67,6 +72,8 @@ VALUE Service-Type Sip-Session 15 # Schulzrinne, acc, auth_radius
VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius
VALUE Service-Type SIP-Callee-AVPs 31 # Proprietary, avp_radius
### Sip-Method Values ###
VALUE Sip-Method Undefined 0
VALUE Sip-Method Invite 1