From eece61187dc187976bf9d125d4ecc30207352365 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 26 Nov 2015 12:28:44 +0100 Subject: [PATCH] Diameter AsSMGenericEvent to support metahandlers as well as constant values --- agents/libdmt.go | 108 ++++++++++++++++++------------ cdre/cdrexporter.go | 6 +- cdre/libcdre.go | 55 --------------- cdre/libcdre_test.go | 93 ------------------------- engine/cdrs.go | 3 +- engine/handler_derivedcharging.go | 1 + engine/suretax.go | 1 - utils/coreutils.go | 52 ++++++++++++++ utils/utils_test.go | 93 +++++++++++++++++++++++++ 9 files changed, 216 insertions(+), 196 deletions(-) diff --git a/agents/libdmt.go b/agents/libdmt.go index 81ee70fda..f635c8b4b 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -49,6 +49,7 @@ func init() { const ( META_CCR_USAGE = "*ccr_usage" META_CCR_SMG_EVENT_NAME = "*ccr_smg_event_name" + DIAMETER_CCR = "DIAMETER_CCR" ) func loadDictionaries(dictsDir, componentId string) error { @@ -331,57 +332,78 @@ func avpValAsString(a *diam.AVP) string { return dataVal[startIdx+1 : endIdx] } -// Extracts data out of CCR into a SMGenericEvent based on the configured template -func (self *CCR) AsSMGenericEvent(tpl []*config.CfgCdrField) (sessionmanager.SMGenericEvent, error) { - outMap := make(map[string]string) // work with it so we can append values to keys - if evName, err := self.metaHandler(META_CCR_SMG_EVENT_NAME, ""); err != nil { - return nil, err - } else { - outMap[utils.EVENT_NAME] = evName - } - for _, fldTpl := range tpl { - var outVal string - for _, rsrTpl := range fldTpl.Value { - if rsrTpl.IsStatic() { - outVal += rsrTpl.ParseValue("") - } else { - hierarchyPath := strings.Split(rsrTpl.Id, utils.HIERARCHY_SEP) - hpIf := make([]interface{}, len(hierarchyPath)) - for i, val := range hierarchyPath { - hpIf[i] = val - } - matchingAvps, err := self.diamMessage.FindAVPsWithPath(hpIf) - if err != nil || len(matchingAvps) == 0 { - utils.Logger.Warning(fmt.Sprintf(" Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id)) - continue // Filter not matching - } - if matchingAvps[0].Data.Type() == diam.GroupedAVPType { - utils.Logger.Warning(fmt.Sprintf(" Value for field template with id: %s is matching a group AVP, not considering.", rsrTpl.Id)) - continue // Not convertible, ignore - } - outVal += avpValAsString(matchingAvps[0]) - } - } - if _, hasKey := outMap[fldTpl.FieldId]; !hasKey { - outMap[fldTpl.FieldId] = outVal - } else { // If already there, postpend - outMap[fldTpl.FieldId] += outVal - } - } - return sessionmanager.SMGenericEvent(utils.ConvertMapValStrIf(outMap)), nil -} - // Handler for meta functions func (self *CCR) metaHandler(tag, arg string) (string, error) { switch tag { - case META_CCR_SMG_EVENT_NAME: - return "", nil case META_CCR_USAGE: - return "", nil + usage := usageFromCCR(self.CCRequestType, self.CCRequestNumber, self.RequestedServiceUnit.CCTime, time.Duration(300)*time.Second) + return strconv.FormatFloat(usage.Seconds(), 'f', -1, 64), nil } return "", nil } +func (self *CCR) eventFieldValue(cfgFld *config.CfgCdrField) string { + var outVal string + for _, rsrTpl := range cfgFld.Value { + if rsrTpl.IsStatic() { + outVal += rsrTpl.ParseValue("") + } else { + hierarchyPath := strings.Split(rsrTpl.Id, utils.HIERARCHY_SEP) + hpIf := make([]interface{}, len(hierarchyPath)) + for i, val := range hierarchyPath { + hpIf[i] = val + } + matchingAvps, err := self.diamMessage.FindAVPsWithPath(hpIf) + if err != nil || len(matchingAvps) == 0 { + utils.Logger.Warning(fmt.Sprintf(" Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id)) + continue // Filter not matching + } + if matchingAvps[0].Data.Type() == diam.GroupedAVPType { + utils.Logger.Warning(fmt.Sprintf(" Value for field template with id: %s is matching a group AVP, ignoring.", rsrTpl.Id)) + continue // Not convertible, ignore + } + outVal += avpValAsString(matchingAvps[0]) + } + } + return outVal +} + +// Extracts data out of CCR into a SMGenericEvent based on the configured template +func (self *CCR) AsSMGenericEvent(cfgFlds []*config.CfgCdrField) (sessionmanager.SMGenericEvent, 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 { + var outVal string + var err error + switch cfgFld.Type { + case utils.FILLER: + outVal = cfgFld.Value.Id() + cfgFld.Padding = "right" + case utils.CONSTANT: + outVal = cfgFld.Value.Id() + case utils.METATAG: + + outVal, err = self.metaHandler(cfgFld.MetatagId, cfgFld.Layout) + if err != nil { + utils.Logger.Warning(fmt.Sprintf(" Ignoring processing of metafunction: %s, error: %s", cfgFld.MetatagId, err.Error())) + } + case utils.CDRFIELD: + outVal = self.eventFieldValue(cfgFld) + } + fmtOut := outVal + if fmtOut, err = utils.FmtFieldWidth(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 nil, err + } + if _, hasKey := outMap[cfgFld.FieldId]; !hasKey { + outMap[cfgFld.FieldId] = fmtOut + } else { // If already there, postpend + outMap[cfgFld.FieldId] += fmtOut + } + } + return sessionmanager.SMGenericEvent(utils.ConvertMapValStrIf(outMap)), nil +} + func NewCCAFromCCR(ccr *CCR) *CCA { return &CCA{SessionId: ccr.SessionId, AuthApplicationId: ccr.AuthApplicationId, CCRequestType: ccr.CCRequestType, CCRequestNumber: ccr.CCRequestNumber, diamMessage: diam.NewMessage(ccr.diamMessage.Header.CommandCode, ccr.diamMessage.Header.CommandFlags&^diam.RequestFlag, ccr.diamMessage.Header.ApplicationID, diff --git a/cdre/cdrexporter.go b/cdre/cdrexporter.go index 8c28304b8..eb5c01ce4 100644 --- a/cdre/cdrexporter.go +++ b/cdre/cdrexporter.go @@ -271,7 +271,7 @@ func (cdre *CdrExporter) composeHeader() error { return err } fmtOut := outVal - if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { + if fmtOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { utils.Logger.Err(fmt.Sprintf(" Cannot export CDR header, field %s, error: %s", cfgFld.Tag, err.Error())) return err } @@ -300,7 +300,7 @@ func (cdre *CdrExporter) composeTrailer() error { return err } fmtOut := outVal - if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { + if fmtOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { utils.Logger.Err(fmt.Sprintf(" Cannot export CDR trailer, field: %s, error: %s", cfgFld.Tag, err.Error())) return err } @@ -366,7 +366,7 @@ func (cdre *CdrExporter) processCdr(cdr *engine.StoredCdr) error { return err } fmtOut := outVal - if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { + if fmtOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { utils.Logger.Err(fmt.Sprintf(" Cannot export CDR with cgrid: %s, runid: %s, fieldName: %s, fieldValue: %s, error: %s", cdr.CgrId, cdr.MediationRunId, cfgFld.Tag, outVal, err.Error())) return err } diff --git a/cdre/libcdre.go b/cdre/libcdre.go index f2ce17372..f2c52775a 100644 --- a/cdre/libcdre.go +++ b/cdre/libcdre.go @@ -19,64 +19,9 @@ along with this program. If not, see package cdre import ( - "errors" - "fmt" - "github.com/cgrates/cgrates/utils" ) -// Used as generic function logic for various fields - -// Attributes -// source - the base source -// width - the field width -// strip - if present it will specify the strip strategy, when missing strip will not be allowed -// padding - if present it will specify the padding strategy to use, left, right, zeroleft, zeroright -func FmtFieldWidth(source string, width int, strip, padding string, mandatory bool) (string, error) { - if mandatory && len(source) == 0 { - return "", errors.New("Empty source value") - } - if width == 0 { // Disable width processing if not defined - return source, nil - } - if len(source) == width { // the source is exactly the maximum length - return source, nil - } - if len(source) > width { //the source is bigger than allowed - if len(strip) == 0 { - return "", fmt.Errorf("Source %s is bigger than the width %d, no strip defied", source, width) - } - if strip == "right" { - return source[:width], nil - } else if strip == "xright" { - return source[:width-1] + "x", nil // Suffix with x to mark prefix - } else if strip == "left" { - diffIndx := len(source) - width - return source[diffIndx:], nil - } else if strip == "xleft" { // Prefix one x to mark stripping - diffIndx := len(source) - width - return "x" + source[diffIndx+1:], nil - } - } else { //the source is smaller as the maximum allowed - if len(padding) == 0 { - return "", fmt.Errorf("Source %s is smaller than the width %d, no padding defined", source, width) - } - var paddingFmt string - switch padding { - case "right": - paddingFmt = fmt.Sprintf("%%-%ds", width) - case "left": - paddingFmt = fmt.Sprintf("%%%ds", width) - case "zeroleft": - paddingFmt = fmt.Sprintf("%%0%ds", width) - } - if len(paddingFmt) != 0 { - return fmt.Sprintf(paddingFmt, source), nil - } - } - return source, nil -} - // Mask a number of characters in the suffix of the destination func MaskDestination(dest string, maskLen int) string { destLen := len(dest) diff --git a/cdre/libcdre_test.go b/cdre/libcdre_test.go index a7eb1bc85..2ceb6cfca 100644 --- a/cdre/libcdre_test.go +++ b/cdre/libcdre_test.go @@ -22,99 +22,6 @@ import ( "testing" ) -func TestMandatory(t *testing.T) { - _, err := FmtFieldWidth("", 0, "", "", true) - if err == nil { - t.Errorf("Failed to detect mandatory value") - } -} - -func TestMaxLen(t *testing.T) { - result, err := FmtFieldWidth("test", 4, "", "", false) - expected := "test" - if err != nil || result != expected { - t.Errorf("Expected \"test\" was \"%s\"", result) - } -} - -func TestRPadding(t *testing.T) { - result, err := FmtFieldWidth("test", 8, "", "right", false) - expected := "test " - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestPaddingFiller(t *testing.T) { - result, err := FmtFieldWidth("", 8, "", "right", false) - expected := " " - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestLPadding(t *testing.T) { - result, err := FmtFieldWidth("test", 8, "", "left", false) - expected := " test" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestZeroLPadding(t *testing.T) { - result, err := FmtFieldWidth("test", 8, "", "zeroleft", false) - expected := "0000test" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestRStrip(t *testing.T) { - result, err := FmtFieldWidth("test", 2, "right", "", false) - expected := "te" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestXRStrip(t *testing.T) { - result, err := FmtFieldWidth("test", 3, "xright", "", false) - expected := "tex" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestLStrip(t *testing.T) { - result, err := FmtFieldWidth("test", 2, "left", "", false) - expected := "st" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestXLStrip(t *testing.T) { - result, err := FmtFieldWidth("test", 3, "xleft", "", false) - expected := "xst" - if err != nil || result != expected { - t.Errorf("Expected \"%s \" was \"%s\"", expected, result) - } -} - -func TestStripNotAllowed(t *testing.T) { - _, err := FmtFieldWidth("test", 3, "", "", false) - if err == nil { - t.Error("Expected error") - } -} - -func TestPaddingNotAllowed(t *testing.T) { - _, err := FmtFieldWidth("test", 5, "", "", false) - if err == nil { - t.Error("Expected error") - } -} - func TestMaskDestination(t *testing.T) { dest := "+4986517174963" if destMasked := MaskDestination(dest, 3); destMasked != "+4986517174***" { diff --git a/engine/cdrs.go b/engine/cdrs.go index 99d47226c..bb91737d9 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -273,7 +273,7 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) { return cdrRuns, nil } attrsDC := &utils.AttrDerivedChargers{Tenant: storedCdr.Tenant, Category: storedCdr.Category, Direction: storedCdr.Direction, - Account: storedCdr.Account, Subject: storedCdr.Subject} + Account: storedCdr.Account, Subject: storedCdr.Subject, Destination: storedCdr.Destination} var dcs utils.DerivedChargers if err := self.rater.GetDerivedChargers(attrsDC, &dcs); err != nil { utils.Logger.Err(fmt.Sprintf("Could not get derived charging for cgrid %s, error: %s", storedCdr.CgrId, err.Error())) @@ -289,6 +289,7 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) { } } if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched + continue } dcReqTypeFld, _ := utils.NewRSRField(dc.ReqTypeField) diff --git a/engine/handler_derivedcharging.go b/engine/handler_derivedcharging.go index d5dc087f8..d008cb482 100644 --- a/engine/handler_derivedcharging.go +++ b/engine/handler_derivedcharging.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "fmt" "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" ) diff --git a/engine/suretax.go b/engine/suretax.go index f58758699..cbf7d3666 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -195,7 +195,6 @@ func SureTaxProcessCdr(cdr *StoredCdr) error { if err != nil { return err } - utils.Logger.Debug(fmt.Sprintf("NewSureTaxRequest: %s\n", string(jsnContent))) resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(jsnContent)) if err != nil { return err diff --git a/utils/coreutils.go b/utils/coreutils.go index 6a2286f0a..365047007 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -421,3 +421,55 @@ func Clone(a, b interface{}) error { } return nil } + +// Used as generic function logic for various fields + +// Attributes +// source - the base source +// width - the field width +// strip - if present it will specify the strip strategy, when missing strip will not be allowed +// padding - if present it will specify the padding strategy to use, left, right, zeroleft, zeroright +func FmtFieldWidth(source string, width int, strip, padding string, mandatory bool) (string, error) { + if mandatory && len(source) == 0 { + return "", errors.New("Empty source value") + } + if width == 0 { // Disable width processing if not defined + return source, nil + } + if len(source) == width { // the source is exactly the maximum length + return source, nil + } + if len(source) > width { //the source is bigger than allowed + if len(strip) == 0 { + return "", fmt.Errorf("Source %s is bigger than the width %d, no strip defied", source, width) + } + if strip == "right" { + return source[:width], nil + } else if strip == "xright" { + return source[:width-1] + "x", nil // Suffix with x to mark prefix + } else if strip == "left" { + diffIndx := len(source) - width + return source[diffIndx:], nil + } else if strip == "xleft" { // Prefix one x to mark stripping + diffIndx := len(source) - width + return "x" + source[diffIndx+1:], nil + } + } else { //the source is smaller as the maximum allowed + if len(padding) == 0 { + return "", fmt.Errorf("Source %s is smaller than the width %d, no padding defined", source, width) + } + var paddingFmt string + switch padding { + case "right": + paddingFmt = fmt.Sprintf("%%-%ds", width) + case "left": + paddingFmt = fmt.Sprintf("%%%ds", width) + case "zeroleft": + paddingFmt = fmt.Sprintf("%%0%ds", width) + } + if len(paddingFmt) != 0 { + return fmt.Sprintf(paddingFmt, source), nil + } + } + return source, nil +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 8d6dcc657..1f5951c07 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -486,3 +486,96 @@ func TestConvertIfaceToString(t *testing.T) { t.Error(resVal, converted) } } + +func TestMandatory(t *testing.T) { + _, err := FmtFieldWidth("", 0, "", "", true) + if err == nil { + t.Errorf("Failed to detect mandatory value") + } +} + +func TestMaxLen(t *testing.T) { + result, err := FmtFieldWidth("test", 4, "", "", false) + expected := "test" + if err != nil || result != expected { + t.Errorf("Expected \"test\" was \"%s\"", result) + } +} + +func TestRPadding(t *testing.T) { + result, err := FmtFieldWidth("test", 8, "", "right", false) + expected := "test " + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestPaddingFiller(t *testing.T) { + result, err := FmtFieldWidth("", 8, "", "right", false) + expected := " " + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestLPadding(t *testing.T) { + result, err := FmtFieldWidth("test", 8, "", "left", false) + expected := " test" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestZeroLPadding(t *testing.T) { + result, err := FmtFieldWidth("test", 8, "", "zeroleft", false) + expected := "0000test" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestRStrip(t *testing.T) { + result, err := FmtFieldWidth("test", 2, "right", "", false) + expected := "te" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestXRStrip(t *testing.T) { + result, err := FmtFieldWidth("test", 3, "xright", "", false) + expected := "tex" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestLStrip(t *testing.T) { + result, err := FmtFieldWidth("test", 2, "left", "", false) + expected := "st" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestXLStrip(t *testing.T) { + result, err := FmtFieldWidth("test", 3, "xleft", "", false) + expected := "xst" + if err != nil || result != expected { + t.Errorf("Expected \"%s \" was \"%s\"", expected, result) + } +} + +func TestStripNotAllowed(t *testing.T) { + _, err := FmtFieldWidth("test", 3, "", "", false) + if err == nil { + t.Error("Expected error") + } +} + +func TestPaddingNotAllowed(t *testing.T) { + _, err := FmtFieldWidth("test", 5, "", "", false) + if err == nil { + t.Error("Expected error") + } +}