From d2332472d56c627e47c60c7e0a17fc098dfc4791 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 9 May 2016 13:39:12 +0200 Subject: [PATCH] CDRC XML support with Broadsoft CDRs in tests --- cdrc/cdrc.go | 6 +- cdrc/xml_it_test.go | 158 +++++++++++++++++++++++++ data/conf/samples/cdrcxml/cgrates.json | 44 +++++++ utils/consts.go | 1 + 4 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 cdrc/xml_it_test.go create mode 100644 data/conf/samples/cdrcxml/cgrates.json diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index 2b35dd828..7cc2523a2 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -181,6 +181,10 @@ func (self *Cdrc) processFile(filePath string) error { self.httpSkipTlsCheck, self.partialRecordsCache) case utils.FWV: recordsProcessor = NewFwvRecordsProcessor(file, self.dfltCdrcCfg, self.cdrcCfgs, self.httpClient, self.httpSkipTlsCheck, self.timezone) + case utils.XML: + if recordsProcessor, err = NewXMLRecordsProcessor(file, self.dfltCdrcCfg.CDRPath, self.timezone, self.httpSkipTlsCheck, self.cdrcCfgs); err != nil { + return err + } default: return fmt.Errorf("Unsupported CDR format: %s", self.dfltCdrcCfg.CdrFormat) } @@ -202,7 +206,7 @@ func (self *Cdrc) processFile(filePath string) error { utils.Logger.Info(fmt.Sprintf(" DryRun CDR: %+v", storedCdr)) continue } - if err := self.cdrs.Call("Responder.ProcessCdr", storedCdr, &reply); err != nil { + if err := self.cdrs.Call("CdrsV1.ProcessCdr", storedCdr, &reply); err != nil { utils.Logger.Err(fmt.Sprintf(" Failed sending CDR, %+v, error: %s", storedCdr, err.Error())) } else if reply != "OK" { utils.Logger.Err(fmt.Sprintf(" Received unexpected reply for CDR, %+v, reply: %s", storedCdr, reply)) diff --git a/cdrc/xml_it_test.go b/cdrc/xml_it_test.go new file mode 100644 index 000000000..0c83a9dc3 --- /dev/null +++ b/cdrc/xml_it_test.go @@ -0,0 +1,158 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +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 cdrc + +import ( + "io/ioutil" + "net/rpc" + "net/rpc/jsonrpc" + "os" + "path" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var xmlCfgPath string +var xmlCfg *config.CGRConfig +var cdrcXmlCfgs []*config.CdrcConfig +var cdrcXmlCfg *config.CdrcConfig +var cdrcXmlRPC *rpc.Client +var xmlPathIn1, xmlPathOut1 string + +func TestXmlITInitConfig(t *testing.T) { + if !*testIT { + return + } + var err error + xmlCfgPath = path.Join(*dataDir, "conf", "samples", "cdrcxml") + if xmlCfg, err = config.NewCGRConfigFromFolder(xmlCfgPath); err != nil { + t.Fatal("Got config error: ", err.Error()) + } +} + +// InitDb so we can rely on count +func TestXmlITInitCdrDb(t *testing.T) { + if !*testIT { + return + } + if err := engine.InitStorDb(xmlCfg); err != nil { + t.Fatal(err) + } +} + +func TestXmlITCreateCdrDirs(t *testing.T) { + if !*testIT { + return + } + for _, cdrcProfiles := range xmlCfg.CdrcProfiles { + for i, cdrcInst := range cdrcProfiles { + for _, dir := range []string{cdrcInst.CdrInDir, cdrcInst.CdrOutDir} { + if err := os.RemoveAll(dir); err != nil { + t.Fatal("Error removing folder: ", dir, err) + } + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal("Error creating folder: ", dir, err) + } + } + if i == 0 { // Initialize the folders to check later + xmlPathIn1 = cdrcInst.CdrInDir + xmlPathOut1 = cdrcInst.CdrOutDir + } + } + } +} + +func TestXmlITStartEngine(t *testing.T) { + if !*testIT { + return + } + if _, err := engine.StopStartEngine(xmlCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func TestXmlITRpcConn(t *testing.T) { + if !*testIT { + return + } + var err error + cdrcXmlRPC, err = jsonrpc.Dial("tcp", xmlCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal("Could not connect to rater: ", err.Error()) + } +} + +// The default scenario, out of cdrc defined in .cfg file +func TestXmlITHandleCdr1File(t *testing.T) { + if !*testIT { + return + } + fileName := "file1.xml" + tmpFilePath := path.Join("/tmp", fileName) + if err := ioutil.WriteFile(tmpFilePath, []byte(cdrXmlBroadsoft), 0644); err != nil { + t.Fatal(err.Error) + } + if err := os.Rename(tmpFilePath, path.Join(xmlPathIn1, fileName)); err != nil { + t.Fatal("Error moving file to processing directory: ", err) + } +} + +func TestXmlITProcessedFiles(t *testing.T) { + if !*testIT { + return + } + time.Sleep(time.Duration(2**waitRater) * time.Millisecond) + if outContent1, err := ioutil.ReadFile(path.Join(xmlPathOut1, "file1.xml")); err != nil { + t.Error(err) + } else if cdrXmlBroadsoft != string(outContent1) { + t.Errorf("Expecting: %q, received: %q", cdrXmlBroadsoft, string(outContent1)) + } +} + +func TestXmlITAnalyseCDRs(t *testing.T) { + if !*testIT { + return + } + var reply []*engine.ExternalCDR + if err := cdrcXmlRPC.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{}, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 2 { + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } + if err := cdrcXmlRPC.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{DestinationPrefixes: []string{"+4986517174963"}}, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } + +} + +func TestXmlITKillEngine(t *testing.T) { + if !*testIT { + return + } + if err := engine.KillEngine(*waitRater); err != nil { + t.Error(err) + } +} diff --git a/data/conf/samples/cdrcxml/cgrates.json b/data/conf/samples/cdrcxml/cgrates.json new file mode 100644 index 000000000..4723b19c6 --- /dev/null +++ b/data/conf/samples/cdrcxml/cgrates.json @@ -0,0 +1,44 @@ +{ + +// Real-time Charging System for Telecom & ISP environments +// Copyright (C) ITsysCOM GmbH + + + "rals": { + "enabled": true + }, + + "cdrs": { + "enabled": true, + "rals_conns": [], +}, + + + "cdrc": [ + { + "id": "XMLit1", + "enabled": true, + "cdr_format": "xml", + "cdr_in_dir": "/tmp/cdrctests/xmlit1/in", + "cdr_out_dir": "/tmp/cdrctests/xmlit1/out", + "cdr_path": "broadWorksCDR>cdrData", + "cdr_source_id": "xmlit1", + "cdr_filter": "broadWorksCDR>cdrData>headerModule>type(Normal)", + "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "broadWorksCDR>cdrData>basicModule>localCallId", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*rated", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "~broadWorksCDR>cdrData>basicModule>userId:s/.*@(.*)/${1}/", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "broadWorksCDR>cdrData>basicModule>userNumber", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "broadWorksCDR>cdrData>basicModule>calledNumber", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "broadWorksCDR>cdrData>basicModule>startTime", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "broadWorksCDR>cdrData>basicModule>answerTime", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*substract_usage", "value": "broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", "mandatory": true}, + ], + }, +], + + +} \ No newline at end of file diff --git a/utils/consts.go b/utils/consts.go index 80bf5747a..087194491 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -286,6 +286,7 @@ const ( SessionTTLLastUsed = "SessionTTLLastUsed" SessionTTLUsage = "SessionTTLUsage" HandlerSubstractUsage = "*substract_usage" + XML = "xml" ) var (