Adding CGR-CDR handler

This commit is contained in:
DanB
2013-11-07 12:35:30 +01:00
parent bb7b144f84
commit 3eda5495ea
5 changed files with 212 additions and 218 deletions

View File

@@ -45,16 +45,15 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) {
}
}()
} else {
engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %v", err))
engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %s", err.Error()))
}
}
func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
if genCdr, err := new(GenCdr).New(body); err == nil {
storage.SetCdr(genCdr)
if cgrCdr, err := NewCgrCdrFromHttpReq(r); err == nil {
storage.SetCdr(cgrCdr)
if cfg.CDRSMediator == "internal" {
errMedi := medi.MediateDBCDR(genCdr, storage)
errMedi := medi.MediateDBCDR(cgrCdr, storage)
if errMedi != nil {
engine.Logger.Err(fmt.Sprintf("Could not run mediation on CDR: %s", errMedi.Error()))
}
@@ -62,7 +61,7 @@ func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
//TODO: use the connection to mediator
}
} else {
engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %v", err))
engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %s", err.Error()))
}
}
@@ -76,7 +75,7 @@ func New(s engine.CdrStorage, m *mediator.Mediator, c *config.CGRConfig) *CDRS {
}
func (cdrs *CDRS) StartCapturingCDRs() {
http.HandleFunc("/cgr_json", cgrCdrHandler) // Attach CGR CDR Handler
http.HandleFunc("/cgr", cgrCdrHandler) // Attach CGR CDR Handler
http.HandleFunc("/freeswitch_json", fsCdrHandler) // Attach FreeSWITCH JSON CDR Handler
http.ListenAndServe(cfg.CDRSListen, nil)
}

121
cdrs/cgrcdr.go Normal file
View File

@@ -0,0 +1,121 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 <http://www.gnu.org/licenses/>
*/
package cdrs
import (
"github.com/cgrates/cgrates/utils"
"strconv"
"time"
"net/http"
)
const (
ACCID = "accid"
CDRHOST = "cdrhost"
REQTYPE = "reqtype"
DIRECTION = "direction"
TENANT = "tenant"
TOR = "tor"
ACCOUNT = "account"
SUBJECT = "subject"
DESTINATION = "destination"
TIME_ANSWER = "time_answer"
DURATION = "duration"
)
var primaryFields []string = []string{ACCID, CDRHOST, REQTYPE, DIRECTION, TENANT, TOR, ACCOUNT, SUBJECT, DESTINATION, TIME_ANSWER, DURATION}
func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
if req.Form == nil {
if err := req.ParseForm(); err != nil {
return nil, err
}
}
cgrCdr := make(CgrCdr)
for k, vals := range req.Form {
cgrCdr[k] = vals[0] // We only support the first value for now, if more are provided it is considered remote's fault
}
return cgrCdr, nil
}
type CgrCdr map[string]string
func (cgrCdr CgrCdr) GetCgrId() string {
return utils.FSCgrId(cgrCdr[ACCID])
}
func (cgrCdr CgrCdr) GetAccId() string {
return cgrCdr[ACCID]
}
func (cgrCdr CgrCdr) GetCdrHost() string {
return cgrCdr[CDRHOST]
}
func (cgrCdr CgrCdr) GetDirection() string {
//TODO: implement direction
return "*out"
}
func (cgrCdr CgrCdr) GetOrigId() string {
return cgrCdr[CDRHOST]
}
func (cgrCdr CgrCdr) GetSubject() string {
return cgrCdr[SUBJECT]
}
func (cgrCdr CgrCdr) GetAccount() string {
return cgrCdr[ACCOUNT]
}
// Charging destination number
func (cgrCdr CgrCdr) GetDestination() string {
return cgrCdr[DESTINATION]
}
func (cgrCdr CgrCdr) GetTOR() string {
return cgrCdr[TOR]
}
func (cgrCdr CgrCdr) GetTenant() string {
return cgrCdr[TENANT]
}
func (cgrCdr CgrCdr) GetReqType() string {
return cgrCdr[REQTYPE]
}
func (cgrCdr CgrCdr) GetExtraFields() map[string]string {
extraFields := make(map[string]string)
for k, v := range cgrCdr {
if !utils.IsSliceMember(primaryFields, k) {
extraFields[k] = v
}
}
return extraFields
}
func (cgrCdr CgrCdr) GetFallbackSubj() string {
return cfg.DefaultSubject
}
func (cgrCdr CgrCdr) GetAnswerTime() (t time.Time, err error) {
st, err := strconv.ParseInt(cgrCdr[TIME_ANSWER], 0, 64)
t = time.Unix(st, 0)
return
}
// Extracts duration as considered by the telecom switch
func (cgrCdr CgrCdr) GetDuration() int64 {
dur, _ := strconv.ParseInt(cgrCdr[DURATION], 0, 64)
return dur
}

