From b418cd5303cecc6276bfa2777689ee77538db1d5 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 18 Sep 2018 18:39:32 +0200 Subject: [PATCH] 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") - } -}