func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor) *CCA { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter); !passes { passesAllFilters = false } } if !passesAllFilters { // Not going with this processor further return nil } if reqProcessor.DryRun { // DryRun should log the matching processor as well as the received CCR utils.Logger.Info(fmt.Sprintf("<DiameterAgent> RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf("<DiameterAgent> CCR message: %s", ccr.diamMessage)) } cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) smgEv, err := ccr.AsSMGenericEvent(reqProcessor.CCRFields) if err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) return nil } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) return cca } if reqProcessor.PublishEvent && self.pubsubs != nil { evt, err := smgEv.AsMapStringString() if err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) return nil } var reply string if err := self.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) return nil } } var maxUsage float64 if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf("<DiameterAgent> SMGenericEvent: %+v", smgEv)) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.LimitedSuccess), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } } else { // Find out maxUsage over APIs switch ccr.CCRequestType { case 1: err = self.smg.Call("SMGenericV1.SessionStart", smgEv, &maxUsage) case 2: err = self.smg.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage) case 3, 4: var rpl string if ccr.CCRequestType == 3 { err = self.smg.Call("SMGenericV1.SessionEnd", smgEv, &rpl) } else if ccr.CCRequestType == 4 { err = self.smg.Call("SMGenericV1.ChargeEvent", smgEv, &maxUsage) if maxUsage == 0 { smgEv[utils.USAGE] = 0 // For CDR not to debit } } if self.cgrCfg.DiameterAgentCfg().CreateCDR { if errCdr := self.smg.Call("SMGenericV1.ProcessCdr", smgEv, &rpl); errCdr != nil { err = errCdr } } } var populatedResultCode bool if err != nil { utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()) { // 5030 in case of AccountNotFound if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "5030", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } populatedResultCode = true } else if strings.HasSuffix(err.Error(), utils.ErrCreditInsufficient.Error()) { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } populatedResultCode = true } else { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v, API error: %s", ccr.diamMessage, err)) return cca } } if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 && !populatedResultCode { // Not enough balance, RFC demands 4012 if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } populatedResultCode = true } else if !populatedResultCode { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.Success), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } } if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && !populatedResultCode { // For terminate or previously marked unauthorized, we don't add granted-service-unit AVP if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Granted-Service-Unit", "CC-Time"}, strconv.FormatFloat(maxUsage, 'f', 0, 64), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Granted-Service-Unit, error: %s", ccr.diamMessage, err)) return nil } } } if err := cca.SetProcessorAVPs(reqProcessor, maxUsage); err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) return cca } return cca }
func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, processorVars map[string]string, cca *CCA) (bool, 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("<DiameterAgent> RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf("<DiameterAgent> CCR message: %s", ccr.diamMessage)) } if !reqProcessor.AppendCCA { *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } smgEv, err := ccr.AsSMGenericEvent(reqProcessor.CCRFields) if err != nil { *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { return false, err } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) return false, ErrDiameterRatingFailed } if len(reqProcessor.Flags) != 0 { smgEv[utils.CGRFlags] = reqProcessor.Flags.String() // Populate CGRFlags automatically } if reqProcessor.PublishEvent && self.pubsubs != nil { evt, err := smgEv.AsMapStringString() if err != nil { *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { return false, err } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) return false, ErrDiameterRatingFailed } var reply string if err := self.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { return false, err } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) return false, ErrDiameterRatingFailed } } var maxUsage float64 processorVars[CGRResultCode] = strconv.Itoa(diam.Success) processorVars[CGRError] = "" if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf("<DiameterAgent> SMGenericEvent: %+v", smgEv)) processorVars[CGRResultCode] = strconv.Itoa(diam.LimitedSuccess) } else { // Find out maxUsage over APIs switch ccr.CCRequestType { case 1: err = self.smg.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage) case 2: err = self.smg.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage) case 3, 4: // Handle them together since we generate CDR for them var rpl string if ccr.CCRequestType == 3 { err = self.smg.Call("SMGenericV1.TerminateSession", smgEv, &rpl) } else if ccr.CCRequestType == 4 { err = self.smg.Call("SMGenericV1.ChargeEvent", smgEv, &maxUsage) if maxUsage == 0 { smgEv[utils.USAGE] = 0 // For CDR not to debit } } if self.cgrCfg.DiameterAgentCfg().CreateCDR { if errCdr := self.smg.Call("SMGenericV1.ProcessCDR", smgEv, &rpl); errCdr != nil { err = errCdr } } } if err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Processing message: %+v, API error: %s", ccr.diamMessage, err)) switch { // Prettify some errors case strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()): processorVars[CGRError] = utils.ErrAccountNotFound.Error() case strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()): processorVars[CGRError] = utils.ErrUserNotFound.Error() case strings.HasSuffix(err.Error(), utils.ErrInsufficientCredit.Error()): processorVars[CGRError] = utils.ErrInsufficientCredit.Error() case strings.HasSuffix(err.Error(), utils.ErrAccountDisabled.Error()): processorVars[CGRError] = utils.ErrAccountDisabled.Error() case strings.HasSuffix(err.Error(), utils.ErrRatingPlanNotFound.Error()): processorVars[CGRError] = utils.ErrRatingPlanNotFound.Error() case strings.HasSuffix(err.Error(), utils.ErrUnauthorizedDestination.Error()): processorVars[CGRError] = utils.ErrUnauthorizedDestination.Error() default: // Unknown error processorVars[CGRError] = err.Error() processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed) } } if maxUsage < 0 { maxUsage = 0 } if prevMaxUsageStr, hasKey := processorVars[CGRMaxUsage]; hasKey { prevMaxUsage, _ := strconv.ParseFloat(prevMaxUsageStr, 64) if prevMaxUsage < maxUsage { maxUsage = prevMaxUsage } } processorVars[CGRMaxUsage] = strconv.FormatFloat(maxUsage, 'f', -1, 64) } if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { return false, err } if err := cca.SetProcessorAVPs(reqProcessor, processorVars); err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { return false, err } utils.Logger.Err(fmt.Sprintf("<DiameterAgent> CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) return false, ErrDiameterRatingFailed } return true, nil }