From 33725d9f4e227e2c04c2136d25d060a7a47d9976 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 18 Sep 2018 12:06:48 +0200 Subject: [PATCH 01/52] Updating go-diameter in glide, adding sctp library --- glide.lock | 4 +++- glide.yaml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/glide.lock b/glide.lock index d84e46c31..a3f4923e6 100644 --- a/glide.lock +++ b/glide.lock @@ -24,7 +24,7 @@ imports: - name: github.com/DisposaBoy/JsonConfigReader version: 33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4 - name: github.com/fiorix/go-diameter - version: d742356e99f1457ee1fb8ddc238c932e9f5ca87b + version: 16028e641c19a8dd67509053bc558d389258ff6d subpackages: - diam - diam/avp @@ -33,6 +33,8 @@ imports: - diam/sm - diam/sm/smparser - diam/sm/smpeer +- name: github.com/ishidawataru/sctp + version: 6e2cb1366111dcf547c13531e3a263a067715847 - name: github.com/go-sql-driver/mysql version: 99ff426eb706cffe92ff3d058e168b278cabf7c7 - name: github.com/gorhill/cronexpr diff --git a/glide.yaml b/glide.yaml index cb0c84879..3d722b019 100644 --- a/glide.yaml +++ b/glide.yaml @@ -14,6 +14,7 @@ import: - diam/datatype - diam/dict - diam/sm +- package: github.com/ishidawataru/sctp - package: github.com/go-sql-driver/mysql - package: github.com/gorhill/cronexpr - package: github.com/jinzhu/gorm From 3239a7551f8a280b1f0a547533c42009888621d4 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 18 Sep 2018 18:39:32 +0200 Subject: [PATCH 02/52] Stripped diameter1 code --- agents/dmtagent.go | 224 +------- agents/dmtagent_it_test.go | 1075 ----------------------------------- agents/hapool_it_test.go | 120 ---- agents/httpagent_it_test.go | 4 + agents/libdmt.go | 832 +-------------------------- agents/libdmt_test.go | 545 ------------------ 6 files changed, 26 insertions(+), 2774 deletions(-) delete mode 100644 agents/dmtagent_it_test.go delete mode 100644 agents/hapool_it_test.go diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 7011e22ae..4d1893d66 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -21,14 +21,9 @@ package agents import ( "fmt" "reflect" - "strconv" - "strings" "sync" - "time" "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" "github.com/fiorix/go-diameter/diam" @@ -36,16 +31,20 @@ import ( "github.com/fiorix/go-diameter/diam/sm" ) -func NewDiameterAgent(cgrCfg *config.CGRConfig, sessionS rpcclient.RpcClientConnection, - pubsubs rpcclient.RpcClientConnection) (*DiameterAgent, error) { - da := &DiameterAgent{cgrCfg: cgrCfg, sessionS: sessionS, - pubsubs: pubsubs, connMux: new(sync.Mutex)} - if reflect.ValueOf(da.pubsubs).IsNil() { - da.pubsubs = nil // Empty it so we can check it later +func NewDiameterAgent(cgrCfg *config.CGRConfig, + sessionS, thdS rpcclient.RpcClientConnection) (*DiameterAgent, error) { + if sessionS != nil && reflect.ValueOf(sessionS).IsNil() { + sessionS = nil } + if thdS != nil && reflect.ValueOf(thdS).IsNil() { + thdS = nil + } + da := &DiameterAgent{ + cgrCfg: cgrCfg, sessionS: sessionS, + thdS: thdS, connMux: new(sync.Mutex)} dictsDir := cgrCfg.DiameterAgentCfg().DictionariesDir if len(dictsDir) != 0 { - if err := loadDictionaries(dictsDir, "DiameterAgent"); err != nil { + if err := loadDictionaries(dictsDir, utils.DiameterAgent); err != nil { return nil, err } } @@ -54,11 +53,16 @@ func NewDiameterAgent(cgrCfg *config.CGRConfig, sessionS rpcclient.RpcClientConn type DiameterAgent struct { cgrCfg *config.CGRConfig - sessionS rpcclient.RpcClientConnection // Connection towards CGR-SMG component - pubsubs rpcclient.RpcClientConnection // Connection towards CGR-PubSub component + sessionS rpcclient.RpcClientConnection // Connection towards CGR-SessionS component + thdS rpcclient.RpcClientConnection // Connection towards CGR-ThresholdS component connMux *sync.Mutex // Protect connection for read/write } +// ListenAndServe is called when DiameterAgent is started, usually from within cmd/cgr-engine +func (self *DiameterAgent) ListenAndServe() error { + return diam.ListenAndServe(self.cgrCfg.DiameterAgentCfg().Listen, self.handlers(), nil) +} + // Creates the message handlers func (self *DiameterAgent) handlers() diam.Handler { settings := &sm.Settings{ @@ -69,201 +73,15 @@ func (self *DiameterAgent) handlers() diam.Handler { FirmwareRevision: datatype.Unsigned32(utils.DIAMETER_FIRMWARE_REVISION), } dSM := sm.New(settings) - dSM.HandleFunc("CCR", self.handleCCR) - dSM.HandleFunc("ALL", self.handleALL) + dSM.HandleFunc("ALL", self.handleALL) // route all commands to one dispatcher go func() { for err := range dSM.ErrorReports() { - utils.Logger.Err(fmt.Sprintf(" StateMachine error: %+v", err)) + utils.Logger.Err(fmt.Sprintf("<%s> sm error: %v", utils.DiameterAgent, err)) } }() return dSM } -func (da DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, - procVars processorVars, cca *CCA) (processed bool, err error) { - passesAllFilters := true - for _, fldFilter := range reqProcessor.RequestFilter { - if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes { - passesAllFilters = false - } - } - if !passesAllFilters { // Not going with this processor further - return false, nil - } - if reqProcessor.DryRun { // DryRun should log the matching processor as well as the received CCR - utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) - utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) - } - if !reqProcessor.AppendCCA { - *cca = *NewBareCCAFromCCR(ccr, da.cgrCfg.DiameterAgentCfg().OriginHost, da.cgrCfg.DiameterAgentCfg().OriginRealm) - procVars = make(processorVars) - } - smgEv, err := ccr.AsMapIface(reqProcessor.CCRFields) - if err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) - *cca = *NewBareCCAFromCCR(ccr, da.cgrCfg.DiameterAgentCfg().OriginHost, da.cgrCfg.DiameterAgentCfg().OriginRealm) - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, da.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v messageSetAVPsWithPath, error: %s", cca.diamMessage, err.Error())) - return false, err - } - return false, ErrDiameterRatingFailed - } - if len(reqProcessor.Flags) != 0 { - smgEv[utils.CGRFlags] = reqProcessor.Flags.String() // Populate CGRFlags automatically - for flag, val := range reqProcessor.Flags { - procVars[flag] = val - } - } - if reqProcessor.PublishEvent && da.pubsubs != nil { - evt, err := engine.NewMapEvent(smgEv).AsMapString(nil) - if err != nil { - *cca = *NewBareCCAFromCCR(ccr, da.cgrCfg.DiameterAgentCfg().OriginHost, da.cgrCfg.DiameterAgentCfg().OriginRealm) - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, da.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - return false, err - } - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) - return false, ErrDiameterRatingFailed - } - var reply string - if err := da.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { - *cca = *NewBareCCAFromCCR(ccr, da.cgrCfg.DiameterAgentCfg().OriginHost, da.cgrCfg.DiameterAgentCfg().OriginRealm) - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, da.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - return false, err - } - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) - return false, ErrDiameterRatingFailed - } - } - if reqProcessor.DryRun { // DryRun does not send over network - utils.Logger.Info(fmt.Sprintf(" SMGenericEvent: %+v", smgEv)) - procVars[CGRResultCode] = strconv.Itoa(diam.LimitedSuccess) - } else { // Query SessionS over APIs - var tnt string - if tntIf, has := smgEv[utils.Tenant]; has { - if tntStr, canCast := utils.CastFieldIfToString(tntIf); canCast { - tnt = tntStr - } - } - cgrEv := &utils.CGREvent{ - Tenant: utils.FirstNonEmpty(tnt, - config.CgrConfig().DefaultTenant), - ID: "dmt:" + utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: smgEv, - } - switch ccr.CCRequestType { - case 1: - var initReply sessions.V1InitSessionReply - err = da.sessionS.Call(utils.SessionSv1InitiateSession, - procVars.asV1InitSessionArgs(cgrEv), &initReply) - if procVars[utils.MetaCGRReply], err = NewCGRReply(&initReply, err); err != nil { - return - } - case 2: - var updateReply sessions.V1UpdateSessionReply - err = da.sessionS.Call(utils.SessionSv1UpdateSession, - procVars.asV1UpdateSessionArgs(cgrEv), &updateReply) - if procVars[utils.MetaCGRReply], err = NewCGRReply(&updateReply, err); err != nil { - return - } - case 3, 4: // Handle them together since we generate CDR for them - var rpl string - if ccr.CCRequestType == 3 { - if err = da.sessionS.Call(utils.SessionSv1TerminateSession, - procVars.asV1TerminateSessionArgs(cgrEv), &rpl); err != nil { - procVars[utils.MetaCGRReply], _ = NewCGRReply(nil, err) - } - } else if ccr.CCRequestType == 4 { - var evntRply sessions.V1ProcessEventReply - err = da.sessionS.Call(utils.SessionSv1ProcessEvent, - procVars.asV1ProcessEventArgs(cgrEv), &evntRply) - if utils.ErrHasPrefix(err, utils.RalsErrorPrfx) { - cgrEv.Event[utils.Usage] = 0 // avoid further debits - } else if evntRply.MaxUsage != nil { - cgrEv.Event[utils.Usage] = *evntRply.MaxUsage // make sure the CDR reflects the debit - } - if procVars[utils.MetaCGRReply], err = NewCGRReply(&evntRply, err); err != nil { - return - } - } - if da.cgrCfg.DiameterAgentCfg().CreateCDR && - (!da.cgrCfg.DiameterAgentCfg().CDRRequiresSession || err == nil || - !strings.HasSuffix(err.Error(), utils.ErrNoActiveSession.Error())) { // Check if CDR requires session - if errCdr := da.sessionS.Call(utils.SessionSv1ProcessCDR, cgrEv, &rpl); errCdr != nil { - err = errCdr - procVars[utils.MetaCGRReply], _ = NewCGRReply(nil, err) - } - } - } - } - diamCode := strconv.Itoa(diam.Success) - if procVars.hasVar(CGRResultCode) { - diamCode, _ = procVars.valAsString(CGRResultCode) - } - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, diamCode, - false, da.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - return false, err - } - if err := cca.SetProcessorAVPs(reqProcessor, procVars); err != nil { - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, da.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - return false, err - } - utils.Logger.Err(fmt.Sprintf(" CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) - return false, ErrDiameterRatingFailed - } - if reqProcessor.DryRun { - utils.Logger.Info(fmt.Sprintf(" CCA message: %s", cca.diamMessage)) - } - return true, nil -} - -func (self *DiameterAgent) handlerCCR(c diam.Conn, m *diam.Message) { - ccr, err := NewCCRFromDiameterMessage(m, self.cgrCfg.DiameterAgentCfg().DebitInterval) - if err != nil { - utils.Logger.Err(fmt.Sprintf(" Unmarshaling message: %s, error: %s", m, err)) - return - } - cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) - var processed, lclProcessed bool - procVars := make(processorVars) // Shared between processors - for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { - lclProcessed, err = self.processCCR(ccr, reqProcessor, procVars, cca) - if lclProcessed { // Process local so we don't overwrite globally - processed = lclProcessed - } - if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) { - break - } - } - if err != nil && err != ErrDiameterRatingFailed { - utils.Logger.Err(fmt.Sprintf(" CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) - return - } else if !processed { - utils.Logger.Err(fmt.Sprintf(" No request processor enabled for CCR: %s, ignoring request", ccr.diamMessage)) - return - } - self.connMux.Lock() - defer self.connMux.Unlock() - if _, err := cca.AsDiameterMessage().WriteTo(c); err != nil { - utils.Logger.Err(fmt.Sprintf(" Failed to write message to %s: %s\n%s\n", c.RemoteAddr(), err, cca.AsDiameterMessage())) - return - } -} - -// Simply dispatch the handling in goroutines -// Could be futher improved with rate control -func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { - go self.handlerCCR(c, m) -} - func (self *DiameterAgent) handleALL(c diam.Conn, m *diam.Message) { - utils.Logger.Warning(fmt.Sprintf(" Received unexpected message from %s:\n%s", c.RemoteAddr(), m)) -} - -func (self *DiameterAgent) ListenAndServe() error { - return diam.ListenAndServe(self.cgrCfg.DiameterAgentCfg().Listen, self.handlers(), nil) + utils.Logger.Warning(fmt.Sprintf("<%s> received unexpected message from %s:\n%s", utils.DiameterAgent, c.RemoteAddr(), m)) } diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go deleted file mode 100644 index df69ca8a0..000000000 --- a/agents/dmtagent_it_test.go +++ /dev/null @@ -1,1075 +0,0 @@ -// +build integration - -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -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 -*/ - -package agents - -import ( - "flag" - "net/rpc" - "net/rpc/jsonrpc" - "path" - "reflect" - "testing" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" - "github.com/fiorix/go-diameter/diam" - "github.com/fiorix/go-diameter/diam/avp" - "github.com/fiorix/go-diameter/diam/datatype" - "github.com/fiorix/go-diameter/diam/dict" -) - -var waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache") -var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") -var interations = flag.Int("iterations", 1, "Number of iterations to do for dry run simulation") -var replyTimeout = flag.String("reply_timeout", "1s", "Maximum duration to wait for a reply") - -var daCfgPath string -var daCfg *config.CGRConfig -var apierRpc *rpc.Client -var dmtClient *DiameterClient - -var rplyTimeout time.Duration - -func TestDmtAgentInitCfg(t *testing.T) { - daCfgPath = path.Join(*dataDir, "conf", "samples", "dmtagent") - // Init config first - var err error - daCfg, err = config.NewCGRConfigFromFolder(daCfgPath) - if err != nil { - t.Error(err) - } - daCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush() - config.SetCgrConfig(daCfg) - rplyTimeout, _ = utils.ParseDurationWithSecs(*replyTimeout) -} - -func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) { - cfgDefaults, _ := config.NewDefaultCGRConfig() - loadDictionaries(cfgDefaults.DiameterAgentCfg().DictionariesDir, "UNIT_TEST") - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - ccr := &CCR{ - SessionId: "routinga;1442095190;1476802709", - OriginHost: cfgDefaults.DiameterAgentCfg().OriginHost, - OriginRealm: cfgDefaults.DiameterAgentCfg().OriginRealm, - DestinationHost: cfgDefaults.DiameterAgentCfg().OriginHost, - DestinationRealm: cfgDefaults.DiameterAgentCfg().OriginRealm, - AuthApplicationId: 4, - ServiceContextId: "voice@huawei.com", - CCRequestType: 1, - CCRequestNumber: 0, - EventTimestamp: time.Date(2015, 11, 23, 12, 22, 24, 0, time.UTC), - ServiceIdentifier: 0, - SubscriptionId: []struct { - SubscriptionIdType int `avp:"Subscription-Id-Type"` - SubscriptionIdData string `avp:"Subscription-Id-Data"` - }{ - struct { - SubscriptionIdType int `avp:"Subscription-Id-Type"` - SubscriptionIdData string `avp:"Subscription-Id-Data"` - }{SubscriptionIdType: 0, SubscriptionIdData: "4986517174963"}, - struct { - SubscriptionIdType int `avp:"Subscription-Id-Type"` - SubscriptionIdData string `avp:"Subscription-Id-Data"` - }{SubscriptionIdType: 0, SubscriptionIdData: "4986517174963"}}, - debitInterval: time.Duration(300) * time.Second, - } - ccr.RequestedServiceUnit.CCTime = 300 - ccr.UsedServiceUnit.CCTime = 0 - ccr.ServiceInformation.INInformation.CallingPartyAddress = "4986517174963" - ccr.ServiceInformation.INInformation.CalledPartyAddress = "4986517174964" - ccr.ServiceInformation.INInformation.RealCalledNumber = "4986517174964" - ccr.ServiceInformation.INInformation.ChargeFlowType = 0 - ccr.ServiceInformation.INInformation.CallingVlrNumber = "49123956767" - ccr.ServiceInformation.INInformation.CallingCellIDOrSAI = "12340185301425" - ccr.ServiceInformation.INInformation.BearerCapability = "capable" - ccr.ServiceInformation.INInformation.CallReferenceNumber = "askjadkfjsdf" - ccr.ServiceInformation.INInformation.MSCAddress = "123324234" - ccr.ServiceInformation.INInformation.TimeZone = 0 - ccr.ServiceInformation.INInformation.CalledPartyNP = "4986517174964" - ccr.ServiceInformation.INInformation.SSPTime = "20091020120101" - var err error - if ccr.diamMessage, err = ccr.AsDiameterMessage(); err != nil { - t.Error(err) - } - eSMGE := map[string]interface{}{"EventName": DIAMETER_CCR, - "OriginID": "routinga;1442095190;1476802709", - "Account": "*users", "Category": "call", - "AnswerTime": "2015-11-23 12:22:24 +0000 UTC", - "Destination": "4986517174964", "Direction": "*out", - "RequestType": "*users", - "SetupTime": "2015-11-23 12:22:24 +0000 UTC", - "Subject": "*users", "SubscriberId": "4986517174963", - "ToR": "*voice", "Tenant": "*users", "Usage": "5m0s"} - ccrFields := []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "TOR", FieldId: "ToR", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "OriginID", FieldId: "OriginID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Session-Id", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "RequestType", FieldId: "RequestType", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Direction", FieldId: "Direction", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Tenant", FieldId: "Tenant", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Category", FieldId: "Category", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Account", FieldId: "Account", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Subject", FieldId: "Subject", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("^*users", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Destination", FieldId: "Destination", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Service-Information>IN-Information>Real-Called-Number", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "SetupTime", FieldId: "SetupTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Event-Timestamp", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "AnswerTime", FieldId: "AnswerTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Event-Timestamp", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Usage", FieldId: "Usage", Type: "*handler", HandlerId: "*ccr_usage", Mandatory: true}, - &config.CfgCdrField{Tag: "SubscriberID", FieldId: "SubscriberId", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}} - if smge, err := ccr.AsMapIface(ccrFields); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eSMGE, smge) { - t.Errorf("Expecting: %+v, received: %+v", eSMGE, smge) - } -} - -func TestDmtAgentPopulateCCTotalOctets(t *testing.T) { - daRP := &config.DARequestProcessor{CCAFields: []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "GrantedUnit", - FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), - FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Time", - Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "GrantedOctet", - FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), - FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Total-Octets", - Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), - Mandatory: true}, - }} - ccr := new(CCR) - ccr.diamMessage = ccr.AsBareDiameterMessage() - cca := NewBareCCAFromCCR(ccr, "cgr-da", "cgrates.org") - if err := cca.SetProcessorAVPs(daRP, - processorVars{CGRError: "", CGRMaxUsage: "153600"}); err != nil { - t.Error(err) - } - if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{ - "Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Not found") - } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template - t.Errorf("Expecting 153600, received: %s", strResult) - } - if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{ - "Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, - dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Not found") - } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template - t.Errorf("Expecting 153600, received: %s", strResult) - } -} - -// Remove data in both rating and accounting db -func TestDmtAgentResetDataDb(t *testing.T) { - if err := engine.InitDataDb(daCfg); err != nil { - t.Fatal(err) - } -} - -// Wipe out the cdr database -func TestDmtAgentResetStorDb(t *testing.T) { - if err := engine.InitStorDb(daCfg); err != nil { - t.Fatal(err) - } -} - -// Start CGR Engine -func TestDmtAgentStartEngine(t *testing.T) { - if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { - t.Fatal(err) - } -} - -// Connect rpc client to rater -func TestDmtAgentApierRpcConn(t *testing.T) { - var err error - apierRpc, err = jsonrpc.Dial("tcp", daCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed - if err != nil { - t.Fatal(err) - } -} - -// Load the tariff plan, creating accounts and their balances -func TestDmtAgentTPFromFolder(t *testing.T) { - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} - var loadInst utils.LoadInstance - 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 -} - -func TestDmtAgentConnectDiameterClient(t *testing.T) { - dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, - daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DictionariesDir) - if err != nil { - t.Fatal(err) - } -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' -func TestDmtAgentSendCCRInit(t *testing.T) { - cdr := &engine.CDR{ - CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), - OrderID: 123, ToR: utils.VOICE, OriginID: "testccr1", OriginHost: "192.168.1.1", - Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "1001", - Subject: "1001", Destination: "1004", - SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), - AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(0), RunID: utils.DEFAULT_RUNID, - ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - } - ccr := storedCdrToCCR(cdr, "UNIT_TEST", - daCfg.DiameterAgentCfg().OriginRealm, - daCfg.DiameterAgentCfg().VendorId, - daCfg.DiameterAgentCfg().ProductName, - utils.DIAMETER_FIRMWARE_REVISION, - daCfg.DiameterAgentCfg().DebitInterval, false) - m, err := ccr.AsDiameterMessage() - if err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - 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("Granted-Service-Unit not found") - } else if strCCTime := avpValAsString(avps[0]); strCCTime != "300" { - t.Errorf("Expecting 300, received: %s", strCCTime) - } - if result, err := msg.FindAVP("Result-Code", dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if resultStr := avpValAsString(result); resultStr != "2001" { - t.Errorf("Expecting 2001, received: %s", resultStr) - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.5008 - if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) - } -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:52:26Z"' -func TestDmtAgentSendCCRUpdate(t *testing.T) { - cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", - SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(300) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - } - ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, - daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false) - m, err := ccr.AsDiameterMessage() - if err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - 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("Granted-Service-Unit not found") - } else if strCCTime := avpValAsString(avps[0]); strCCTime != "300" { - t.Errorf("Expecting 300, received: %s, (%+v)", strCCTime, avps) - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.251800 - if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) - } -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:57:26Z"' -func TestDmtAgentSendCCRUpdate2(t *testing.T) { - cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", - SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(600) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - } - ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, - daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false) - m, err := ccr.AsDiameterMessage() - if err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - 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("Granted-Service-Unit not found") - } else if strCCTime := avpValAsString(avps[0]); strCCTime != "300" { - t.Errorf("Expecting 300, received: %s", strCCTime) - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.002800 - if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if utils.Round(acnt.BalanceMap[utils.MONETARY].GetTotalValue(), 5, utils.ROUNDING_MIDDLE) != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) - } -} - -func TestDmtAgentSendCCRTerminate(t *testing.T) { - cdr := &engine.CDR{ - CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), - OrderID: 123, ToR: utils.VOICE, - OriginID: "testccr1", OriginHost: "192.168.1.1", - Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "1001", - Subject: "1001", Destination: "1004", - SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), - AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), - RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(610) * time.Second, - ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - } - ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, - daCfg.DiameterAgentCfg().VendorId, - daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, - daCfg.DiameterAgentCfg().DebitInterval, true) - m, err := ccr.AsDiameterMessage() - if err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No answer to CCR terminate received") - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.243500 - if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { // Should also consider derived charges which double the cost of 6m10s - 2x0.7584 - t.Errorf("Expected: %v, received: %v", - eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) - } -} - -func TestDmtAgentSendCCRSMS(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr2")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("message@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) - ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information - AVP: []*diam.AVP{ - diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data - }}), - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data - }}), - }, - }), - }}) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - - time.Sleep(time.Duration(100) * time.Millisecond) - dmtClient.ReceivedMessage(rplyTimeout) // Discard the received message so we can test next one - /* - if msg == nil { - t.Fatal("No message returned") - } - 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("Granted-Service-Unit not found") - } else if strCCTime := avpValAsString(avps[0]); strCCTime != "0" { - t.Errorf("Expecting 0, received: %s", strCCTime) - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.205 - if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { // Should also consider derived charges which double the cost of 6m10s - 2x0.7584 - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) - } - */ - var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, ToRs: []string{utils.SMS}} - if err := apierRpc.Call("ApierV2.GetCdrs", req, &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 != "1" { // should be 1 but maxUsage returns rounded version - t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) - } - if cdrs[0].Cost != 0.6 { - t.Errorf("Unexpected CDR Cost received, cdr: %+v ", cdrs[0].Cost) - } - } -} - -func TestDmtAgentSendCCRSMSWrongAccount(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr3")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("message@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("non_existent")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) - ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information - AVP: []*diam.AVP{ - diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49602200011")), // Address-Data - }}), - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49780029555")), // Address-Data - }}), - }, - }), - }}) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(100) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) // Discard the received message so we can test next one - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template - t.Errorf("Expecting 5030, received: %s", strResult) - } -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' -func TestDmtAgentSendCCRInitWrongAccount(t *testing.T) { - cdr := &engine.CDR{CGRID: utils.Sha1("testccr4", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "testccr4", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Tenant: "cgrates.org", Category: "call", Account: "non_existent", Subject: "non_existent", Destination: "1004", - SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(0) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - } - ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, - daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false) - m, err := ccr.AsDiameterMessage() - if err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(100) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) // Discard the received message so we can test next one - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template - t.Errorf("Expecting 5030, received: %s", strResult) - } -} - -func TestDmtAgentSendCCRSimpaEvent(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr5")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("routing1.huawei.com")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("simpa@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 13, 16, 47, 58, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(1)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), - diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), - }, - }), - diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), - }, - }), - }, - }) - ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ // Service-Information - AVP: []*diam.AVP{ - diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information - AVP: []*diam.AVP{ - diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(29000, avp.Mbit, 2011, &diam.GroupedAVP{ // MC-Information - AVP: []*diam.AVP{ - diam.NewAVP(20938, avp.Mbit, 2011, datatype.OctetString("0x38924012914528")), // HighLayerCharacteristics - diam.NewAVP(29002, avp.Mbit, 2011, datatype.UTF8String("12928471313847173")), // MC-Service-Id - diam.NewAVP(29003, avp.Mbit, 2011, datatype.UTF8String("SPV123456012123")), // TransparentData - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // MC-Information - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(0)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("33780029555")), // Address-Data - }, - }), - }, - }), - }}) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) // Discard the received message so we can test next one - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "2001" { // Result-Code set in the template - t.Errorf("Expecting 2001, received: %s", strResult) - } -} - -func TestDmtAgentCdrs(t *testing.T) { - var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, ToRs: []string{utils.VOICE}} - if err := apierRpc.Call("ApierV2.GetCdrs", req, &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 != "10m10s" { - t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) - } - if cdrs[0].Cost != 0.7565 { - t.Errorf("Unexpected CDR Cost received, cdr: %+v ", cdrs[0]) - } - } -} - -func TestDmtAgentSendDataGrpInit(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) - ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information - AVP: []*diam.AVP{ - diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data - }}), - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data - }}), - }, - }), - }}) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code not found") - } else if resCode := avpValAsString(avps[0]); resCode != "2001" { - t.Errorf("Expecting 2001, received: %s", resCode) - } -} - -func TestDmtAgentSendDataGrpUpdate(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(2)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) - ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information - AVP: []*diam.AVP{ - diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data - }}), - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data - }}), - }, - }), - }}) - ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time - diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1000)), // CC-Input-Octets - diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(24)), // CC-Output-Octets - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)), // Data session for group 1 - }, - }) - ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time - diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1024)), // CC-Input-Octets - diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(512)), // CC-Output-Octets - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(2)), // Data session for group 2 - }, - }) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code not found") - } else if resCode := avpValAsString(avps[0]); resCode != "2001" { - t.Errorf("Expecting 2001, received: %s", resCode) - } -} - -func TestDmtAgentSendDataGrpTerminate(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com")) - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(3)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), - diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data - }}) - ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) - ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) - ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP - }, - }), - diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information - AVP: []*diam.AVP{ - diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data - }}), - diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address - AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data - }}), - }, - }), - }}) - ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time - diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(512)), // CC-Input-Octets - diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(0)), // CC-Output-Octets - }, - }), - }, - }) - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(3000) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code not found") - } else if resCode := avpValAsString(avps[0]); resCode != "2001" { - t.Errorf("Expecting 2001, received: %s", resCode) - } -} - -func TestDmtAgentSendDataGrpCDRs(t *testing.T) { - var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{CGRIDs: []string{utils.Sha1("testdatagrp")}} - if err := apierRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(cdrs) != 3 { - t.Error("Unexpected number of CDRs returned: ", len(cdrs)) - } -} - -/* -func TestDmtAgentDryRun1(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("pubsub1")) // Match specific DryRun profile - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - if _, err := ccr.NewAVP("Framed-IP-Address", avp.Mbit, 0, datatype.UTF8String("10.228.16.4")); err != nil { - t.Error(err) - } - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(100) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "300" { // Result-Code set in the template - t.Errorf("Expecting 300, received: %s", strResult) - } -} -*/ - -func TestDmtAgentDryRun1(t *testing.T) { - ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) - ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - ccr.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - ccr.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("CGR-DA")) - ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("pubsub1")) // Match specific DryRun profile - ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(2)) - ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) - ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) - ccr.NewAVP(avp.TerminationCause, avp.Mbit, 0, datatype.Enumerated(1)) - ccr.NewAVP(443, avp.Mbit, 0, &diam.GroupedAVP{ // Subscription-Id - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Type - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data - }}) - ccr.NewAVP(443, avp.Mbit, 0, &diam.GroupedAVP{ // Subscription-Id - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(1)), // Subscription-Id-Type - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208123456789")), // Subscription-Id-Data - }}) - ccr.NewAVP(439, avp.Mbit, 0, datatype.Unsigned32(0)) // Service-Identifier - ccr.NewAVP(437, avp.Mbit, 0, &diam.GroupedAVP{ // Requested-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(300)), // CC-Time - }}) - ccr.NewAVP(873, avp.Mbit|avp.Vbit, 10415, &diam.GroupedAVP{ // Service-information - AVP: []*diam.AVP{ - diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information - AVP: []*diam.AVP{ - diam.NewAVP(20336, avp.Mbit, 2011, datatype.UTF8String("1001")), // CallingPartyAdress - diam.NewAVP(20337, avp.Mbit, 2011, datatype.UTF8String("1002")), // CalledPartyAdress - diam.NewAVP(20339, avp.Mbit, 2011, datatype.Unsigned32(0)), // ChargeFlowType - diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("33609004940")), // CallingVlrNumber - diam.NewAVP(20303, avp.Mbit, 2011, datatype.UTF8String("208104941749984")), // CallingCellID - diam.NewAVP(20313, avp.Mbit, 2011, datatype.OctetString("0x8090a3")), // BearerCapability - diam.NewAVP(20321, avp.Mbit, 2011, datatype.OctetString("0x401c4132ed665")), // CallreferenceNumber - diam.NewAVP(20322, avp.Mbit, 2011, datatype.UTF8String("33609004940")), // MSCAddress - diam.NewAVP(20386, avp.Mbit, 2011, datatype.UTF8String("20160501010101")), // SSPTime - diam.NewAVP(20938, avp.Mbit, 2011, datatype.OctetString("0x00000001")), // HighLayerCharacteristics - diam.NewAVP(20324, avp.Mbit, 2011, datatype.Integer32(8)), // Time-Zone - }, - }), - }}) - if _, err := ccr.NewAVP("Framed-IP-Address", avp.Mbit, 0, datatype.UTF8String("10.228.16.4")); err != nil { - t.Error(err) - } - for i := 0; i < *interations; i++ { - if err := dmtClient.SendMessage(ccr); err != nil { - t.Error(err) - } - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } - /* - if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { - t.Error(err) - } else if len(avps) == 0 { - t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "300" { // Result-Code set in the template - t.Errorf("Expecting 300, received: %s", strResult) - } - */ - } -} - -/* -func TestDmtAgentLoadCER(t *testing.T) { - m := diam.NewRequest(diam.CapabilitiesExchange, 4, dict.Default) - m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) - m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) - m.NewAVP(avp.HostIPAddress, avp.Mbit, 0, datatype.Address(net.ParseIP("127.0.0.1"))) - m.NewAVP(avp.VendorID, avp.Mbit, 0, datatype.Unsigned32(999)) - m.NewAVP(avp.ProductName, 0, 0, datatype.UTF8String("CGR-DA")) - m.NewAVP(avp.OriginStateID, avp.Mbit, 0, datatype.Unsigned32(1)) - m.NewAVP(avp.AcctApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - m.NewAVP(avp.FirmwareRevision, avp.Mbit, 0, datatype.Unsigned32(1)) - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } - time.Sleep(time.Duration(100) * time.Millisecond) - msg := dmtClient.ReceivedMessage(rplyTimeout) - if msg == nil { - t.Fatal("No message returned") - } -} - -func TestDmtAgentConnectDiameterClientCER(t *testing.T) { - dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, - daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, "") - if err != nil { - t.Fatal(err) - } - m := diam.NewRequest(diam.CapabilitiesExchange, 0, dict.Default) - m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("UNIT_TEST2")) - m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity(daCfg.DiameterAgentCfg().OriginRealm)) - m.NewAVP(avp.VendorID, avp.Mbit, 0, datatype.Unsigned32(daCfg.DiameterAgentCfg().VendorId)) - m.NewAVP(avp.ProductName, 0, 0, datatype.UTF8String(daCfg.DiameterAgentCfg().ProductName)) - m.NewAVP(avp.VendorSpecificApplicationID, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.VendorID, avp.Mbit, 0, datatype.Unsigned32(10415)), - diam.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)), - }, - }) - if err := dmtClient.SendMessage(m); err != nil { - t.Error(err) - } -} -*/ - -func TestDmtAgentStopEngine(t *testing.T) { - if err := engine.KillEngine(*waitRater); err != nil { - t.Error(err) - } -} diff --git a/agents/hapool_it_test.go b/agents/hapool_it_test.go deleted file mode 100644 index 69d7f0061..000000000 --- a/agents/hapool_it_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// +build integration - -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -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 -*/ - -package agents - -/* -import ( - "os/exec" - "path" - "testing" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" -) - -var cgrRater1Cmd, cgrSmg1Cmd *exec.Cmd - -func TestHaPoolInitCfg(t *testing.T) { - daCfgPath = path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater1") - // Init config first - var err error - daCfg, err = config.NewCGRConfigFromFolder(daCfgPath) - if err != nil { - t.Error(err) - } - daCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush() - config.SetCgrConfig(daCfg) -} - -// Remove data in both rating and accounting db -func TestHaPoolResetDataDb(t *testing.T) { - TestDmtAgentResetDataDb(t) -} - -// Wipe out the cdr database -func TestHaPoolResetStorDb(t *testing.T) { - TestDmtAgentResetStorDb(t) -} - -// Start CGR Engine -func TestHaPoolStartEngine(t *testing.T) { - engine.KillEngine(*waitRater) // just to make sure - var err error - cgrRater1 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater1") - if cgrRater1Cmd, err = engine.StartEngine(cgrRater1, *waitRater); err != nil { - t.Fatal("cgrRater1: ", err) - } - cgrRater2 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater2") - if _, err = engine.StartEngine(cgrRater2, *waitRater); err != nil { - t.Fatal("cgrRater2: ", err) - } - cgrSmg1 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrsmg1") - if cgrSmg1Cmd, err = engine.StartEngine(cgrSmg1, *waitRater); err != nil { - t.Fatal("cgrSmg1: ", err) - } - cgrSmg2 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrsmg2") - if _, err = engine.StartEngine(cgrSmg2, *waitRater); err != nil { - t.Fatal("cgrSmg2: ", err) - } - cgrDa := path.Join(*dataDir, "conf", "samples", "hapool", "dagent") - if _, err = engine.StartEngine(cgrDa, *waitRater); err != nil { - t.Fatal("cgrDa: ", err) - } - -} - -// Connect rpc client to rater -func TestHaPoolApierRpcConn(t *testing.T) { - TestDmtAgentApierRpcConn(t) -} - -// Load the tariff plan, creating accounts and their balances -func TestHaPoolTPFromFolder(t *testing.T) { - TestDmtAgentTPFromFolder(t) -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' -func TestHaPoolSendCCRInit(t *testing.T) { - TestDmtAgentSendCCRInit(t) -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:52:26Z"' -func TestHaPoolSendCCRUpdate(t *testing.T) { - TestDmtAgentSendCCRUpdate(t) -} - -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:57:26Z"' -func TestHaPoolSendCCRUpdate2(t *testing.T) { - TestDmtAgentSendCCRUpdate2(t) -} - -func TestHaPoolSendCCRTerminate(t *testing.T) { - TestDmtAgentSendCCRTerminate(t) -} - -func TestHaPoolCdrs(t *testing.T) { - TestDmtAgentCdrs(t) -} - -func TestHaPoolStopEngine(t *testing.T) { - TestDmtAgentStopEngine(t) -} -*/ diff --git a/agents/httpagent_it_test.go b/agents/httpagent_it_test.go index 296829072..100a4ea91 100644 --- a/agents/httpagent_it_test.go +++ b/agents/httpagent_it_test.go @@ -21,6 +21,7 @@ along with this program. If not, see package agents import ( + "flag" "fmt" "io/ioutil" "net/http" @@ -41,6 +42,9 @@ var ( haCfg *config.CGRConfig haRPC *rpc.Client httpC *http.Client // so we can cache the connection + err error + dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here") + waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache") ) func TestHAitInitCfg(t *testing.T) { diff --git a/agents/libdmt.go b/agents/libdmt.go index 01e3447ea..256695ecd 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -18,204 +18,21 @@ along with this program. If not, see package agents -/* -Build various type of packets here -*/ - import ( - "errors" "fmt" - "math" - "math/rand" - "net" "os" "path/filepath" - "strconv" "strings" - "time" - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" - "github.com/fiorix/go-diameter/diam" - "github.com/fiorix/go-diameter/diam/avp" - "github.com/fiorix/go-diameter/diam/datatype" "github.com/fiorix/go-diameter/diam/dict" ) -func init() { - rand.Seed(time.Now().UnixNano()) -} - -const ( - META_CCR_USAGE = "*ccr_usage" - META_VALUE_EXPONENT = "*value_exponent" - META_SUM = "*sum" - DIAMETER_CCR = "DIAMETER_CCR" - DiameterRatingFailed = 5031 - CGRError = "CGRError" - CGRMaxUsage = "CGRMaxUsage" - CGRResultCode = "CGRResultCode" -) - -var ( - ErrFilterNotPassing = errors.New("Filter not passing") - ErrDiameterRatingFailed = errors.New("Diameter rating failed") -) - -// processorVars will hold various variables using during request processing -// here so we can define methods on it -type processorVars map[string]interface{} - -// hasSubsystems will return true on single subsystem being present in processorVars -func (pv processorVars) hasSubsystems() (has bool) { - for _, k := range []string{utils.MetaAccounts, utils.MetaResources, - utils.MetaSuppliers, utils.MetaAttributes} { - if _, has = pv[k]; has { - return - } - } - return -} - -func (pv processorVars) hasVar(k string) (has bool) { - _, has = pv[k] - return -} - -// valAsInterface returns the string value for fldName -func (pv processorVars) valAsInterface(fldPath string) (val interface{}, err error) { - fldName := fldPath - if strings.HasPrefix(fldPath, utils.MetaCGRReply) { - fldName = utils.MetaCGRReply - } - if !pv.hasVar(fldName) { - err = errors.New("not found") - return - } - return config.NewNavigableMap(pv).FieldAsInterface(strings.Split(fldPath, utils.HIERARCHY_SEP)) -} - -// valAsString returns the string value for fldName -// returns empty if fldName not found -func (pv processorVars) valAsString(fldPath string) (val string, err error) { - fldName := fldPath - if strings.HasPrefix(fldPath, utils.MetaCGRReply) { - fldName = utils.MetaCGRReply - } - if !pv.hasVar(fldName) { - return "", utils.ErrNotFoundNoCaps - } - return config.NewNavigableMap(pv).FieldAsString(strings.Split(fldPath, utils.HIERARCHY_SEP)) -} - -// asV1AuthorizeArgs returns the arguments needed by SessionSv1.AuthorizeEvent -func (pv processorVars) asV1AuthorizeArgs(cgrEv *utils.CGREvent) (args *sessions.V1AuthorizeArgs) { - args = &sessions.V1AuthorizeArgs{ // defaults - GetMaxUsage: true, - CGREvent: *cgrEv, - } - if !pv.hasSubsystems() { - return - } - if !pv.hasVar(utils.MetaAccounts) { - args.GetMaxUsage = false - } - if pv.hasVar(utils.MetaResources) { - args.AuthorizeResources = true - } - if pv.hasVar(utils.MetaSuppliers) { - args.GetSuppliers = true - } - if pv.hasVar(utils.MetaAttributes) { - args.GetAttributes = true - } - return -} - -// asV1InitSessionArgs returns the arguments used in SessionSv1.InitSession -func (pv processorVars) asV1InitSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1InitSessionArgs) { - args = &sessions.V1InitSessionArgs{ // defaults - InitSession: true, - CGREvent: *cgrEv, - } - if !pv.hasSubsystems() { - return - } - if !pv.hasVar(utils.MetaAccounts) { - args.InitSession = false - } - if pv.hasVar(utils.MetaResources) { - args.AllocateResources = true - } - if pv.hasVar(utils.MetaAttributes) { - args.GetAttributes = true - } - return -} - -// asV1UpdateSessionArgs returns the arguments used in SessionSv1.InitSession -func (pv processorVars) asV1UpdateSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1UpdateSessionArgs) { - args = &sessions.V1UpdateSessionArgs{ // defaults - UpdateSession: true, - CGREvent: *cgrEv, - } - if !pv.hasSubsystems() { - return - } - if !pv.hasVar(utils.MetaAccounts) { - args.UpdateSession = false - } - if pv.hasVar(utils.MetaAttributes) { - args.GetAttributes = true - } - return -} - -// asV1TerminateSessionArgs returns the arguments used in SMGv1.TerminateSession -func (pv processorVars) asV1TerminateSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1TerminateSessionArgs) { - args = &sessions.V1TerminateSessionArgs{ // defaults - TerminateSession: true, - CGREvent: *cgrEv, - } - if !pv.hasSubsystems() { - return - } - if !pv.hasVar(utils.MetaAccounts) { - args.TerminateSession = false - } - if pv.hasVar(utils.MetaResources) { - args.ReleaseResources = true - } - return -} - -func (pv processorVars) asV1ProcessEventArgs(cgrEv *utils.CGREvent) (args *sessions.V1ProcessEventArgs) { - args = &sessions.V1ProcessEventArgs{ // defaults - Debit: true, - CGREvent: *cgrEv, - } - if !pv.hasSubsystems() { - return - } - if !pv.hasVar(utils.MetaAccounts) { - args.Debit = false - } - if pv.hasVar(utils.MetaResources) { - args.AllocateResources = true - } - if pv.hasVar(utils.MetaAttributes) { - args.GetAttributes = true - } - return -} - func loadDictionaries(dictsDir, componentId string) error { fi, err := os.Stat(dictsDir) if err != nil { if strings.HasSuffix(err.Error(), "no such file or directory") { - return fmt.Errorf(" Invalid dictionaries folder: <%s>", dictsDir) + return fmt.Errorf("<%s> Invalid dictionaries folder: <%s>", componentId, dictsDir) } return err } else if !fi.IsDir() { // If config dir defined, needs to exist @@ -240,651 +57,4 @@ func loadDictionaries(dictsDir, componentId string) error { } return nil }) - -} - -// Returns reqType, requestNr and ccTime in seconds -func disectUsageForCCR(usage time.Duration, debitInterval time.Duration, callEnded bool) (reqType, reqNr, reqCCTime, usedCCTime int) { - usageSecs := usage.Seconds() - debitIntervalSecs := debitInterval.Seconds() - reqType = 1 - if usage > 0 { - reqType = 2 - } - if callEnded { - reqType = 3 - } - reqNr = int(usageSecs / debitIntervalSecs) - if callEnded { - reqNr += 1 - } - ccTimeFloat := debitInterval.Seconds() - if callEnded { - ccTimeFloat = math.Mod(usageSecs, debitIntervalSecs) - } - if reqType == 1 { // Initial does not have usedCCTime - reqCCTime = int(ccTimeFloat) - } else if reqType == 2 { - reqCCTime = int(ccTimeFloat) - usedCCTime = int(math.Mod(usageSecs, debitIntervalSecs)) - } else if reqType == 3 { - usedCCTime = int(ccTimeFloat) // Termination does not have requestCCTime - } - return -} - -func usageFromCCR(reqType int, reqNr, usedCCTime int64, debitIterval time.Duration) (usage time.Duration) { - //dISecs := debitIterval.Nano() - //var ccTime int - usage = debitIterval - if reqType == 3 { - reqNr -= 1 // decrease request number to reach the real number - usage = (time.Duration(usedCCTime) * time.Second) + time.Duration(debitIterval.Nanoseconds()*reqNr) - } - return -} - -// Utility function to convert from StoredCdr to CCR struct -func storedCdrToCCR(cdr *engine.CDR, originHost, originRealm string, vendorId int, productName string, - firmwareRev int, debitInterval time.Duration, callEnded bool) *CCR { - //sid := "session;" + strconv.Itoa(int(rand.Uint32())) - reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(cdr.Usage, debitInterval, callEnded) - ccr := &CCR{SessionId: cdr.CGRID, OriginHost: originHost, OriginRealm: originRealm, DestinationHost: originHost, DestinationRealm: originRealm, - AuthApplicationId: 4, ServiceContextId: cdr.ExtraFields["Service-Context-Id"], CCRequestType: reqType, CCRequestNumber: reqNr, EventTimestamp: cdr.AnswerTime, - ServiceIdentifier: 0} - ccr.SubscriptionId = make([]struct { - SubscriptionIdType int `avp:"Subscription-Id-Type"` - SubscriptionIdData string `avp:"Subscription-Id-Data"` - }, 1) - ccr.SubscriptionId[0].SubscriptionIdType = 0 - ccr.SubscriptionId[0].SubscriptionIdData = cdr.Account - ccr.RequestedServiceUnit.CCTime = reqCCTime - ccr.UsedServiceUnit.CCTime = usedCCTime - ccr.ServiceInformation.INInformation.CallingPartyAddress = cdr.Account - ccr.ServiceInformation.INInformation.CalledPartyAddress = cdr.Destination - ccr.ServiceInformation.INInformation.RealCalledNumber = cdr.Destination - ccr.ServiceInformation.INInformation.ChargeFlowType = 0 - ccr.ServiceInformation.INInformation.CallingVlrNumber = cdr.ExtraFields["Calling-Vlr-Number"] - ccr.ServiceInformation.INInformation.CallingCellIDOrSAI = cdr.ExtraFields["Calling-CellID-Or-SAI"] - ccr.ServiceInformation.INInformation.BearerCapability = cdr.ExtraFields["Bearer-Capability"] - ccr.ServiceInformation.INInformation.CallReferenceNumber = cdr.CGRID - ccr.ServiceInformation.INInformation.TimeZone = 0 - ccr.ServiceInformation.INInformation.SSPTime = cdr.ExtraFields["SSP-Time"] - return ccr -} - -// Not the cleanest but most efficient way to retrieve a string from AVP since there are string methods on all datatypes -// and the output is always in teh form "DataType{real_string}Padding:x" -func avpValAsString(a *diam.AVP) string { - dataVal := a.Data.String() - startIdx := strings.Index(dataVal, "{") - endIdx := strings.Index(dataVal, "}") - if startIdx == 0 || endIdx == 0 { - return "" - } - return dataVal[startIdx+1 : endIdx] -} - -// Handler for meta functions -func metaHandler(m *diam.Message, procVars processorVars, - tag, arg string, dur time.Duration) (string, error) { - switch tag { - case META_CCR_USAGE: - var ok bool - var reqType datatype.Enumerated - var reqNr, usedUnit datatype.Unsigned32 - if ccReqTypeAvp, err := m.FindAVP("CC-Request-Type", 0); err != nil { - return "", err - } else if ccReqTypeAvp == nil { - return "", errors.New("CC-Request-Type not found") - } else if reqType, ok = ccReqTypeAvp.Data.(datatype.Enumerated); !ok { - return "", fmt.Errorf("CC-Request-Type must be Enumerated and not %v", ccReqTypeAvp.Data.Type()) - } - if ccReqNrAvp, err := m.FindAVP("CC-Request-Number", 0); err != nil { - return "", err - } else if ccReqNrAvp == nil { - return "", errors.New("CC-Request-Number not found") - } else if reqNr, ok = ccReqNrAvp.Data.(datatype.Unsigned32); !ok { - return "", fmt.Errorf("CC-Request-Number must be Unsigned32 and not %v", ccReqNrAvp.Data.Type()) - } - switch reqType { - case datatype.Enumerated(1), datatype.Enumerated(2): - if reqUnitAVPs, err := m.FindAVPsWithPath([]interface{}{"Requested-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { - return "", err - } else if len(reqUnitAVPs) == 0 { - return "", errors.New("Requested-Service-Unit>CC-Time not found") - } else if usedUnit, ok = reqUnitAVPs[0].Data.(datatype.Unsigned32); !ok { - return "", fmt.Errorf("Requested-Service-Unit>CC-Time must be Unsigned32 and not %v", reqUnitAVPs[0].Data.Type()) - } - case datatype.Enumerated(3), datatype.Enumerated(4): - if usedUnitAVPs, err := m.FindAVPsWithPath([]interface{}{"Used-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { - return "", err - } else if len(usedUnitAVPs) != 0 { - if usedUnit, ok = usedUnitAVPs[0].Data.(datatype.Unsigned32); !ok { - return "", fmt.Errorf("Used-Service-Unit>CC-Time must be Unsigned32 and not %v", usedUnitAVPs[0].Data.Type()) - } - } - } - return usageFromCCR(int(reqType), int64(reqNr), int64(usedUnit), dur).String(), nil - } - return "", nil -} - -// metaValueExponent will multiply the float value with the exponent provided. -// Expects 2 arguments in template separated by | -func metaValueExponent(m *diam.Message, procVars processorVars, - argsTpl utils.RSRFields, roundingDecimals int) (string, error) { - valStr := composedFieldvalue(m, argsTpl, 0, procVars) - handlerArgs := strings.Split(valStr, utils.HandlerArgSep) - if len(handlerArgs) != 2 { - return "", errors.New("Unexpected number of arguments") - } - val, err := strconv.ParseFloat(handlerArgs[0], 64) - if err != nil { - return "", err - } - exp, err := strconv.Atoi(handlerArgs[1]) - if err != nil { - return "", err - } - res := val * math.Pow10(exp) - return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil -} - -func metaSum(m *diam.Message, procVars processorVars, - argsTpl utils.RSRFields, passAtIndex, roundingDecimals int) (string, error) { - valStr := composedFieldvalue(m, argsTpl, passAtIndex, procVars) - handlerArgs := strings.Split(valStr, utils.HandlerArgSep) - var summed float64 - for _, arg := range handlerArgs { - val, err := strconv.ParseFloat(arg, 64) - if err != nil { - return "", err - } - summed += val - } - return strconv.FormatFloat(utils.Round(summed, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil -} - -// splitIntoInterface is used to split a string into []interface{} instead of []string -func splitIntoInterface(content, sep string) []interface{} { - spltStr := strings.Split(content, sep) - spltIf := make([]interface{}, len(spltStr)) - for i, val := range spltStr { - spltIf[i] = val - } - return spltIf -} - -// avpsWithPath is used to find AVPs by specifying RSRField as filter -func avpsWithPath(m *diam.Message, rsrFld *utils.RSRField) ([]*diam.AVP, error) { - return m.FindAVPsWithPath( - splitIntoInterface(rsrFld.Id, utils.HIERARCHY_SEP), dict.UndefinedVendorID) -} - -func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, procVars processorVars) (bool, int) { - if fieldFilter == nil { - return true, 0 - } - // check procVars before AVPs - if val, err := procVars.valAsString(fieldFilter.Id); err != utils.ErrNotFoundNoCaps { - if err != nil { - utils.Logger.Warning( - fmt.Sprintf(" parsing value: <%s> as string, error: <%s>", - fieldFilter.Id, err.Error())) - return false, 0 - } - if _, err := fieldFilter.Parse(val); err != nil { - return false, 0 - } - return true, 0 - } - avps, err := avpsWithPath(m, fieldFilter) - if err != nil { - return false, 0 - } - if len(avps) == 0 { // No AVP found in request, treat it same as empty - if _, err := fieldFilter.Parse(""); err != nil { - return false, 0 - } - return true, -1 - } - for avpIdx, avpVal := range avps { // First match wins due to index - if _, err = fieldFilter.Parse(avpValAsString(avpVal)); err == nil { - return true, avpIdx - } - } - return false, 0 -} - -func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int, procVars processorVars) (outVal string) { - var err error - for _, rsrTpl := range outTpl { - var valToParse string - if !rsrTpl.IsStatic() { // for Static we will parse empty valToParse bellow - // check procVars before AVPs - if valToParse, err = procVars.valAsString(rsrTpl.Id); err != nil { - if err != utils.ErrNotFoundNoCaps { - utils.Logger.Warning( - fmt.Sprintf("<%s> %s", utils.DiameterAgent, err.Error())) - continue - } - // not found in processorVars, look in AVPs - // AVPs from here - matchingAvps, err := avpsWithPath(m, rsrTpl) - if err != nil || len(matchingAvps) == 0 { - if err != nil { - utils.Logger.Err(fmt.Sprintf("<%s> Error matching AVPS: %s", - utils.DiameterAgent, err.Error())) - } - continue - } - if len(matchingAvps) <= avpIdx { - utils.Logger.Warning( - fmt.Sprintf("<%s> Cannot retrieve AVP with index %d for field template with id: %s", - utils.DiameterAgent, avpIdx, rsrTpl.Id)) - continue // Not convertible, ignore - } - if matchingAvps[0].Data.Type() == diam.GroupedAVPType { - utils.Logger.Warning( - fmt.Sprintf("<%s> Value for field template with id: %s is matching a group AVP, ignoring.", - utils.DiameterAgent, rsrTpl.Id)) - continue // Not convertible, ignore - } - valToParse = avpValAsString(matchingAvps[avpIdx]) - } - } - if parsed, err := rsrTpl.Parse(valToParse); err != nil { - utils.Logger.Warning( - fmt.Sprintf("<%s> %s", - utils.DiameterAgent, err.Error())) - } else { - outVal += parsed - } - } - return -} - -// Used to return the encoded value based on what AVP understands for it's type -func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([]byte, error) { - switch dictAVP.Data.Type { - case datatype.OctetStringType, datatype.DiameterIdentityType, datatype.DiameterURIType, datatype.IPFilterRuleType, datatype.QoSFilterRuleType, datatype.UTF8StringType: - return []byte(valStr), nil - case datatype.AddressType: - return []byte(net.ParseIP(valStr)), nil - case datatype.EnumeratedType, datatype.Integer32Type, datatype.Unsigned32Type: - i, err := strconv.Atoi(valStr) - if err != nil { - return nil, err - } - return datatype.Enumerated(i).Serialize(), nil - case datatype.Unsigned64Type, datatype.Integer64Type: - i, err := strconv.ParseInt(valStr, 10, 64) - if err != nil { - return nil, err - } - return datatype.Unsigned64(i).Serialize(), nil - case datatype.Float32Type: - f, err := strconv.ParseFloat(valStr, 32) - if err != nil { - return nil, err - } - return datatype.Float32(f).Serialize(), nil - case datatype.Float64Type: - f, err := strconv.ParseFloat(valStr, 64) - if err != nil { - return nil, err - } - return datatype.Float64(f).Serialize(), nil - case datatype.GroupedType: - return nil, errors.New("GroupedType not supported for serialization") - case datatype.IPv4Type: - return datatype.IPv4(net.ParseIP(valStr)).Serialize(), nil - case datatype.TimeType: - t, err := utils.ParseTimeDetectLayout(valStr, timezone) - if err != nil { - return nil, err - } - return datatype.Time(t).Serialize(), nil - default: - return nil, fmt.Errorf("Unsupported type for serialization: %v", dictAVP.Data.Type) - } -} - -func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, - extraParam interface{}, procVars processorVars) (fmtValOut string, err error) { - var outVal string - passAtIndex := -1 - passedAllFilters := true - for _, fldFilter := range cfgFld.FieldFilter { - var pass bool - if pass, passAtIndex = passesFieldFilter(m, fldFilter, procVars); !pass { - passedAllFilters = false - break - } - } - if !passedAllFilters { - return "", ErrFilterNotPassing // Not matching field filters, will have it empty - } - if passAtIndex == -1 { - passAtIndex = 0 // No filter - } - switch cfgFld.Type { - case utils.META_FILLER: - outVal = cfgFld.Value.Id() - cfgFld.Padding = "right" - case utils.META_CONSTANT: - outVal = cfgFld.Value.Id() - case utils.META_HANDLER: - switch cfgFld.HandlerId { - case META_VALUE_EXPONENT: - outVal, err = metaValueExponent(m, procVars, cfgFld.Value, 10) // FixMe: add here configured number of decimals - case META_SUM: - outVal, err = metaSum(m, procVars, cfgFld.Value, passAtIndex, 10) - default: - outVal, err = metaHandler(m, procVars, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) - if err != nil { - utils.Logger.Warning(fmt.Sprintf(" Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error())) - } - } - case utils.META_COMPOSED: - outVal = composedFieldvalue(m, cfgFld.Value, 0, procVars) - case utils.MetaGrouped: // GroupedAVP - outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex, procVars) - } - if fmtValOut, err = utils.FmtFieldWidth(cfgFld.Tag, outVal, cfgFld.Width, - cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { - utils.Logger.Warning(fmt.Sprintf(" Error when processing field template with tag: %s, error: %s", cfgFld.Tag, err.Error())) - return "", err - } - return fmtValOut, nil -} - -// messageAddAVPsWithPath will dynamically add AVPs into the message -// append: append to the message, on false overwrite if AVP is single or add to group if AVP is Grouped -func messageSetAVPsWithPath(m *diam.Message, path []interface{}, - avpValStr string, appnd bool, timezone string) error { - if len(path) == 0 { - return errors.New("Empty path as AVP filter") - } - dictAVPs := make([]*dict.AVP, len(path)) // for each subpath, one dictionary AVP - for i, subpath := range path { - if dictAVP, err := m.Dictionary().FindAVP(m.Header.ApplicationID, subpath); err != nil { - return err - } else if dictAVP == nil { - return fmt.Errorf("Cannot find AVP with id: %s", path[len(path)-1]) - } else { - dictAVPs[i] = dictAVP - } - } - if dictAVPs[len(path)-1].Data.Type == diam.GroupedAVPType { - return errors.New("Last AVP in path cannot be GroupedAVP") - } - var msgAVP *diam.AVP // Keep a reference here towards last AVP - lastAVPIdx := len(path) - 1 - for i := lastAVPIdx; i >= 0; i-- { - var typeVal datatype.Type - if i == lastAVPIdx { - avpValByte, err := serializeAVPValueFromString(dictAVPs[i], avpValStr, timezone) - if err != nil { - return err - } - typeVal, err = datatype.Decode(dictAVPs[i].Data.Type, avpValByte) // Check here - if err != nil { - return err - } - } else { - typeVal = &diam.GroupedAVP{ - AVP: []*diam.AVP{msgAVP}} - } - newMsgAVP := diam.NewAVP(dictAVPs[i].Code, avp.Mbit, dictAVPs[i].VendorID, typeVal) // FixMe: maybe Mbit with dictionary one - if i == lastAVPIdx-1 && !appnd { // last AVP needs to be appended in group - avps, _ := m.FindAVPsWithPath(path[:lastAVPIdx], dict.UndefinedVendorID) - if len(avps) != 0 { // Group AVP already in the message - prevGrpData := avps[len(avps)-1].Data.(*diam.GroupedAVP) // Take the last avp found to append there - prevGrpData.AVP = append(prevGrpData.AVP, msgAVP) - m.Header.MessageLength += uint32(msgAVP.Len()) - return nil - } - } - msgAVP = newMsgAVP - } - if !appnd { // Not group AVP, replace the previous set one with this one - avps, _ := m.FindAVPsWithPath(path, dict.UndefinedVendorID) - if len(avps) != 0 { // Group AVP already in the message - m.Header.MessageLength -= uint32(avps[len(avps)-1].Len()) // decrease message length since we overwrite - *avps[len(avps)-1] = *msgAVP - m.Header.MessageLength += uint32(msgAVP.Len()) - return nil - } - } - m.AVP = append(m.AVP, msgAVP) - m.Header.MessageLength += uint32(msgAVP.Len()) - return nil -} - -// debitInterval is the configured debitInterval, in sync with the diameter client one -func NewCCRFromDiameterMessage(m *diam.Message, debitInterval time.Duration) (*CCR, error) { - var ccr CCR - if err := m.Unmarshal(&ccr); err != nil { - return nil, err - } - ccr.diamMessage = m - ccr.debitInterval = debitInterval - return &ccr, nil -} - -// CallControl Request -// FixMe: strip it down to mandatory bare structure format by RFC 4006 -type CCR struct { - SessionId string `avp:"Session-Id"` - OriginHost string `avp:"Origin-Host"` - OriginRealm string `avp:"Origin-Realm"` - DestinationHost string `avp:"Destination-Host"` - DestinationRealm string `avp:"Destination-Realm"` - AuthApplicationId int `avp:"Auth-Application-Id"` - ServiceContextId string `avp:"Service-Context-Id"` - CCRequestType int `avp:"CC-Request-Type"` - CCRequestNumber int `avp:"CC-Request-Number"` - EventTimestamp time.Time `avp:"Event-Timestamp"` - SubscriptionId []struct { - SubscriptionIdType int `avp:"Subscription-Id-Type"` - SubscriptionIdData string `avp:"Subscription-Id-Data"` - } `avp:"Subscription-Id"` - ServiceIdentifier int `avp:"Service-Identifier"` - RequestedServiceUnit struct { - CCTime int `avp:"CC-Time"` - } `avp:"Requested-Service-Unit"` - UsedServiceUnit struct { - CCTime int `avp:"CC-Time"` - } `avp:"Used-Service-Unit"` - ServiceInformation struct { - INInformation struct { - CallingPartyAddress string `avp:"Calling-Party-Address"` - CalledPartyAddress string `avp:"Called-Party-Address"` - RealCalledNumber string `avp:"Real-Called-Number"` - ChargeFlowType int `avp:"Charge-Flow-Type"` - CallingVlrNumber string `avp:"Calling-Vlr-Number"` - CallingCellIDOrSAI string `avp:"Calling-CellID-Or-SAI"` - BearerCapability string `avp:"Bearer-Capability"` - CallReferenceNumber string `avp:"Call-Reference-Number"` - MSCAddress string `avp:"MSC-Address"` - TimeZone int `avp:"Time-Zone"` - CalledPartyNP string `avp:"Called-Party-NP"` - SSPTime string `avp:"SSP-Time"` - } `avp:"IN-Information"` - } `avp:"Service-Information"` - diamMessage *diam.Message // Used to parse fields with CGR templates - debitInterval time.Duration // Configured debit interval -} - -// AsBareDiameterMessage converts CCR into a bare DiameterMessage -// Compatible with the required fields of CCA -func (self *CCR) AsBareDiameterMessage() *diam.Message { - m := diam.NewRequest(diam.CreditControl, 4, nil) - m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(self.SessionId)) - m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity(self.OriginHost)) - m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity(self.OriginRealm)) - m.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(self.AuthApplicationId)) - m.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(self.CCRequestType)) - m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(self.CCRequestNumber)) - return m -} - -// Used when sending from client to agent -func (self *CCR) AsDiameterMessage() (*diam.Message, error) { - m := self.AsBareDiameterMessage() - if _, err := m.NewAVP("Destination-Host", avp.Mbit, 0, datatype.DiameterIdentity(self.DestinationHost)); err != nil { - return nil, err - } - if _, err := m.NewAVP("Destination-Realm", avp.Mbit, 0, datatype.DiameterIdentity(self.DestinationRealm)); err != nil { - return nil, err - } - if _, err := m.NewAVP("Service-Context-Id", avp.Mbit, 0, datatype.UTF8String(self.ServiceContextId)); err != nil { - return nil, err - } - if _, err := m.NewAVP("Event-Timestamp", avp.Mbit, 0, datatype.Time(self.EventTimestamp)); err != nil { - return nil, err - } - for _, subscriptionId := range self.SubscriptionId { - if _, err := m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(subscriptionId.SubscriptionIdType)), // Subscription-Id-Type - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String(subscriptionId.SubscriptionIdData)), // Subscription-Id-Data - }}); err != nil { - return nil, err - } - } - if _, err := m.NewAVP("Service-Identifier", avp.Mbit, 0, datatype.Unsigned32(self.ServiceIdentifier)); err != nil { - return nil, err - } - if _, err := m.NewAVP("Requested-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(self.RequestedServiceUnit.CCTime))}}); err != nil { // CC-Time - return nil, err - } - if _, err := m.NewAVP("Used-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(self.UsedServiceUnit.CCTime))}}); err != nil { // CC-Time - return nil, err - } - if _, err := 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(self.ServiceInformation.INInformation.CallingPartyAddress)), // Calling-Party-Address - diam.NewAVP(832, avp.Mbit, 10415, datatype.UTF8String(self.ServiceInformation.INInformation.CalledPartyAddress)), // Called-Party-Address - diam.NewAVP(20327, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.RealCalledNumber)), // Real-Called-Number - diam.NewAVP(20339, avp.Mbit, 2011, datatype.Unsigned32(self.ServiceInformation.INInformation.ChargeFlowType)), // Charge-Flow-Type - diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.CallingVlrNumber)), // Calling-Vlr-Number - diam.NewAVP(20303, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.CallingCellIDOrSAI)), // Calling-CellID-Or-SAI - diam.NewAVP(20313, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.BearerCapability)), // Bearer-Capability - diam.NewAVP(20321, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.CallReferenceNumber)), // Call-Reference-Number - diam.NewAVP(20322, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.MSCAddress)), // MSC-Address - diam.NewAVP(20324, avp.Mbit, 2011, datatype.Unsigned32(self.ServiceInformation.INInformation.TimeZone)), // Time-Zone - diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.CalledPartyNP)), // Called-Party-NP - diam.NewAVP(20386, avp.Mbit, 2011, datatype.UTF8String(self.ServiceInformation.INInformation.SSPTime)), // SSP-Time - }, - }), - }}); err != nil { - return nil, err - } - return m, nil -} - -// Extracts data out of CCR into a SMGenericEvent based on the configured template -func (self *CCR) AsMapIface(cfgFlds []*config.CfgCdrField) (map[string]interface{}, error) { - outMap := make(map[string]string) // work with it so we can append values to keys - outMap[utils.EVENT_NAME] = DIAMETER_CCR - for _, cfgFld := range cfgFlds { - fmtOut, err := fieldOutVal(self.diamMessage, cfgFld, self.debitInterval, nil) - if err != nil { - if err == ErrFilterNotPassing { - continue // Do nothing in case of Filter not passing - } - return nil, err - } - if _, hasKey := outMap[cfgFld.FieldId]; hasKey && cfgFld.Append { - outMap[cfgFld.FieldId] += fmtOut - } else { - outMap[cfgFld.FieldId] = fmtOut - - } - if cfgFld.BreakOnSuccess { - break - } - } - return utils.ConvertMapValStrIf(outMap), nil -} - -func NewBareCCAFromCCR(ccr *CCR, originHost, originRealm string) *CCA { - cca := &CCA{SessionId: ccr.SessionId, AuthApplicationId: ccr.AuthApplicationId, CCRequestType: ccr.CCRequestType, CCRequestNumber: ccr.CCRequestNumber, - OriginHost: originHost, OriginRealm: originRealm, - diamMessage: diam.NewMessage(ccr.diamMessage.Header.CommandCode, ccr.diamMessage.Header.CommandFlags&^diam.RequestFlag, ccr.diamMessage.Header.ApplicationID, - ccr.diamMessage.Header.HopByHopID, ccr.diamMessage.Header.EndToEndID, ccr.diamMessage.Dictionary()), ccrMessage: ccr.diamMessage, debitInterval: ccr.debitInterval, - } - cca.diamMessage = cca.AsBareDiameterMessage() // Add the required fields to the diameterMessage - return cca -} - -// Call Control Answer, bare structure so we can dynamically manage adding it's fields -type CCA struct { - SessionId string `avp:"Session-Id"` - OriginHost string `avp:"Origin-Host"` - OriginRealm string `avp:"Origin-Realm"` - AuthApplicationId int `avp:"Auth-Application-Id"` - CCRequestType int `avp:"CC-Request-Type"` - CCRequestNumber int `avp:"CC-Request-Number"` - ResultCode int `avp:"Result-Code"` - GrantedServiceUnit struct { - CCTime int `avp:"CC-Time"` - } `avp:"Granted-Service-Unit"` - ccrMessage *diam.Message - diamMessage *diam.Message - debitInterval time.Duration - timezone string -} - -// AsBareDiameterMessage converts CCA into a bare DiameterMessage -func (self *CCA) AsBareDiameterMessage() *diam.Message { - var m diam.Message - utils.Clone(self.diamMessage, &m) - m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(self.SessionId)) - m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity(self.OriginHost)) - m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity(self.OriginRealm)) - m.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(self.AuthApplicationId)) - m.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(self.CCRequestType)) - m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Enumerated(self.CCRequestNumber)) - m.NewAVP(avp.ResultCode, avp.Mbit, 0, datatype.Unsigned32(self.ResultCode)) - return &m -} - -// AsDiameterMessage returns the diameter.Message which can be later written on network -func (self *CCA) AsDiameterMessage() *diam.Message { - return self.diamMessage -} - -// SetProcessorAVPs will add AVPs to self.diameterMessage based on template defined in processor.CCAFields -func (self *CCA) SetProcessorAVPs(reqProcessor *config.DARequestProcessor, processorVars processorVars) error { - for _, cfgFld := range reqProcessor.CCAFields { - fmtOut, err := fieldOutVal(self.ccrMessage, cfgFld, nil, processorVars) - if err == ErrFilterNotPassing { // Field not in or filter not passing, try match in answer - fmtOut, err = fieldOutVal(self.diamMessage, cfgFld, nil, processorVars) - } - if err != nil { - if err == ErrFilterNotPassing { - continue - } - return err - } - if err := messageSetAVPsWithPath(self.diamMessage, - splitIntoInterface(cfgFld.FieldId, utils.HIERARCHY_SEP), - fmtOut, cfgFld.Append, self.timezone); err != nil { - return err - } - if cfgFld.BreakOnSuccess { // don't look for another field - break - } - } - return nil } diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index a9770eea3..2f5d97c32 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -17,548 +17,3 @@ along with this program. If not, see */ package agents - -import ( - "bytes" - "encoding/binary" - "fmt" - "reflect" - "testing" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/utils" - "github.com/fiorix/go-diameter/diam" - "github.com/fiorix/go-diameter/diam/avp" - "github.com/fiorix/go-diameter/diam/datatype" - "github.com/fiorix/go-diameter/diam/dict" -) - -var err error - -func TestDisectUsageForCCR(t *testing.T) { - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(0)*time.Second, - time.Duration(300)*time.Second, false); reqType != 1 || reqNr != 0 || reqCCTime != 300 || usedCCTime != 0 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 0 || reqCCTime != 300 || usedCCTime != 35 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, false); reqType != 2 || reqNr != 3 || reqCCTime != 300 || usedCCTime != 35 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(35)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 1 || reqCCTime != 0 || usedCCTime != 35 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(610)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 3 || reqCCTime != 0 || usedCCTime != 10 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } - if reqType, reqNr, reqCCTime, usedCCTime := disectUsageForCCR(time.Duration(935)*time.Second, time.Duration(300)*time.Second, true); reqType != 3 || reqNr != 4 || reqCCTime != 0 || usedCCTime != 35 { - t.Error(reqType, reqNr, reqCCTime, usedCCTime) - } -} - -func TestUsageFromCCR(t *testing.T) { - if usage := usageFromCCR(1, 0, 0, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { - t.Error(usage) - } - if usage := usageFromCCR(2, 0, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { - t.Error(usage) - } - if usage := usageFromCCR(2, 3, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { - t.Error(usage.Seconds()) - } - if usage := usageFromCCR(3, 3, 10, time.Duration(300)*time.Second); usage != time.Duration(610)*time.Second { - t.Error(usage) - } - if usage := usageFromCCR(3, 4, 35, time.Duration(300)*time.Second); usage != time.Duration(935)*time.Second { - t.Error(usage) - } - if usage := usageFromCCR(3, 1, 35, time.Duration(300)*time.Second); usage != time.Duration(35)*time.Second { - t.Error(usage) - } - if usage := usageFromCCR(1, 0, 0, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second { - t.Error(usage) - } -} - -func TestAvpValAsString(t *testing.T) { - originHostStr := "unit_test" - a := diam.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity(originHostStr)) - if avpValStr := avpValAsString(a); avpValStr != originHostStr { - t.Errorf("Expected: %s, received: %s", originHostStr, avpValStr) - } -} - -func TestMetaValueExponent(t *testing.T) { - m := diam.NewRequest(diam.CreditControl, 4, nil) - m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) - m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), - diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), - }, - }), - diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), - }, - }), - }, - }) - if val, err := metaValueExponent(m, nil, utils.ParseRSRFieldsMustCompile( - "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", - utils.INFIELD_SEP), 10); err != nil { - t.Error(err) - } else if val != "0.1" { - t.Error("Received: ", val) - } - if _, err = metaValueExponent(m, nil, - utils.ParseRSRFieldsMustCompile( - "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", - utils.INFIELD_SEP), 10); err == nil { - t.Error("Should have received error") // Insufficient number arguments - } -} - -func TestMetaSum(t *testing.T) { - m := diam.NewRequest(diam.CreditControl, 4, nil) - m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) - m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), - diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), - }, - }), - diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), - }, - }), - }, - }) - if val, err := metaSum(m, nil, - utils.ParseRSRFieldsMustCompile( - "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", - utils.INFIELD_SEP), 0, 10); err != nil { - t.Error(err) - } else if val != "9995" { - t.Error("Received: ", val) - } - if _, err = metaSum(m, nil, utils.ParseRSRFieldsMustCompile( - "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", - utils.INFIELD_SEP), 0, 10); err == nil { - t.Error("Should have received error") // Insufficient number arguments - } -} - -func TestFieldOutVal(t *testing.T) { - m := diam.NewRequest(diam.CreditControl, 4, nil) - m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) - m.NewAVP("Subscription-Id", 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("33708000003")), // Subscription-Id-Data - }}) - m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(1)), // Subscription-Id-Type - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000003")), // Subscription-Id-Data - }}) - m.NewAVP("Service-Identifier", avp.Mbit, 0, datatype.Unsigned32(0)) - m.NewAVP("Requested-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(360))}}) // CC-Time - cfgFld := &config.CfgCdrField{Tag: "StaticTest", Type: utils.META_COMPOSED, FieldId: utils.ToR, - Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true} - eOut := "*voice" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { - t.Error(err) - } else if fldOut != eOut { - t.Errorf("Expecting:\n%s\nReceived:\n%s", eOut, fldOut) - } - cfgFld = &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.Destination, - Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true} - eOut = "360" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { - t.Error(err) - } else if fldOut != eOut { - t.Errorf("Expecting:\n%s\nReceived:\n%s", eOut, fldOut) - } - // With filter on ProcessorVars - cfgFld = &config.CfgCdrField{Tag: "ComposedTestWithProcessorVarsFilter", Type: utils.META_COMPOSED, FieldId: utils.Destination, - FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(INSUFFICIENT_CREDIT)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true} - if _, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err == nil { - t.Error("Should have error") - } - eOut = "360" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), - processorVars{"CGRError": "INSUFFICIENT_CREDIT"}); err != nil { - t.Error(err) - } else if fldOut != eOut { - t.Errorf("Expecting:\n%s\nReceived:\n%s", eOut, fldOut) - } - // Without filter, we shoud get always the first subscriptionId - cfgFld = &config.CfgCdrField{Tag: "Grouped1", Type: utils.MetaGrouped, FieldId: "Account", - Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true} - eOut = "33708000003" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { - t.Error(err) - } else if fldOut != eOut { - t.Errorf("Expecting:\n%s\nReceived:\n%s", eOut, fldOut) - } - // Without groupedAVP, we shoud get the first subscriptionId - cfgFld = &config.CfgCdrField{ - Tag: "Grouped2", - Type: utils.MetaGrouped, FieldId: "Account", - FieldFilter: utils.ParseRSRFieldsMustCompile( - "Subscription-Id>Subscription-Id-Type(1)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile( - "Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true} - eOut = "208708000003" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { - t.Error(err) - } else if fldOut != eOut { - t.Errorf("Expecting:\n%s\nReceived:\n%s", eOut, fldOut) - } - cfgFld = &config.CfgCdrField{ - Tag: "TestMultipleFiltersEmptyReply", - Type: utils.META_COMPOSED, FieldId: "Account", - FieldFilter: utils.ParseRSRFieldsMustCompile( - "*cgrReply>Error(^$);*cgrReply>MaxUsage(!300);*cgrReply>MaxUsage(!0)", - utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile( - "Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), - Mandatory: true} - procVars := processorVars{ - utils.MetaCGRReply: map[string]interface{}{ - utils.Error: "RALS_ERROR:NOT_FOUND", - }, - } - if _, err := fieldOutVal(m, cfgFld, time.Duration(0), - procVars); err != ErrFilterNotPassing { - t.Error(err) - } -} - -func TestSerializeAVPValueFromString(t *testing.T) { - dictAVP, _ := dict.Default.FindAVP(4, "Session-Id") - eValByte := []byte("simuhuawei;1449573472;00002") - if valByte, err := serializeAVPValueFromString(dictAVP, "simuhuawei;1449573472;00002", "UTC"); err != nil { - t.Error(err) - } else if !bytes.Equal(eValByte, valByte) { - t.Errorf("Expecting: %+v, received: %+v", eValByte, valByte) - } - dictAVP, _ = dict.Default.FindAVP(4, "Result-Code") - eValByte = make([]byte, 4) - binary.BigEndian.PutUint32(eValByte, uint32(5031)) - if valByte, err := serializeAVPValueFromString(dictAVP, "5031", "UTC"); err != nil { - t.Error(err) - } else if !bytes.Equal(eValByte, valByte) { - t.Errorf("Expecting: %+v, received: %+v", eValByte, valByte) - } -} - -func TestMessageSetAVPsWithPath(t *testing.T) { - eMessage := diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Session-Id", avp.Mbit, 0, - datatype.UTF8String("simuhuawei;1449573472;00002")) - m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, - eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, - []interface{}{"Session-Id", "Unknown"}, "simuhuawei;1449573472;00002", - false, "UTC"); err == nil || - err.Error() != "Could not find AVP Unknown" { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, - []interface{}{"Session-Id"}, "simuhuawei;1449573472;00002", - false, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - // test append - eMessage.NewAVP("Session-Id", avp.Mbit, 0, - datatype.UTF8String("simuhuawei;1449573472;00003")) - if err := messageSetAVPsWithPath(m, []interface{}{"Session-Id"}, - "simuhuawei;1449573472;00003", true, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - // test overwrite - eMessage = diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Session-Id", avp.Mbit, 0, - datatype.UTF8String("simuhuawei;1449573472;00002")) - m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, - eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, - []interface{}{"Session-Id"}, "simuhuawei;1449573472;00001", - false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, - []interface{}{"Session-Id"}, "simuhuawei;1449573472;00002", - false, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - eMessage = diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("33708000003")), // Subscription-Id-Data - }}) - m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, - eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, - []interface{}{"Subscription-Id", "Subscription-Id-Data"}, - "33708000003", false, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - // test append - eMessage.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Data - }}) - if err := messageSetAVPsWithPath(m, - []interface{}{"Subscription-Id", "Subscription-Id-Type"}, - "0", true, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - // test group append - eMessage = diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Data - diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("33708000003")), // Subscription-Id-Data - }}) - eMsgSrl, _ := eMessage.Serialize() - m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, []interface{}{"Subscription-Id", "Subscription-Id-Type"}, "0", false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Subscription-Id", "Subscription-Id-Data"}, "33708000003", false, "UTC"); err != nil { - t.Error(err) - } else { - mSrl, _ := m.Serialize() - if !bytes.Equal(eMsgSrl, mSrl) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - } - eMessage = diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Granted-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(300)), // Subscription-Id-Data - }}) - m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, []interface{}{"Granted-Service-Unit", "CC-Time"}, "300", false, "UTC"); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eMessage, m) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } - // Multiple append - eMessage = diam.NewRequest(diam.CreditControl, 4, nil) - eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(431, avp.Mbit, 0, &diam.GroupedAVP{ // Granted-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(3600)), - diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(153600)), // "CC-Total-Octets" - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(10)), - }, - }) - eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(431, avp.Mbit, 0, &diam.GroupedAVP{ // Granted-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(2600)), - diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(143600)), // "CC-Total-Octets" - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(11)), // Rating-Group - }, - }) - m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, "3600", false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "153600", false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Rating-Group"}, "10", false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, "2600", true, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "143600", false, "UTC"); err != nil { - t.Error(err) - } - if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Rating-Group"}, "11", false, "UTC"); err != nil { - t.Error(err) - } - if fmt.Sprintf("%q", eMessage) != fmt.Sprintf("%q", m) { // test with fmt since reflect.DeepEqual does not perform properly here - t.Errorf("Expecting: %+v, received: %+v", eMessage, m) - } -} - -func TestCCASetProcessorAVPs(t *testing.T) { - ccr := &CCR{ // Bare information, just the one needed for answer - SessionId: "routinga;1442095190;1476802709", - AuthApplicationId: 4, - CCRequestType: 1, - CCRequestNumber: 0, - } - ccr.diamMessage = ccr.AsBareDiameterMessage() - ccr.diamMessage.NewAVP("Subscription-Id", 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("33708000003")), // Subscription-Id-Data - }}) - ccr.debitInterval = time.Duration(300) * time.Second - cca := NewBareCCAFromCCR(ccr, "CGR-DA", "cgrates.org") - reqProcessor := &config.DARequestProcessor{Id: "UNIT_TEST", // Set template for tests - CCAFields: []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "Subscription-Id/Subscription-Id-Type", Type: utils.META_COMPOSED, - FieldId: "Subscription-Id>Subscription-Id-Type", - Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Subscription-Id/Subscription-Id-Data", Type: utils.META_COMPOSED, - FieldId: "Subscription-Id>Subscription-Id-Data", - Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true}, - }, - } - eMessage := cca.AsDiameterMessage() - eMessage.NewAVP("Subscription-Id", 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("33708000003")), // Subscription-Id-Data - }}) - if err := cca.SetProcessorAVPs(reqProcessor, processorVars{}); err != nil { - t.Error(err) - } else if ccaMsg := cca.AsDiameterMessage(); !reflect.DeepEqual(eMessage, ccaMsg) { - t.Errorf("Expecting: %+v, received: %+v", eMessage, ccaMsg) - } -} - -func TestCCRAsSMGenericEvent(t *testing.T) { - ccr := &CCR{ // Bare information, just the one needed for answer - SessionId: "ccrasgen1", - AuthApplicationId: 4, - CCRequestType: 3, - } - ccr.diamMessage = ccr.AsBareDiameterMessage() - ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(17)), // CC-Time - diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1341)), // CC-Input-Octets - diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(3079)), // CC-Output-Octets - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)), - }, - }) - ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ - AVP: []*diam.AVP{ - diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit - AVP: []*diam.AVP{ - diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage - diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time - diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(8046)), // CC-Input-Octets - diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(46193)), // CC-Output-Octets - }, - }), - diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)), - }, - }) - ccr.diamMessage.NewAVP("FramedIPAddress", avp.Mbit, 0, datatype.OctetString("0AE40041")) - cfgFlds := make([]*config.CfgCdrField, 0) - eSMGEv := map[string]interface{}{"EventName": "DIAMETER_CCR"} - if rSMGEv, err := ccr.AsMapIface(cfgFlds); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { - t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) - } - cfgFlds = []*config.CfgCdrField{ - &config.CfgCdrField{ - Tag: "LastUsed", - FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(1)", utils.INFIELD_SEP), - FieldId: "LastUsed", - Type: "*handler", - HandlerId: "*sum", - Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), - Mandatory: true, - }, - } - eSMGEv = map[string]interface{}{"EventName": "DIAMETER_CCR", "LastUsed": "54239"} - if rSMGEv, err := ccr.AsMapIface(cfgFlds); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { - t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) - } - cfgFlds = []*config.CfgCdrField{ - &config.CfgCdrField{ - Tag: "LastUsed", - FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(99)", utils.INFIELD_SEP), - FieldId: "LastUsed", - Type: "*handler", - HandlerId: "*sum", - Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP), - Mandatory: true, - }, - } - eSMGEv = map[string]interface{}{"EventName": "DIAMETER_CCR", "LastUsed": "4420"} - if rSMGEv, err := ccr.AsMapIface(cfgFlds); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eSMGEv, rSMGEv) { - t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv) - } -} - -func TestPassesFieldFilter(t *testing.T) { - m := diam.NewRequest(diam.CreditControl, 4, nil) // Multiple-Services-Credit-Control>Rating-Group - if pass, _ := passesFieldFilter(m, - utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Rating-Group(^$)", - utils.INFIELD_SEP)[0], nil); !pass { - t.Error("Does not pass") - } - procVars := processorVars{ - utils.MetaCGRReply: map[string]interface{}{ - utils.CapAttributes: map[string]interface{}{ - "RadReply": "AccessAccept", - utils.Account: "1001", - }, - utils.CapMaxUsage: time.Duration(0), - utils.Error: "", - }, - } - if pass, _ := passesFieldFilter(nil, - utils.ParseRSRFieldsMustCompile("*cgrReply>MaxUsage(^0s$)", utils.INFIELD_SEP)[0], - procVars); !pass { - t.Error("not passing valid filter") - } - if pass, _ := passesFieldFilter(nil, - utils.ParseRSRFieldsMustCompile("*cgrReply>MaxUsage{*duration_seconds}(^0$)", utils.INFIELD_SEP)[0], - procVars); !pass { - t.Error("not passing valid filter") - } - if pass, _ := passesFieldFilter(nil, - utils.ParseRSRFieldsMustCompile("*cgrReply>Error(^$)", utils.INFIELD_SEP)[0], - procVars); !pass { - t.Error("not passing valid filter") - } -} From 38a0144e0849c34c4a3f9eb2d6d12313cbf99e5a Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 18 Sep 2018 21:00:49 +0200 Subject: [PATCH 03/52] DiameterAgent config - replacing PubSubConns with ThresholdSConns, DiameterProcessor publish_event with threshods_event --- cmd/cgr-engine/cgr-engine.go | 20 +++++++++++--------- config/config.go | 7 ++++--- config/config_defaults.go | 2 +- config/config_json_test.go | 2 +- config/config_test.go | 8 +++++--- config/daconfig.go | 18 +++++++++--------- config/libconfig_json.go | 4 ++-- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 18dcae1a3..3037e6723 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -278,31 +278,33 @@ func startAsteriskAgent(internalSMGChan chan rpcclient.RpcClientConnection, exit exitChan <- true } -func startDiameterAgent(internalSMGChan, internalPubSubSChan chan rpcclient.RpcClientConnection, exitChan chan bool) { +func startDiameterAgent(internalSMGChan, internalThdSChan chan rpcclient.RpcClientConnection, exitChan chan bool) { var err error utils.Logger.Info("Starting CGRateS DiameterAgent service") - var smgConn, pubsubConn *rpcclient.RpcClientPool + var smgConn, thdSConn *rpcclient.RpcClientPool if len(cfg.DiameterAgentCfg().SessionSConns) != 0 { smgConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.TLSClientKey, cfg.TLSClientCerificate, cfg.ConnectAttempts, cfg.Reconnects, cfg.ConnectTimeout, cfg.ReplyTimeout, cfg.DiameterAgentCfg().SessionSConns, internalSMGChan, cfg.InternalTtl) if err != nil { - utils.Logger.Crit(fmt.Sprintf(" Could not connect to SMG: %s", err.Error())) + utils.Logger.Crit(fmt.Sprintf("<%s> Could not connect to %s: %s", + utils.DiameterAgent, utils.SessionS, err.Error())) exitChan <- true return } } - if len(cfg.DiameterAgentCfg().PubSubConns) != 0 { - pubsubConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.TLSClientKey, cfg.TLSClientCerificate, + if len(cfg.DiameterAgentCfg().ThresholdSConns) != 0 { + thdSConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.TLSClientKey, cfg.TLSClientCerificate, cfg.ConnectAttempts, cfg.Reconnects, cfg.ConnectTimeout, cfg.ReplyTimeout, - cfg.DiameterAgentCfg().PubSubConns, internalPubSubSChan, cfg.InternalTtl) + cfg.DiameterAgentCfg().ThresholdSConns, internalThdSChan, cfg.InternalTtl) if err != nil { - utils.Logger.Crit(fmt.Sprintf(" Could not connect to PubSubS: %s", err.Error())) + utils.Logger.Crit(fmt.Sprintf("<%s> Could not connect to %s: %s", + utils.DiameterAgent, utils.ThresholdS, err.Error())) exitChan <- true return } } - da, err := agents.NewDiameterAgent(cfg, smgConn, pubsubConn) + da, err := agents.NewDiameterAgent(cfg, smgConn, thdSConn) if err != nil { utils.Logger.Err(fmt.Sprintf(" error: %s!", err)) exitChan <- true @@ -1298,7 +1300,7 @@ func main() { } if cfg.DiameterAgentCfg().Enabled { - go startDiameterAgent(internalSMGChan, internalPubSubSChan, exitChan) + go startDiameterAgent(internalSMGChan, internalThresholdSChan, exitChan) } if cfg.RadiusAgentCfg().Enabled { diff --git a/config/config.go b/config/config.go index 133983dc0..ae434a679 100755 --- a/config/config.go +++ b/config/config.go @@ -637,9 +637,10 @@ func (self *CGRConfig) checkConfigSanity() error { return errors.New("SMGeneric not enabled but referenced by DiameterAgent component") } } - for _, daPubSubSConn := range self.diameterAgentCfg.PubSubConns { - if daPubSubSConn.Address == utils.MetaInternal && !self.PubSubServerEnabled { - return errors.New("PubSubS not enabled but requested by DiameterAgent component.") + for _, conn := range self.diameterAgentCfg.ThresholdSConns { + if conn.Address == utils.MetaInternal && !self.ThresholdSCfg().Enabled { + return fmt.Errorf("%s not enabled but requested by %s component.", + utils.ThresholdS, utils.DiameterAgent) } } } diff --git a/config/config_defaults.go b/config/config_defaults.go index d914707ec..24d3e6771 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -369,7 +369,7 @@ const CGRATES_CFG_JSON = ` "sessions_conns": [ {"address": "*internal"} // connection towards SessionService ], - "pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234> + "thresholds_conns": [], // address where to reach the thresholds service, empty to disable thresholds functionality: <""|*internal|x.y.z.y:1234> "create_cdr": true, // create CDR out of CCR terminate and send it to SessionS "cdr_requires_session": true, // only create CDR if there is an active session at terminate "debit_interval": "5m", // interval for CCR updates diff --git a/config/config_json_test.go b/config/config_json_test.go index 376409221..813762486 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -602,7 +602,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { &HaPoolJsonCfg{ Address: utils.StringPointer(utils.MetaInternal), }}, - Pubsubs_conns: &[]*HaPoolJsonCfg{}, + Thresholds_conns: &[]*HaPoolJsonCfg{}, Create_cdr: utils.BoolPointer(true), Cdr_requires_session: utils.BoolPointer(true), Debit_interval: utils.StringPointer("5m"), diff --git a/config/config_test.go b/config/config_test.go index 3e9788505..0dc64eaba 100755 --- a/config/config_test.go +++ b/config/config_test.go @@ -942,7 +942,7 @@ func TestCgrCfgJSONDefaultsDiameterAgentCfg(t *testing.T) { DictionariesDir: "/usr/share/cgrates/diameter/dict/", SessionSConns: []*HaPoolConfig{ &HaPoolConfig{Address: "*internal"}}, - PubSubConns: []*HaPoolConfig{}, + ThresholdSConns: []*HaPoolConfig{}, CreateCDR: true, DebitInterval: 5 * time.Minute, Timezone: "", @@ -965,8 +965,10 @@ func TestCgrCfgJSONDefaultsDiameterAgentCfg(t *testing.T) { if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.SessionSConns, testDA.SessionSConns) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.SessionSConns, testDA.SessionSConns) } - if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.PubSubConns, testDA.PubSubConns) { - t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.PubSubConns, testDA.PubSubConns) + if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.ThresholdSConns, + testDA.ThresholdSConns) { + t.Errorf("expecting: %+v, received: %+v", + cgrCfg.diameterAgentCfg.ThresholdSConns, testDA.ThresholdSConns) } if !reflect.DeepEqual(cgrCfg.diameterAgentCfg.CreateCDR, testDA.CreateCDR) { t.Errorf("expecting: %+v, received: %+v", cgrCfg.diameterAgentCfg.CreateCDR, testDA.CreateCDR) diff --git a/config/daconfig.go b/config/daconfig.go index e7daf35a8..b009f7afa 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -29,7 +29,7 @@ type DiameterAgentCfg struct { Listen string // address where to listen for diameter requests DictionariesDir string SessionSConns []*HaPoolConfig // connections towards SMG component - PubSubConns []*HaPoolConfig // connection towards pubsubs + ThresholdSConns []*HaPoolConfig // connection towards pubsubs CreateCDR bool CDRRequiresSession bool DebitInterval time.Duration @@ -61,11 +61,11 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro self.SessionSConns[idx].loadFromJsonCfg(jsnHaCfg) } } - if jsnCfg.Pubsubs_conns != nil { - self.PubSubConns = make([]*HaPoolConfig, len(*jsnCfg.Pubsubs_conns)) - for idx, jsnHaCfg := range *jsnCfg.Pubsubs_conns { - self.PubSubConns[idx] = NewDfltHaPoolConfig() - self.PubSubConns[idx].loadFromJsonCfg(jsnHaCfg) + if jsnCfg.Thresholds_conns != nil { + self.ThresholdSConns = make([]*HaPoolConfig, len(*jsnCfg.Thresholds_conns)) + for idx, jsnHaCfg := range *jsnCfg.Thresholds_conns { + self.ThresholdSConns[idx] = NewDfltHaPoolConfig() + self.ThresholdSConns[idx].loadFromJsonCfg(jsnHaCfg) } } if jsnCfg.Create_cdr != nil { @@ -121,7 +121,7 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro type DARequestProcessor struct { Id string DryRun bool - PublishEvent bool + ThresholdSEvent bool RequestFilter utils.RSRFields Flags utils.StringMap // Various flags to influence behavior ContinueOnSuccess bool @@ -140,8 +140,8 @@ func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg if jsnCfg.Dry_run != nil { self.DryRun = *jsnCfg.Dry_run } - if jsnCfg.Publish_event != nil { - self.PublishEvent = *jsnCfg.Publish_event + if jsnCfg.Thresholds_event != nil { + self.ThresholdSEvent = *jsnCfg.Thresholds_event } var err error if jsnCfg.Request_filter != nil { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 07f2cb6b1..8fd6623a3 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -331,7 +331,7 @@ type DiameterAgentJsonCfg struct { Listen *string // address where to listen for diameter requests Dictionaries_dir *string // path towards additional dictionaries Sessions_conns *[]*HaPoolJsonCfg // Connections towards generic SM - Pubsubs_conns *[]*HaPoolJsonCfg // connection towards pubsubs + Thresholds_conns *[]*HaPoolJsonCfg // connection towards pubsubs Create_cdr *bool Cdr_requires_session *bool Debit_interval *string @@ -347,7 +347,7 @@ type DiameterAgentJsonCfg struct { type DARequestProcessorJsnCfg struct { Id *string Dry_run *bool - Publish_event *bool + Thresholds_event *bool Request_filter *string Flags *[]string Continue_on_success *bool From b56e322e88089550aa43fbc11f8de75ad2d0ee06 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 19 Sep 2018 08:59:53 +0300 Subject: [PATCH 04/52] Add to contributors --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d8aec749a..03fe28de2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -69,6 +69,7 @@ information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file. | @vtzan | Vasilios Tzanoudakis | | @ccppprogrammer | Sergei Lavrov | | @chrisgis333 | Krzysztof Grzyb | +| @Trial97 | Alexandru Tripon | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/utils/reflect.go b/utils/reflect.go index 8475ad705..acbcd5069 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -244,7 +244,7 @@ func IfaceAsString(fld interface{}) (out string, err error) { case int64: return strconv.FormatInt(fld.(int64), 10), nil case uint32: - return strconv.FormatUint(uint64(fld.(int32)), 10), nil + return strconv.FormatUint(uint64(fld.(uint32)), 10), nil case uint64: return strconv.FormatUint(fld.(uint64), 10), nil case bool: From 70a07a16363f13310fd7e2748405b765d27c0022 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 2 Oct 2018 19:42:09 +0200 Subject: [PATCH 45/52] Config InflateTemplates function as standalone, added *appid to request *vars --- agents/agentreq.go | 17 +++------ agents/diamagent.go | 9 +++-- config/config_defaults.go | 2 +- config/config_json_test.go | 2 +- config/daconfig.go | 8 ++--- config/fctemplate.go | 35 ++++++++++--------- data/conf/samples/diamagent/data.json | 8 ++--- data/conf/samples/diamagent/dryrun.json | 25 +++++++++++++ ...{diameter_processors.json => message.json} | 22 ++---------- data/conf/samples/diamagent/simpa.json | 2 +- data/conf/samples/diamagent/voice.json | 6 ++-- utils/consts.go | 1 + 12 files changed, 72 insertions(+), 65 deletions(-) create mode 100644 data/conf/samples/diamagent/dryrun.json rename data/conf/samples/diamagent/{diameter_processors.json => message.json} (68%) diff --git a/agents/agentreq.go b/agents/agentreq.go index 0c6224d07..68581ead8 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -91,20 +91,11 @@ func (ar *AgentRequest) FieldAsInterface(fldPath []string) (val interface{}, err // FieldAsString implements engine.DataProvider func (ar *AgentRequest) FieldAsString(fldPath []string) (val string, err error) { - switch fldPath[0] { - default: - return "", fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) - case utils.MetaReq: - return ar.Request.FieldAsString(fldPath[1:]) - case utils.MetaVars: - return ar.Vars.FieldAsString(fldPath[1:]) - case utils.MetaCgreq: - return ar.CGRRequest.FieldAsString(fldPath[1:]) - case utils.MetaCgrep: - return ar.CGRReply.FieldAsString(fldPath[1:]) - case utils.MetaRep: - return ar.Reply.FieldAsString(fldPath[1:]) + var iface interface{} + if iface, err = ar.FieldAsInterface(fldPath); err != nil { + return } + return utils.IfaceAsString(iface) } // AsNavigableMap implements engine.DataProvider diff --git a/agents/diamagent.go b/agents/diamagent.go index ad5648d39..df875bace 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -47,11 +47,15 @@ func NewDiameterAgent(cgrCfg *config.CGRConfig, filterS *engine.FilterS, msgTemplates := da.cgrCfg.DiameterAgentCfg().Templates // Inflate *template field types for _, procsr := range da.cgrCfg.DiameterAgentCfg().RequestProcessors { - if err := procsr.RequestFields.InflateTemplates(msgTemplates); err != nil { + if tpls, err := config.InflateTemplates(procsr.RequestFields, msgTemplates); err != nil { return nil, err + } else if tpls != nil { + procsr.RequestFields = tpls } - if err := procsr.ReplyFields.InflateTemplates(msgTemplates); err != nil { + if tpls, err := config.InflateTemplates(procsr.ReplyFields, msgTemplates); err != nil { return nil, err + } else if tpls != nil { + procsr.ReplyFields = tpls } } return da, nil @@ -110,6 +114,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { utils.OriginRealm: da.cgrCfg.DiameterAgentCfg().OriginRealm, utils.ProductName: da.cgrCfg.DiameterAgentCfg().ProductName, utils.MetaApp: dApp.Name, + utils.MetaAppID: dApp.ID, utils.MetaCmd: dCmd.Short + "R", } rply := config.NewNavigableMap(nil) // share it among different processors diff --git a/config/config_defaults.go b/config/config_defaults.go index 71b54847f..7626b0b07 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -384,7 +384,7 @@ const CGRATES_CFG_JSON = ` {"tag": "OriginRealm", "field_id": "Origin-Realm", "type": "*composed", "value": "~*vars.OriginRealm", "mandatory": true}, {"tag": "AuthApplicationId", "field_id": "Auth-Application-Id", "type": "*composed", - "value": "~*vars.ProductName", "mandatory": true}, + "value": "~*vars.*appid", "mandatory": true}, {"tag": "CCRequestType", "field_id": "CC-Request-Type", "type": "*composed", "value": "~*req.CC-Request-Type", "mandatory": true}, {"tag": "CCRequestNumber", "field_id": "CC-Request-Number", "type": "*composed", diff --git a/config/config_json_test.go b/config/config_json_test.go index bf566cad6..1f3cddbca 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -631,7 +631,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { {Tag: utils.StringPointer("AuthApplicationId"), Field_id: utils.StringPointer("Auth-Application-Id"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer("~*vars.ProductName"), + Value: utils.StringPointer("~*vars.*appid"), Mandatory: utils.BoolPointer(true)}, {Tag: utils.StringPointer("CCRequestType"), Field_id: utils.StringPointer("CC-Request-Type"), diff --git a/config/daconfig.go b/config/daconfig.go index 7eaf46264..011ff7639 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -31,7 +31,7 @@ type DiameterAgentCfg struct { OriginRealm string VendorId int ProductName string - Templates map[string]FCTemplates + Templates map[string][]*FCTemplate RequestProcessors []*DARequestProcessor } @@ -69,7 +69,7 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) (err e } if jsnCfg.Templates != nil { if da.Templates == nil { - da.Templates = make(map[string]FCTemplates) + da.Templates = make(map[string][]*FCTemplate) } for k, jsnTpls := range jsnCfg.Templates { if da.Templates[k], err = FCTemplatesFromFCTemplatesJsonCfg(jsnTpls); err != nil { @@ -107,8 +107,8 @@ type DARequestProcessor struct { Flags utils.StringMap Timezone string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> ContinueOnSuccess bool - RequestFields FCTemplates - ReplyFields FCTemplates + RequestFields []*FCTemplate + ReplyFields []*FCTemplate } func (dap *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg) (err error) { diff --git a/config/fctemplate.go b/config/fctemplate.go index 7f57270db..38d9da4fa 100755 --- a/config/fctemplate.go +++ b/config/fctemplate.go @@ -129,31 +129,34 @@ func FCTemplatesFromFCTemplatesJsonCfg(jsnCfgFlds []*FcTemplateJsonCfg) ([]*FCTe return retFields, nil } -type FCTemplates []*FCTemplate - -// PopulateFromTemplates will replace fields of type "*template" with values out of map -func (tpls FCTemplates) InflateTemplates(msgTemplates map[string]FCTemplates) (err error) { - for i := 0; i < len(tpls); { - if tpls[i].Type == utils.MetaTemplate { - var tplID string - if tplID, err = tpls[i].Value.ParseValue(nil); err != nil { - return +// InflateTemplates will replace the *template fields with template content out msgTpls +func InflateTemplates(fcts []*FCTemplate, msgTpls map[string][]*FCTemplate) ([]*FCTemplate, error) { + var hasTpl bool + for i := 0; i < len(fcts); { + if fcts[i].Type == utils.MetaTemplate { + hasTpl = true + tplID, err := fcts[i].Value.ParseValue(nil) + if err != nil { + return nil, err } - refTpl, has := msgTemplates[tplID] + refTpl, has := msgTpls[tplID] if !has { - return fmt.Errorf("no template with id: <%s>", tplID) + return nil, fmt.Errorf("no template with id: <%s>", tplID) } else if len(refTpl) == 0 { continue } - wrkSlice := make([]*FCTemplate, len(refTpl)+len(tpls[i:])-1) // so we can cover tpls[i+1:] + wrkSlice := make([]*FCTemplate, len(refTpl)+len(fcts[i:])-1) // so we can cover tpls[i+1:] copy(wrkSlice[:len(refTpl)], refTpl) // copy fields out of referenced template - if len(tpls[i:]) > 1 { // copy the rest of the fields after MetaTemplate - copy(wrkSlice[len(refTpl):], tpls[i+1:]) + if len(fcts[i:]) > 1 { // copy the rest of the fields after MetaTemplate + copy(wrkSlice[len(refTpl):], fcts[i+1:]) } - tpls = append(tpls[:i], wrkSlice...) // append the work + fcts = append(fcts[:i], wrkSlice...) // append the work continue // don't increase index so we can recheck } i++ } - return + if !hasTpl { + return nil, nil + } + return fcts, nil } diff --git a/data/conf/samples/diamagent/data.json b/data/conf/samples/diamagent/data.json index 6e7da7f2e..bc9d340cc 100644 --- a/data/conf/samples/diamagent/data.json +++ b/data/conf/samples/diamagent/data.json @@ -6,7 +6,7 @@ { "id": "data_init", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:1", "*prefix:*req.Service-Context-Id:gprs"], + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:1", "*prefix:*req.Service-Context-Id:gprs"], "flags": ["*initiate", "*accounts"], "request_fields":[ {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*data"}, @@ -32,7 +32,7 @@ { "id": "data_update_grp1", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:2", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:2", "*string:*req.Multiple-Services-Credit-Control.Rating-Group:1", "*prefix:*req.Service-Context-Id:gprs"], "flags": ["*update", "*accounts"], "continue_on_success": true, @@ -65,7 +65,7 @@ { "id": "data_update_grp2", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:2", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:2", "*string:*req.Multiple-Services-Credit-Control.Rating-Group[1]:2", "*prefix:*req.Service-Context-Id:gprs"], "flags": ["*update", "*accounts"], "request_fields":[ @@ -97,7 +97,7 @@ { "id": "data_terminate", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:3", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:3", "*prefix:*req.Service-Context-Id:gprs"], "flags": ["*terminate", "*accounts"], "request_fields":[ diff --git a/data/conf/samples/diamagent/dryrun.json b/data/conf/samples/diamagent/dryrun.json new file mode 100644 index 000000000..28b03dba2 --- /dev/null +++ b/data/conf/samples/diamagent/dryrun.json @@ -0,0 +1,25 @@ +{ + +"diameter_agent": { + "request_processors": [ + + { + "id": "dryrun1", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.Service-Context-Id:TestDiamITDryRun"], + "flags": ["*dryrun"], + "request_fields":[ + {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", + "value": "~*req.Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid"}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "~*req.Event-Timestamp", "mandatory": true}, + ], + "reply_fields":[ + {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, + {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, + ], + }, + ], +}, + +} \ No newline at end of file diff --git a/data/conf/samples/diamagent/diameter_processors.json b/data/conf/samples/diamagent/message.json similarity index 68% rename from data/conf/samples/diamagent/diameter_processors.json rename to data/conf/samples/diamagent/message.json index 43ec85e64..86bd7e1ce 100644 --- a/data/conf/samples/diamagent/diameter_processors.json +++ b/data/conf/samples/diamagent/message.json @@ -3,26 +3,9 @@ "diameter_agent": { "request_processors": [ - { - "id": "dryrun1", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.Service-Context-Id:gprs"], - "flags": ["*dryrun"], - "request_fields":[ - {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"}, - {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", - "value": "~*req.Session-Id", "mandatory": true}, - {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid"}, - {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "~*req.Event-Timestamp", "mandatory": true}, - ], - "reply_fields":[ - {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, - ], - }, - { "id": "message", - "filters": ["*string:*vars.cmd:*ccr", "*prefix:*req.Service-Context-Id:message", + "filters": ["*string:*vars.*cmd:CCR", "*prefix:*req.Service-Context-Id:message", "*string:*req.CC-Request-Type:4"], "flags": ["*event", "*accounts"], "request_fields":[ @@ -54,5 +37,4 @@ ], }, -} - +} \ No newline at end of file diff --git a/data/conf/samples/diamagent/simpa.json b/data/conf/samples/diamagent/simpa.json index 21708bfdd..d58867b16 100644 --- a/data/conf/samples/diamagent/simpa.json +++ b/data/conf/samples/diamagent/simpa.json @@ -5,7 +5,7 @@ "request_processors": [ { "id": "simpa_event", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:4", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:4", "*prefix:*req.Service-Context-Id:simpa"], "flags": ["*event", "*accounts"], "request_fields":[ diff --git a/data/conf/samples/diamagent/voice.json b/data/conf/samples/diamagent/voice.json index e79c9ba85..1ff077007 100644 --- a/data/conf/samples/diamagent/voice.json +++ b/data/conf/samples/diamagent/voice.json @@ -5,7 +5,7 @@ "request_processors": [ { "id": "VoiceInit", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:1", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:1", "*prefix:*req.Service-Context-Id:voice"], "flags": ["*initiate", "*accounts", "*attributes"], "request_fields":[ @@ -33,7 +33,7 @@ }, { "id": "VoiceUpdate", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:2", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:2", "*prefix:*req.Service-Context-Id:voice"], "flags": ["*update", "*accounts", "*attributes"], "request_fields":[ @@ -59,7 +59,7 @@ }, { "id": "VoiceTerminate", - "filters": ["*string:*vars.cmd:*ccr", "*string:*req.CC-Request-Type:3", + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:3", "*prefix:*req.Service-Context-Id:voice"], "flags": ["*terminate", "*accounts", "*attributes"], "request_fields":[ diff --git a/utils/consts.go b/utils/consts.go index 3a1692bdf..0d5220269 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -521,6 +521,7 @@ const ( ANDSep = "&" PipeSep = "|" MetaApp = "*app" + MetaAppID = "*appid" MetaCmd = "*cmd" MetaTemplate = "*template" MetaCCA = "*cca" From 08fab209171eab13ae6ac7c05a47debd1808cb52 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 2 Oct 2018 20:40:13 +0200 Subject: [PATCH 46/52] Diameter Result-Code should not be in *cca template --- agents/diamagent.go | 3 ++- config/config_defaults.go | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/agents/diamagent.go b/agents/diamagent.go index df875bace..3fd911b3b 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -147,6 +147,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { writeOnConn(c, m.Answer(diam.UnableToComply)) return } + fmt.Printf("After processing successfully the message, have reply: %+v\n", rply) a := m.Answer(diam.Success) // write reply into message for _, val := range rply.Values() { @@ -191,7 +192,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { return } } - writeOnConn(c, m.Answer(diam.UnableToComply)) + writeOnConn(c, a) } func (da *DiameterAgent) processRequest(reqProcessor *config.DARequestProcessor, diff --git a/config/config_defaults.go b/config/config_defaults.go index 7626b0b07..648649d4e 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -377,8 +377,6 @@ const CGRATES_CFG_JSON = ` "*cca": [ {"tag": "SessionId", "field_id": "Session-Id", "type": "*composed", "value": "~*req.Session-Id", "mandatory": true}, - {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", - "value": "2001", "mandatory": true}, {"tag": "OriginHost", "field_id": "Origin-Host", "type": "*composed", "value": "~*vars.OriginHost", "mandatory": true}, {"tag": "OriginRealm", "field_id": "Origin-Realm", "type": "*composed", From 56ada587d2a936e406708a16cdeec1ab0ee08fbd Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 3 Oct 2018 19:43:35 +0200 Subject: [PATCH 47/52] Completing Diameter DryRun integration test --- agents/diam_it_test.go | 88 ++++++++++++++++++++----- agents/diamagent.go | 9 ++- agents/libdiam.go | 21 +++--- agents/libdiam_test.go | 74 +++++++++++++++++++++ agents/libhttpagent.go | 11 ++-- agents/librad.go | 11 ++-- data/conf/samples/diamagent/dryrun.json | 4 +- 7 files changed, 179 insertions(+), 39 deletions(-) diff --git a/agents/diam_it_test.go b/agents/diam_it_test.go index 5c27da48b..391cd7e22 100644 --- a/agents/diam_it_test.go +++ b/agents/diam_it_test.go @@ -46,7 +46,7 @@ var dmtClient *DiameterClient var rplyTimeout time.Duration -func TestDiamITInitCfg(t *testing.T) { +func TestDiamItInitCfg(t *testing.T) { daCfgPath = path.Join(*dataDir, "conf", "samples", "diamagent") // Init config first var err error @@ -60,14 +60,14 @@ func TestDiamITInitCfg(t *testing.T) { } // Remove data in both rating and accounting db -func TestDiamITResetDataDb(t *testing.T) { +func TestDiamItResetDataDb(t *testing.T) { if err := engine.InitDataDb(daCfg); err != nil { t.Fatal(err) } } // Wipe out the cdr database -func TestDiamITResetStorDb(t *testing.T) { +func TestDiamItResetStorDb(t *testing.T) { if err := engine.InitStorDb(daCfg); err != nil { t.Fatal(err) } @@ -75,14 +75,14 @@ func TestDiamITResetStorDb(t *testing.T) { /* // Start CGR Engine -func TestDiamITStartEngine(t *testing.T) { +func TestDiamItStartEngine(t *testing.T) { if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { t.Fatal(err) } } */ -func TestDiamITConnectDiameterClient(t *testing.T) { +func TestDiamItConnectDiameterClient(t *testing.T) { dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, @@ -93,7 +93,7 @@ func TestDiamITConnectDiameterClient(t *testing.T) { } // Connect rpc client to rater -func TestDiamITApierRpcConn(t *testing.T) { +func TestDiamItApierRpcConn(t *testing.T) { var err error apierRpc, err = jsonrpc.Dial("tcp", daCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed if err != nil { @@ -102,7 +102,7 @@ func TestDiamITApierRpcConn(t *testing.T) { } // Load the tariff plan, creating accounts and their balances -func TestDiamITTPFromFolder(t *testing.T) { +func TestDiamItTPFromFolder(t *testing.T) { attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} var loadInst utils.LoadInstance if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { @@ -111,7 +111,7 @@ func TestDiamITTPFromFolder(t *testing.T) { time.Sleep(time.Duration(1000) * time.Millisecond) // Give time for scheduler to execute topups } -func TestDiamITDryRun(t *testing.T) { +func TestDiamItDryRun(t *testing.T) { ccr := diam.NewRequest(diam.CreditControl, 4, nil) ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) @@ -120,7 +120,7 @@ func TestDiamITDryRun(t *testing.T) { ccr.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("CGR-DA")) ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("TestDiamITDryRun")) // Match specific DryRun profile + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("TestDiamItDryRun")) // Match specific DryRun profile ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1)) ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) @@ -169,15 +169,73 @@ func TestDiamITDryRun(t *testing.T) { if msg == nil { t.Fatal("No message returned") } - avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID) - if err != nil { + // Result-Code + eVal := "2002" + 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) } - if len(avps) == 0 { - t.Error("Result-Code") + eVal = "cgrates;1451911932;00082" + 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 != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) } - eVal := "300" - if val, err := diamAVPAsString(avps[0]); err != nil { + eVal = "CGR-DA" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Origin-Host"}, 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) + } + eVal = "cgrates.org" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Origin-Realm"}, 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) + } + eVal = "4" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Auth-Application-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 != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) + } + eVal = "1" + if avps, err := msg.FindAVPsWithPath([]interface{}{"CC-Request-Type"}, 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) + } + eVal = "1" + if avps, err := msg.FindAVPsWithPath([]interface{}{"CC-Request-Number"}, 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/agents/diamagent.go b/agents/diamagent.go index 3fd911b3b..39e310a91 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -147,7 +147,6 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { writeOnConn(c, m.Answer(diam.UnableToComply)) return } - fmt.Printf("After processing successfully the message, have reply: %+v\n", rply) a := m.Answer(diam.Success) // write reply into message for _, val := range rply.Values() { @@ -179,12 +178,12 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { writeOnConn(c, m.Answer(diam.UnableToComply)) return } - var apnd bool - if itm.Config == nil || !itm.Config.NewBranch { - apnd = true + var newBranch bool + if itm.Config != nil && itm.Config.NewBranch { + newBranch = true } if err := messageSetAVPsWithPath(a, itm.Path, - itmStr, apnd, da.cgrCfg.DefaultTimezone); err != nil { + itmStr, newBranch, da.cgrCfg.DefaultTimezone); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> error: %s setting reply item: %s for message: %s", utils.DiameterAgent, err.Error(), utils.ToJSON(itm), m)) diff --git a/agents/libdiam.go b/agents/libdiam.go index b8a5f193f..5f5cc403f 100644 --- a/agents/libdiam.go +++ b/agents/libdiam.go @@ -194,7 +194,7 @@ func newDiamDataType(typ datatype.TypeID, valStr, // messageAddAVPsWithPath will dynamically add AVPs into the message // append: append to the message, on false overwrite if AVP is single or add to group if AVP is Grouped func messageSetAVPsWithPath(m *diam.Message, pathStr []string, - avpValStr string, appnd bool, tmz string) (err error) { + avpValStr string, newBranch bool, tmz string) (err error) { if len(pathStr) == 0 { return errors.New("empty path as AVP filter") } @@ -209,11 +209,11 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, dictAVPs[i] = dictAVP } } - if dictAVPs[len(path)-1].Data.Type == diam.GroupedAVPType { + lastAVPIdx := len(path) - 1 + if dictAVPs[lastAVPIdx].Data.Type == diam.GroupedAVPType { return errors.New("last AVP in path cannot be GroupedAVP") } var msgAVP *diam.AVP // Keep a reference here towards last AVP - lastAVPIdx := len(path) - 1 for i := lastAVPIdx; i >= 0; i-- { var typeVal datatype.Type if i == lastAVPIdx { @@ -225,7 +225,7 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, AVP: []*diam.AVP{msgAVP}} } newMsgAVP := diam.NewAVP(dictAVPs[i].Code, avp.Mbit, dictAVPs[i].VendorID, typeVal) // FixMe: maybe Mbit with dictionary one - if i == lastAVPIdx-1 && !appnd { // last AVP needs to be appended in group + if i == lastAVPIdx-1 && !newBranch { avps, err := m.FindAVPsWithPath(path[:lastAVPIdx], dict.UndefinedVendorID) if err != nil { return err @@ -239,7 +239,7 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, } msgAVP = newMsgAVP } - if !appnd { // Not group AVP, replace the previous set one with this one + if !newBranch { // Not group AVP, replace the previous set one with this one avps, err := m.FindAVPsWithPath(path, dict.UndefinedVendorID) if err != nil { return err @@ -302,11 +302,14 @@ func (dP *diameterDP) FieldAsString(fldPath []string) (data string, err error) { // FieldAsInterface is part of engine.DataProvider interface func (dP *diameterDP) FieldAsInterface(fldPath []string) (data interface{}, err error) { - if data, err = dP.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return nil, err + if data, err = dP.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return nil, err + } + err = nil // cancel previous err + } else { + return // data was found in cache } - err = nil // cancel previous err // lastPath can contain selector inside lastPath := fldPath[len(fldPath)-1] var slctrStr string diff --git a/agents/libdiam_test.go b/agents/libdiam_test.go index 9949d998d..151bea642 100644 --- a/agents/libdiam_test.go +++ b/agents/libdiam_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package agents import ( + "reflect" "testing" "github.com/fiorix/go-diameter/diam" @@ -103,3 +104,76 @@ func TestDPFieldAsInterface(t *testing.T) { t.Errorf("Expecting: %v, received: %v", eOut, out) } } + +func TestMessageSetAVPsWithPath(t *testing.T) { + eMessage := diam.NewRequest(diam.CreditControl, 4, nil) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00001")) + m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, + eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00001", + false, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage, m) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // create same attribute twice + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00002")) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00002", + true, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage.AVP, m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // overwrite of previous attribute + eMessage = diam.NewRequest(diam.CreditControl, 4, nil) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00001")) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00003")) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00003", + false, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage.AVP, m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // adding a groupped AVP + eMessage.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), + }}) + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Type"}, "0", + false, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Data"}, "1001", + false, "UTC"); err != nil { + t.Error(err) + } else if len(eMessage.AVP) != len(m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + eMessage.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1002")), + }}) + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Type"}, "1", + true, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Data"}, "1002", + false, "UTC"); err != nil { + t.Error(err) + } else if len(eMessage.AVP) != len(m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } +} diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go index 4b06c6abb..ff2c47dbe 100644 --- a/agents/libhttpagent.go +++ b/agents/libhttpagent.go @@ -62,11 +62,14 @@ func (hU *httpUrlDP) FieldAsInterface(fldPath []string) (data interface{}, err e if len(fldPath) != 1 { return nil, utils.ErrNotFound } - if data, err = hU.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return + if data, err = hU.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return + } + err = nil // cancel previous err + } else { + return // data found in cache } - err = nil // cancel previous err data = hU.req.FormValue(fldPath[0]) hU.cache.Set(fldPath, data, false) return diff --git a/agents/librad.go b/agents/librad.go index a133a22cd..33eabfd90 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -158,11 +158,14 @@ func (pk *radiusDP) FieldAsInterface(fldPath []string) (data interface{}, err er if len(fldPath) != 1 { return nil, utils.ErrNotFound } - if data, err = pk.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return + if data, err = pk.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return + } + err = nil // cancel previous err + } else { + return // data found in cache } - err = nil // cancel previous err if len(pk.req.AttributesWithName(fldPath[0], "")) != 0 { data = pk.req.AttributesWithName(fldPath[0], "")[0].GetStringValue() } diff --git a/data/conf/samples/diamagent/dryrun.json b/data/conf/samples/diamagent/dryrun.json index 28b03dba2..452149651 100644 --- a/data/conf/samples/diamagent/dryrun.json +++ b/data/conf/samples/diamagent/dryrun.json @@ -5,7 +5,7 @@ { "id": "dryrun1", - "filters": ["*string:*vars.*cmd:CCR", "*string:*req.Service-Context-Id:TestDiamITDryRun"], + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.Service-Context-Id:TestDiamItDryRun"], "flags": ["*dryrun"], "request_fields":[ {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"}, @@ -16,7 +16,7 @@ ], "reply_fields":[ {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, + {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "2002"}, ], }, ], From 60a80fc8f1734c97fa1006c8bb2418bbe6147184 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 3 Oct 2018 19:51:01 +0200 Subject: [PATCH 48/52] Removing Result-Code out of config test --- config/config_json_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/config/config_json_test.go b/config/config_json_test.go index 1f3cddbca..8488ee041 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -613,11 +613,6 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("~*req.Session-Id"), Mandatory: utils.BoolPointer(true)}, - {Tag: utils.StringPointer("ResultCode"), - Field_id: utils.StringPointer("Result-Code"), - Type: utils.StringPointer(utils.META_CONSTANT), - Value: utils.StringPointer("2001"), - Mandatory: utils.BoolPointer(true)}, {Tag: utils.StringPointer("OriginHost"), Field_id: utils.StringPointer("Origin-Host"), Type: utils.StringPointer(utils.META_COMPOSED), @@ -650,8 +645,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { if cfg, err := dfCgrJsonCfg.DiameterAgentJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { - rcv := *cfg.Request_processors - t.Errorf("Received: %+v", rcv) + t.Errorf("Received: %+v", cfg) } } From e5299d97b138d9e6c046c19371a55c30c63379cc Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 4 Oct 2018 20:31:39 +0200 Subject: [PATCH 49/52] 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 = "^" From b70bbe180dd6903618523a80b4e64836ae8eb948 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 5 Oct 2018 12:42:08 +0200 Subject: [PATCH 50/52] Diameter CallControl operational for voice --- agents/agentreq.go | 6 +- agents/diam_it_test.go | 155 ++++++++++++++++++++++++- data/conf/samples/diamagent/voice.json | 16 ++- 3 files changed, 170 insertions(+), 7 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index 9354af2a0..4fc2d4182 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -204,7 +204,11 @@ func (aReq *AgentRequest) ParseField( return "", fmt.Errorf("invalid debitInterval <%s> to %s", strVal3, utils.MetaCCUsage) } - return usedCCTime + time.Duration(debitItvl.Nanoseconds()*reqNr), nil + mltpl := reqNr - 2 // init and terminate will be ignored + if mltpl < 0 { + mltpl = 0 + } + return usedCCTime + time.Duration(debitItvl.Nanoseconds()*mltpl), nil } if err != nil { diff --git a/agents/diam_it_test.go b/agents/diam_it_test.go index a54d5afe7..42c05bcc1 100644 --- a/agents/diam_it_test.go +++ b/agents/diam_it_test.go @@ -263,7 +263,7 @@ func TestDiamItCCRInit(t *testing.T) { 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))}}) + 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))}}) @@ -305,7 +305,7 @@ func TestDiamItCCRInit(t *testing.T) { t.Errorf("expecting: %s, received: <%s>", eVal, val) } // Result-Code - eVal = "900" // 15 mins of session + eVal = "300" // 5 mins of session if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { t.Error(err) @@ -317,3 +317,154 @@ func TestDiamItCCRInit(t *testing.T) { t.Errorf("expecting: %s, received: <%s>", eVal, val) } } + +func TestDiamItCCRUpdate(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(2)) + m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(2)) + 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, 57, 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(300))}}) + m.NewAVP(avp.UsedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(300))}}) + 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 = "300" // 5 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) + } +} + +func TestDiamItCCRTerminate(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(3)) + m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(3)) + 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, 15, 12, 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(0))}}) + m.NewAVP(avp.UsedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(250))}}) + 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) + } + time.Sleep(time.Duration(*waitRater) * time.Millisecond) + var cdrs []*engine.CDR + args := utils.RPCCDRsFilter{RunIDs: []string{utils.MetaRaw}} + if err := apierRpc.Call(utils.CdrsV1GetCDRs, 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 != 550*time.Second { + t.Errorf("Unexpected Usage CDR: %+v", cdrs[0]) + } + } + +} diff --git a/data/conf/samples/diamagent/voice.json b/data/conf/samples/diamagent/voice.json index 036da0570..bf085e906 100644 --- a/data/conf/samples/diamagent/voice.json +++ b/data/conf/samples/diamagent/voice.json @@ -19,7 +19,8 @@ "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": "*constant", "value": "15m"}, + {"tag": "Usage", "field_id": "Usage", "type": "*composed", + "value": "~*req.Requested-Service-Unit.CC-Time:s/(.*)/${1}s/", "mandatory": true}, {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "~*req.Subscription-Id.Subscription-Id-Data", "mandatory": true}, ], @@ -40,12 +41,16 @@ {"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": "*composed", + "value": "~*req.Requested-Service-Unit.CC-Time:s/(.*)/${1}s/", "mandatory": true}, + {"tag": "LastUsed", "field_id": "LastUsed", "type": "*composed", + "value": "~*req.Used-Service-Unit.CC-Time:s/(.*)/${1}s/", "mandatory": true}, {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "~*req.Subscription-Id.Subscription-Id-Data", "mandatory": true}, ], @@ -60,7 +65,7 @@ "id": "VoiceTerminate", "filters": ["*string:*vars.*cmd:CCR", "*string:*req.CC-Request-Type:3", "*prefix:*req.Service-Context-Id:voice"], - "flags": ["*terminate", "*accounts", "*attributes"], + "flags": ["*terminate", "*accounts", "*attributes", "*cdrs"], "request_fields":[ {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*voice"}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", @@ -71,7 +76,10 @@ "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": "*cc_usage", "mandatory": true, + "value": "~*req.CC-Request-Number;~*req.Used-Service-Unit.CC-Time:s/(.*)/${1}s/;5m"}, + {"tag": "LastUsed", "field_id": "LastUsed", "type": "*composed", + "value": "~*req.Used-Service-Unit.CC-Time:s/(.*)/${1}s/", "mandatory": true}, {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "~*req.Subscription-Id.Subscription-Id-Data", "mandatory": true}, ], From 079348c34db25d9737775d0460a388c08868c091 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 5 Oct 2018 13:07:21 +0200 Subject: [PATCH 51/52] Diameter tests for SMS, fixes #1200, fixes #821, fixes #686, fixes #535, fixes #359, fixes #354 --- agents/diam_it_test.go | 71 ++++++++++++++++++- data/conf/samples/diamagent/data.json | 12 ++-- data/conf/samples/diamagent/message.json | 11 ++- .../tariffplans/tutorial/DestinationRates.csv | 1 + data/tariffplans/tutorial/Rates.csv | 1 + data/tariffplans/tutorial/RatingPlans.csv | 3 +- data/tariffplans/tutorial/RatingProfiles.csv | 1 + 7 files changed, 83 insertions(+), 17 deletions(-) diff --git a/agents/diam_it_test.go b/agents/diam_it_test.go index 42c05bcc1..41ed380dc 100644 --- a/agents/diam_it_test.go +++ b/agents/diam_it_test.go @@ -73,14 +73,12 @@ func TestDiamItResetStorDb(t *testing.T) { } } -/* // Start CGR Engine func TestDiamItStartEngine(t *testing.T) { if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { t.Fatal(err) } } -*/ func TestDiamItConnectDiameterClient(t *testing.T) { diamClnt, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS", @@ -466,5 +464,72 @@ func TestDiamItCCRTerminate(t *testing.T) { t.Errorf("Unexpected Usage CDR: %+v", cdrs[0]) } } - +} + +func TestDiamItCCRSMS(t *testing.T) { + ccr := diam.NewRequest(diam.CreditControl, 4, nil) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("TestDmtAgentSendCCRSMS")) + ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) + ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) + ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("message@DiamItCCRSMS")) + ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) + ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2018, 10, 5, 11, 43, 10, 0, time.UTC))) + ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data + }}) + ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data + }}) + ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) + ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) + ccr.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(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number + diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP + }, + }), + diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information + AVP: []*diam.AVP{ + diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address + AVP: []*diam.AVP{ + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1001")), // Address-Data + }}), + diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address + AVP: []*diam.AVP{ + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data + }}), + }, + }), + }}) + if err := diamClnt.SendMessage(ccr); err != nil { + t.Error(err) + } + + time.Sleep(time.Duration(100) * time.Millisecond) + diamClnt.ReceivedMessage(rplyTimeout) + + var cdrs []*engine.CDR + args := utils.RPCCDRsFilter{RunIDs: []string{utils.MetaRaw}, ToRs: []string{utils.SMS}} + if err := apierRpc.Call(utils.CdrsV1GetCDRs, 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 != 1 { + t.Errorf("Unexpected Usage CDR: %+v", cdrs[0]) + } + } } diff --git a/data/conf/samples/diamagent/data.json b/data/conf/samples/diamagent/data.json index bc9d340cc..98a8ae7eb 100644 --- a/data/conf/samples/diamagent/data.json +++ b/data/conf/samples/diamagent/data.json @@ -25,8 +25,8 @@ ], "reply_fields": [ {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "filters": ["*string:*cgrep.MaxUsage:0"], - "field_id": "Result-Code", "type": "*constant", "value": "4010", "blocker": true}, + {"tag": "ResultCode", "filters": ["*rsr::~*cgrep.Error(!^$)"], + "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, ], }, @@ -58,8 +58,8 @@ ], "reply_fields": [ {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "filters": ["*string:*cgrep.MaxUsage:0"], - "field_id": "Result-Code", "type": "*constant", "value": "4010", "blocker": true}, + {"tag": "ResultCode", "filters": ["*rsr::~*cgrep.Error(!^$)"], + "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, ], }, @@ -90,8 +90,8 @@ ], "reply_fields": [ {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "filters": ["*string:*cgrep.MaxUsage:0"], - "field_id": "Result-Code", "type": "*constant", "value": "4010", "blocker": true}, + {"tag": "ResultCode", "filters": ["*rsr::~*cgrep.Error(!^$)"], + "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, ], }, diff --git a/data/conf/samples/diamagent/message.json b/data/conf/samples/diamagent/message.json index 86bd7e1ce..5c8eb0f5a 100644 --- a/data/conf/samples/diamagent/message.json +++ b/data/conf/samples/diamagent/message.json @@ -7,16 +7,15 @@ "id": "message", "filters": ["*string:*vars.*cmd:CCR", "*prefix:*req.Service-Context-Id:message", "*string:*req.CC-Request-Type:4"], - "flags": ["*event", "*accounts"], + "flags": ["*event", "*accounts", "*cdrs"], "request_fields":[ {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "~*req.Session-Id", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*constant", "value": "sms"}, {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid"}, {"tag": "Account", "field_id": "Account", "type": "*composed", "mandatory": true, "value": "~*req.Subscription-Id.Subscription-Id-Data[~Subscription-Id-Type(0)]"}, - {"tag": "Subject", "field_id": "Subject", "type": "*composed", "mandatory": true, - "value": "~*req.Subscription-Id.Subscription-Id-Data[~Subscription-Id-Type(0)]"}, {"tag": "Destination", "field_id": "Destination", "type": "*composed", "mandatory": true, "value": "~*req.Service-Information.SMS-Information.Recipient-Address.Address-Data"}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", @@ -27,10 +26,8 @@ "value": "~*req.Requested-Service-Unit.CC-Time", "mandatory": true}, ], "reply_fields":[ - {"tag": "ResultCode", "filters": ["*string.*cgrep.Error:ACCOUNT_NOT_FOUND"], - "field_id": "Result-Code", "type": "*constant", "value": "5030", "break": true}, - {"tag": "ResultCode", "filters": ["*string.*cgrep.Error:USER_NOT_FOUND"], - "field_id": "Result-Code", "type": "*constant", "value": "5030", "break": true}, + {"tag": "ResultCode", "filters": ["*rsr::~*cgrep.Error(!^$)"], + "field_id": "Result-Code", "type": "*constant", "value": "5030", "blocker": true}, ], }, diff --git a/data/tariffplans/tutorial/DestinationRates.csv b/data/tariffplans/tutorial/DestinationRates.csv index 1e7356366..d928fd878 100644 --- a/data/tariffplans/tutorial/DestinationRates.csv +++ b/data/tariffplans/tutorial/DestinationRates.csv @@ -3,4 +3,5 @@ DR_1001_20CNT,DST_1001,RT_20CNT,*up,4,0, DR_1002_20CNT,DST_1002,RT_20CNT,*up,4,0, DR_1003_MAXCOST_DISC,DST_1003,RT_1CNT_PER_SEC,*up,4,0.12,*disconnect DR_1001_10CNT,DST_1001,RT_10CNT,*up,4,0, +DR_SMS,*any,RT_SMS,*up,4,0, diff --git a/data/tariffplans/tutorial/Rates.csv b/data/tariffplans/tutorial/Rates.csv index c333bff3e..e735ee9aa 100644 --- a/data/tariffplans/tutorial/Rates.csv +++ b/data/tariffplans/tutorial/Rates.csv @@ -7,3 +7,4 @@ RT_40CNT,0.8,0.4,60s,30s,0s RT_40CNT,0,0.2,60s,10s,60s RT_1CNT,0,0.01,60s,60s,0s RT_1CNT_PER_SEC,0,0.01,1s,1s,0s +RT_SMS,0,0.01,1,1,0 diff --git a/data/tariffplans/tutorial/RatingPlans.csv b/data/tariffplans/tutorial/RatingPlans.csv index 4d0ec5a00..959af2ee4 100644 --- a/data/tariffplans/tutorial/RatingPlans.csv +++ b/data/tariffplans/tutorial/RatingPlans.csv @@ -3,4 +3,5 @@ RP_1001,DR_1002_20CNT,*any,10 RP_1001,DR_1003_MAXCOST_DISC,*any,10 RP_1002,DR_1001_20CNT,*any,10 RP_1002_LOW,DR_1001_10CNT,*any,10 -RP_1003,DR_1001_10CNT,*any,10 \ No newline at end of file +RP_1003,DR_1001_10CNT,*any,10 +RP_SMS,DR_SMS,*any,0 \ No newline at end of file diff --git a/data/tariffplans/tutorial/RatingProfiles.csv b/data/tariffplans/tutorial/RatingProfiles.csv index b828be471..bc6e99448 100644 --- a/data/tariffplans/tutorial/RatingProfiles.csv +++ b/data/tariffplans/tutorial/RatingProfiles.csv @@ -2,3 +2,4 @@ *out,cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_1001,, *out,cgrates.org,call,1002,2014-01-14T00:00:00Z,RP_1002,, *out,cgrates.org,call,1003,2014-01-14T00:00:00Z,RP_1003,, +*out,cgrates.org,sms,*any,2014-01-14T00:00:00Z,RP_SMS,, From 3fbab340de0b1ca6428c17404e4e02dc4b406216 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 5 Oct 2018 14:11:03 +0200 Subject: [PATCH 52/52] Corrections for NewAgentRequest constructor --- agents/diamagent.go | 9 +++++---- agents/httpagent.go | 1 - agents/libdiam.go | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/agents/diamagent.go b/agents/diamagent.go index 39e310a91..ef4b16e89 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -124,8 +124,9 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { lclProcessed, err = da.processRequest(reqProcessor, newAgentRequest( newDADataProvider(m), reqVars, rply, - reqProcessor.Tenant, - da.cgrCfg.DefaultTenant, da.filterS)) + reqProcessor.Tenant, da.cgrCfg.DefaultTenant, + utils.FirstNonEmpty(reqProcessor.Timezone, config.CgrConfig().DefaultTimezone), + da.filterS)) if lclProcessed { processed = lclProcessed } @@ -196,14 +197,14 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { func (da *DiameterAgent) processRequest(reqProcessor *config.DARequestProcessor, agReq *AgentRequest) (processed bool, err error) { - if pass, err := da.filterS.Pass(agReq.Tenant, + if pass, err := da.filterS.Pass(agReq.tenant, reqProcessor.Filters, agReq); err != nil || !pass { return pass, err } if agReq.CGRRequest, err = agReq.AsNavigableMap(reqProcessor.RequestFields); err != nil { return } - cgrEv := agReq.CGRRequest.AsCGREvent(agReq.Tenant, utils.NestingSep) + cgrEv := agReq.CGRRequest.AsCGREvent(agReq.tenant, utils.NestingSep) var reqType string for _, typ := range []string{ utils.MetaDryRun, utils.MetaAuth, diff --git a/agents/httpagent.go b/agents/httpagent.go index 5127afe11..ef273e5c5 100644 --- a/agents/httpagent.go +++ b/agents/httpagent.go @@ -58,7 +58,6 @@ func (ha *HTTPAgent) ServeHTTP(w http.ResponseWriter, req *http.Request) { utils.HTTPAgent, err.Error())) return } - var processed bool for _, reqProcessor := range ha.reqProcessors { agReq := newAgentRequest(dcdr, nil, nil, reqProcessor.Tenant, ha.dfltTenant, diff --git a/agents/libdiam.go b/agents/libdiam.go index 5f5cc403f..d2e9e5879 100644 --- a/agents/libdiam.go +++ b/agents/libdiam.go @@ -296,8 +296,7 @@ func (dP *diameterDP) FieldAsString(fldPath []string) (data string, err error) { if err != nil { return } - data, _ = utils.CastFieldIfToString(valIface) - return + return utils.IfaceAsString(valIface) } // FieldAsInterface is part of engine.DataProvider interface