85
cdrs/cgrcdr_test.go Normal file
View File

@@ -0,0 +1,85 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 <http://www.gnu.org/licenses/>
*/
package cdrs
import (
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/cgrates/config"
"testing"
"time"
)
/*
curl --data "accid=asbfdsaf&cdrhost=192.168.1.1&reqtype=rated&direction=*out&tenant=cgrates.org&tor=call&account=1001&subject=1001&destination=1002&time_answer=1383813746&duration=10&field_extr1=val_extr1&fieldextr2=valextr2" http://ipbxdev:2022/cgr
*/
func TestCgrCdrFields(t *testing.T) {
cfg, _ = config.NewDefaultCGRConfig()
cgrCdr := CgrCdr{ "accid":"dsafdsaf", "cdrhost":"192.168.1.1", "reqtype":"rated", "direction":"*out", "tenant":"cgrates.org", "tor":"call",
"account":"1001", "subject":"1001", "destination":"1002", "time_answer":"1383813746", "duration":"10", "field_extr1":"val_extr1", "fieldextr2":"valextr2"}
if cgrCdr.GetCgrId() != utils.FSCgrId("dsafdsaf") {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetAccId() != "dsafdsaf" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetCdrHost() != "192.168.1.1" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetDirection() != "*out" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetSubject() != "1001" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetAccount() != "1001" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetDestination() != "1002" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetTOR() != "call" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetTenant() != "cgrates.org" {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetReqType() != utils.RATED {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetFallbackSubj() != "0" {
t.Error("Error parsing cdr: ", cgrCdr)
}
answerTime, _ := cgrCdr.GetAnswerTime()
expectedATime, _ := time.Parse(time.RFC3339, "2013-11-07T08:42:26Z")
if answerTime.UTC() != expectedATime {
t.Error("Error parsing cdr: ", cgrCdr)
}
if cgrCdr.GetDuration() != 10 {
t.Error("Error parsing cdr: ", cgrCdr)
}
extraFields := cgrCdr.GetExtraFields()
if len(extraFields) != 2 {
t.Error("Error parsing extra fields: ", extraFields)
}
if extraFields["field_extr1"] != "val_extr1" {
t.Error("Error parsing extra fields: ", extraFields)
}
}

View File

@@ -1,163 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 <http://www.gnu.org/licenses/>
*/
package cdrs
import (
"encoding/json"
"errors"
"github.com/cgrates/cgrates/utils"
"strconv"
"time"
)
const (
CDR_MAP = "variables"
DIRECTION = "direction"
ORIG_ID = "id"
SUBJECT = "subject"
ACCOUNT = "account"
DESTINATION = "destination"
REQTYPE = "reqtype" //prepaid or postpaid
TOR = "tor"
UUID = "uuid" // -Unique ID for this call leg
CSTMID = "tenant"
CALL_DEST_NR = "dialed_extension"
PARK_TIME = "start_epoch"
ANSWER_TIME = "time_answer"
HANGUP_TIME = "time_hangup"
DURATION = "duration"
USERNAME = "user_name"
IP = "sip_local_network_addr"
)
type GenCdr map[string]string
func (genCdr GenCdr) New(body []byte) (utils.CDR, error) {
genCdr = make(map[string]string)
var tmp map[string]interface{}
var err error
if err = json.Unmarshal(body, &tmp); err == nil {
if variables, ok := tmp[CDR_MAP]; ok {
if variables, ok := variables.(map[string]interface{}); ok {
for k, v := range variables {
genCdr[k] = v.(string)
}
}
return genCdr, nil
}
}
return nil, err
}
func (genCdr GenCdr) GetCgrId() string {
return utils.FSCgrId(genCdr[UUID])
}
func (genCdr GenCdr) GetAccId() string {
return genCdr[UUID]
}
func (genCdr GenCdr) GetCdrHost() string {
return genCdr[FS_IP]
}
func (genCdr GenCdr) GetDirection() string {
//TODO: implement direction
return "*out"
}
func (genCdr GenCdr) GetOrigId() string {
return genCdr[ORIG_ID]
}
func (genCdr GenCdr) GetSubject() string {
return utils.FirstNonEmpty(genCdr[SUBJECT], genCdr[USERNAME])
}
func (genCdr GenCdr) GetAccount() string {
return utils.FirstNonEmpty(genCdr[ACCOUNT], genCdr[USERNAME])
}
// Charging destination number
func (genCdr GenCdr) GetDestination() string {
return utils.FirstNonEmpty(genCdr[DESTINATION], genCdr[CALL_DEST_NR])
}
func (genCdr GenCdr) GetTOR() string {
return utils.FirstNonEmpty(genCdr[TOR], cfg.DefaultTOR)
}
func (genCdr GenCdr) GetTenant() string {
return utils.FirstNonEmpty(genCdr[CSTMID], cfg.DefaultTenant)
}
func (genCdr GenCdr) GetReqType() string {
return utils.FirstNonEmpty(genCdr[REQTYPE], cfg.DefaultReqType)
}
func (genCdr GenCdr) GetExtraFields() map[string]string {
extraFields := make(map[string]string, len(cfg.CDRSExtraFields))
for _, field := range cfg.CDRSExtraFields {
extraFields[field] = genCdr[field]
}
return extraFields
}
func (genCdr GenCdr) GetFallbackSubj() string {
return cfg.DefaultSubject
}
func (genCdr GenCdr) GetAnswerTime() (t time.Time, err error) {
st, err := strconv.ParseInt(genCdr[ANSWER_TIME], 0, 64)
t = time.Unix(0, st*1000)
return
}
func (genCdr GenCdr) GetHangupTime() (t time.Time, err error) {
st, err := strconv.ParseInt(genCdr[HANGUP_TIME], 0, 64)
t = time.Unix(0, st*1000)
return
}
// Extracts duration as considered by the telecom switch
func (genCdr GenCdr) GetDuration() int64 {
dur, _ := strconv.ParseInt(genCdr[DURATION], 0, 64)
return dur
}
func (genCdr GenCdr) Store() (result string, err error) {
result += genCdr.GetCgrId() + "|"
result += genCdr.GetAccId() + "|"
result += genCdr.GetCdrHost() + "|"
result += genCdr.GetDirection() + "|"
result += genCdr.GetOrigId() + "|"
result += genCdr.GetSubject() + "|"
result += genCdr.GetAccount() + "|"
result += genCdr.GetDestination() + "|"
result += genCdr.GetTOR() + "|"
result += genCdr.GetAccId() + "|"
result += genCdr.GetTenant() + "|"
result += genCdr.GetReqType() + "|"
st, err := genCdr.GetAnswerTime()
if err != nil {
return "", err
}
result += strconv.FormatInt(st.UnixNano(), 10) + "|"
et, err := genCdr.GetHangupTime()
if err != nil {
return "", err
}
result += strconv.FormatInt(et.UnixNano(), 10) + "|"
result += strconv.FormatInt(genCdr.GetDuration(), 10) + "|"
result += genCdr.GetFallbackSubj() + "|"
return
}
func (genCdr GenCdr) Restore(input string) error {
return errors.New("Not implemented")
}

View File

@@ -39,51 +39,3 @@ type CDR interface {
GetFallbackSubj() string
GetExtraFields() map[string]string //Stores extra CDR Fields
}
type GenericCdr map[string]string
func (gcdr GenericCdr) GetCgrId() string {
return ""
}
func (gcdr GenericCdr) GetAccId() string {
return ""
}
func (gcdr GenericCdr) GetCdrHost() string {
return ""
}
func (gcdr GenericCdr) GetDirection() string {
return ""
}
func (gcdr GenericCdr) GetOrigId() string {
return ""
}
func (gcdr GenericCdr) GetSubject() string {
return ""
}
func (gcdr GenericCdr) GetAccount() string {
return ""
}
func (gcdr GenericCdr) GetDestination() string {
return ""
}
func (gcdr GenericCdr) GetTOR() string {
return ""
}
func (gcdr GenericCdr) GetTenant() string {
return ""
}
func (gcdr GenericCdr) GetReqType() string {
return ""
}
func (gcdr GenericCdr) GetAnswerTime() (time.Time, error) {
return time.Now(), nil
}
func (gcdr GenericCdr) GetDuration() int64 {
return 0.0
}
func (gcdr GenericCdr) GetFallbackSubj() string {
return ""
}
func (gcdr GenericCdr) GetExtraFields() map[string]string {
return nil
}