From e5299d97b138d9e6c046c19371a55c30c63379cc Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 4 Oct 2018 20:31:39 +0200 Subject: [PATCH] SessionS returning NO_SESSION if ChargerS are not returning any charger profile, diameter integration test for init voice calls, AttributeS properly rejecting non modified *attributes fields --- agents/agentreq.go | 36 ++++++++++ agents/diam_it_test.go | 85 ++++++++++++++++++++++-- config/rsrparser.go | 6 +- data/conf/samples/diamagent/cgrates.json | 16 +++++ data/conf/samples/diamagent/voice.json | 7 +- data/tariffplans/tutorial/Attributes.csv | 2 + data/tariffplans/tutorial/Chargers.csv | 2 +- engine/attributes.go | 15 +++-- sessions/sessions.go | 6 +- utils/consts.go | 1 + 10 files changed, 154 insertions(+), 22 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index 68581ead8..9354af2a0 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -20,7 +20,9 @@ package agents import ( "fmt" + "strconv" "strings" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -170,6 +172,40 @@ func (aReq *AgentRequest) ParseField( } out = tEnd.Sub(tStart).String() isString = true + case utils.MetaCCUsage: + if len(cfgFld.Value) != 3 { + return nil, fmt.Errorf("invalid arguments <%s> to %s", + utils.ToJSON(cfgFld.Value), utils.MetaCCUsage) + } + strVal1, err := cfgFld.Value[0].ParseDataProvider(aReq, utils.NestingSep) // ReqNr + if err != nil { + return "", err + } + reqNr, err := strconv.ParseInt(strVal1, 10, 64) + if err != nil { + return "", fmt.Errorf("invalid requestNumber <%s> to %s", + strVal1, utils.MetaCCUsage) + } + strVal2, err := cfgFld.Value[1].ParseDataProvider(aReq, utils.NestingSep) // TotalUsage + if err != nil { + return "", err + } + usedCCTime, err := utils.ParseDurationWithNanosecs(strVal2) + if err != nil { + return "", fmt.Errorf("invalid usedCCTime <%s> to %s", + strVal2, utils.MetaCCUsage) + } + strVal3, err := cfgFld.Value[2].ParseDataProvider(aReq, utils.NestingSep) // DebitInterval + if err != nil { + return "", err + } + debitItvl, err := utils.ParseDurationWithNanosecs(strVal3) + if err != nil { + return "", fmt.Errorf("invalid debitInterval <%s> to %s", + strVal3, utils.MetaCCUsage) + } + return usedCCTime + time.Duration(debitItvl.Nanoseconds()*reqNr), nil + } if err != nil { return diff --git a/agents/diam_it_test.go b/agents/diam_it_test.go index 391cd7e22..a54d5afe7 100644 --- a/agents/diam_it_test.go +++ b/agents/diam_it_test.go @@ -42,7 +42,7 @@ var replyTimeout = flag.String("reply_timeout", "1s", "Maximum duration to wait var daCfgPath string var daCfg *config.CGRConfig var apierRpc *rpc.Client -var dmtClient *DiameterClient +var diamClnt *DiameterClient var rplyTimeout time.Duration @@ -83,7 +83,7 @@ func TestDiamItStartEngine(t *testing.T) { */ func TestDiamItConnectDiameterClient(t *testing.T) { - dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS", + diamClnt, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DictionariesPath) @@ -103,7 +103,7 @@ func TestDiamItApierRpcConn(t *testing.T) { // Load the tariff plan, creating accounts and their balances func TestDiamItTPFromFolder(t *testing.T) { - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} var loadInst utils.LoadInstance if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { t.Error(err) @@ -162,10 +162,10 @@ func TestDiamItDryRun(t *testing.T) { t.Error(err) } for i := 0; i < *interations; i++ { - if err := dmtClient.SendMessage(ccr); err != nil { + if err := diamClnt.SendMessage(ccr); err != nil { t.Error(err) } - msg := dmtClient.ReceivedMessage(rplyTimeout) + msg := diamClnt.ReceivedMessage(rplyTimeout) if msg == nil { t.Fatal("No message returned") } @@ -242,3 +242,78 @@ func TestDiamItDryRun(t *testing.T) { } } } + +func TestDiamItCCRInit(t *testing.T) { + m := diam.NewRequest(diam.CreditControl, 4, nil) + m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("bb97be2b9f37c2be9614fff71c8b1d08b1acbff8")) + 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("voice@DiamItCCRInit")) + m.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2018, 10, 4, 14, 42, 20, 0, time.UTC))) + 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("1006")), // 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(600))}}) + 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("1006")), // 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("bb97be2b9f37c2be9614fff71c8b1d08b1acbff8")), // 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) + } + // Result-Code + eVal = "900" // 15 mins of session + if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, + 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) + } +} diff --git a/config/rsrparser.go b/config/rsrparser.go index 175aca5c0..6022a561e 100644 --- a/config/rsrparser.go +++ b/config/rsrparser.go @@ -281,12 +281,12 @@ func (prsr *RSRParser) ParseDataProvider(dP DataProvider, separator string) (out } func (prsr *RSRParser) ParseDataProviderWithInterfaces(dP DataProvider, separator string) (out string, err error) { - var outStr interface{} + var outIface interface{} if prsr.attrValue == "" { - if outStr, err = dP.FieldAsInterface( + if outIface, err = dP.FieldAsInterface( strings.Split(prsr.attrName, separator)); err != nil { return } } - return prsr.ParseValue(outStr) + return prsr.ParseValue(outIface) } diff --git a/data/conf/samples/diamagent/cgrates.json b/data/conf/samples/diamagent/cgrates.json index af07260de..e3ed92240 100644 --- a/data/conf/samples/diamagent/cgrates.json +++ b/data/conf/samples/diamagent/cgrates.json @@ -44,9 +44,25 @@ "enabled": true, }, +"chargers": { + "enabled": true, +}, + "sessions": { "enabled": true, + "attributes_conns": [ + {"address": "127.0.0.1:2012","transport":"*json"} + ], + "chargers_conns": [ + {"address": "127.0.0.1:2012","transport":"*json"} + ], + "rals_conns": [ + {"address": "127.0.0.1:2012","transport":"*json"} + ], + "cdrs_conns": [ + {"address": "127.0.0.1:2012","transport":"*json"} + ], }, "diameter_agent": { diff --git a/data/conf/samples/diamagent/voice.json b/data/conf/samples/diamagent/voice.json index 1ff077007..036da0570 100644 --- a/data/conf/samples/diamagent/voice.json +++ b/data/conf/samples/diamagent/voice.json @@ -13,19 +13,18 @@ {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "~*req.Session-Id", "mandatory": true}, {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*attributes"}, + {"tag": "Category", "field_id": "Category", "type": "*constant", "value": "call"}, {"tag": "Account", "field_id": "Account", "type": "*constant", "value": "*attributes"}, {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "~*req.Service-Information.IN-Information.Real-Called-Number", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "~*req.Event-Timestamp", "mandatory": true}, - {"tag": "Usage", "field_id": "Usage", "type": "*ccr_usage", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*constant", "value": "15m"}, {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "~*req.Subscription-Id.Subscription-Id-Data", "mandatory": true}, ], "reply_fields":[ - {"tag": "ResultCode", "filters": ["*string:*cgrep.Error:ACCOUNT_NOT_FOUND"], - "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, - {"tag": "ResultCode", "filters": ["*string:*cgrep.Error:USER_NOT_FOUND"], + {"tag": "ResultCode", "filters": ["*rsr::~*cgrep.Error(!^$)"], "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit.CC-Time", "type": "*composed", "value": "~*cgrep.MaxUsage{*duration_seconds}", "mandatory": true}, diff --git a/data/tariffplans/tutorial/Attributes.csv b/data/tariffplans/tutorial/Attributes.csv index e5340f04d..2e8f6f686 100644 --- a/data/tariffplans/tutorial/Attributes.csv +++ b/data/tariffplans/tutorial/Attributes.csv @@ -15,5 +15,7 @@ cgrates.org,ATTR_1003_SESSIONAUTH,*sessions,*string:Account:1003,,Password,*any, cgrates.org,ATTR_1003_SESSIONAUTH,,,,RequestType,*any,*prepaid,true,, cgrates.org,ATTR_1003_SESSIONAUTH,,,,PaypalAccount,*any,cgrates@paypal.com,true,, cgrates.org,ATTR_1003_SESSIONAUTH,,,,LCRProfile,*any,premium_cli,true,, +cgrates.org,ATTR_1006_ALIAS,*any,*string:SubscriberId:1006,,Account,*any,1001,true,false,10 +cgrates.org,ATTR_1006_ALIAS,*any,,,RequestType,*any,*prepaid,true,, diff --git a/data/tariffplans/tutorial/Chargers.csv b/data/tariffplans/tutorial/Chargers.csv index e68860cdf..c270b5867 100644 --- a/data/tariffplans/tutorial/Chargers.csv +++ b/data/tariffplans/tutorial/Chargers.csv @@ -1,2 +1,2 @@ #Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight -cgrates.org,Charger1,*string:Account:1001,2014-07-29T15:00:00Z,*rated,ATTR_1001_SIMPLEAUTH,20 \ No newline at end of file +cgrates.org,DEFAULT,,,*default,*none,0 \ No newline at end of file diff --git a/engine/attributes.go b/engine/attributes.go index c5b827fd5..e2a31ded0 100644 --- a/engine/attributes.go +++ b/engine/attributes.go @@ -201,13 +201,14 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent) ( } rply.AlteredFields = append(rply.AlteredFields, fldName) } - for _, valIface := range rply.CGREvent.Event { - if valIface == interface{}(utils.MetaAttributes) { - return nil, utils.NewCGRError(utils.AttributeSv1ProcessEvent, - utils.AttributesNotFoundCaps, - utils.AttributesNotFound, - utils.AttributesNotFound) - } + } + for _, valIface := range rply.CGREvent.Event { + if valIface == interface{}(utils.MetaAttributes) { + return nil, utils.NewCGRError( + utils.AttributeSv1ProcessEvent, + utils.AttributesNotFound, + utils.AttributesNotFound, + utils.AttributesNotFound) } } return diff --git a/sessions/sessions.go b/sessions/sessions.go index df9b0ed34..8377b1c05 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -532,8 +532,10 @@ func (smg *SMGeneric) v2ForkSessions(tnt string, evStart *engine.SafEvent, Event: evStart.AsMapInterface(), } var chrgrs []*engine.ChrgSProcessEventReply - if err := smg.chargerS.Call(utils.ChargerSv1ProcessEvent, cgrEv, &chrgrs); err != nil && - err.Error() != utils.ErrNotFound.Error() { + if err := smg.chargerS.Call(utils.ChargerSv1ProcessEvent, cgrEv, &chrgrs); err != nil { + if err.Error() == utils.ErrNotFound.Error() { + return nil, utils.ErrNoActiveSession + } return nil, err } noneSession := []*SMGSession{ diff --git a/utils/consts.go b/utils/consts.go index 0d5220269..20840fdc8 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -311,6 +311,7 @@ const ( HIERARCHY_SEP = ">" META_COMPOSED = "*composed" META_USAGE_DIFFERENCE = "*usage_difference" + MetaCCUsage = "*cc_usage" MetaString = "*string" NegativePrefix = "!" MatchStartPrefix = "^"