From 200e419dcde2b6278a61578d7adf1257acfca371 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 5 Jun 2015 14:56:39 +0200 Subject: [PATCH] Adding Pdd inside StorDb and events --- engine/cdrs.go | 3 +- engine/event.go | 1 + engine/storedcdr.go | 35 ++++++++++++- engine/storedcdr_test.go | 34 +++++++------ sessionmanager/fsevent.go | 84 +++++++++++++++++++------------ sessionmanager/fsevent_test.go | 5 +- sessionmanager/kamevent.go | 18 ++++++- sessionmanager/kamevent_test.go | 6 ++- sessionmanager/osipsevent.go | 14 +++++- sessionmanager/osipsevent_test.go | 10 +++- utils/consts.go | 1 + 11 files changed, 156 insertions(+), 55 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index a7d34858d..497835f9f 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -273,8 +273,9 @@ func (self *CdrServer) deriveCdrs(storedCdr *StoredCdr) ([]*StoredCdr, error) { dcDurFld, _ := utils.NewRSRField(dc.UsageField) dcSupplFld, _ := utils.NewRSRField(dc.SupplierField) dcDCausseld, _ := utils.NewRSRField(dc.DisconnectCauseField) + dcPddFld, _ := utils.NewRSRField("0") // FixMe forkedCdr, err := storedCdr.ForkCdr(dc.RunId, dcReqTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, - dcSTimeFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true) + dcSTimeFld, dcATimeFld, dcDurFld, dcPddFld, dcSupplFld, dcDCausseld, []*utils.RSRField{}, true) if err != nil { Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", storedCdr.CgrId, dc.RunId, err.Error())) continue // do not add it to the forked CDR list diff --git a/engine/event.go b/engine/event.go index b88c82e91..c145593c8 100644 --- a/engine/event.go +++ b/engine/event.go @@ -40,6 +40,7 @@ type Event interface { GetAnswerTime(string) (time.Time, error) GetEndTime() (time.Time, error) GetDuration(string) (time.Duration, error) + GetPdd(string) (time.Duration, error) GetSupplier(string) string GetDisconnectCause(string) string GetOriginatorIP(string) string diff --git a/engine/storedcdr.go b/engine/storedcdr.go index 866c037d3..96dc77ce6 100644 --- a/engine/storedcdr.go +++ b/engine/storedcdr.go @@ -46,6 +46,9 @@ func NewStoredCdrFromExternalCdr(extCdr *ExternalCdr) (*StoredCdr, error) { if storedCdr.Usage, err = utils.ParseDurationWithSecs(extCdr.Usage); err != nil { return nil, err } + if storedCdr.Pdd, err = utils.ParseDurationWithSecs(extCdr.Pdd); err != nil { + return nil, err + } if len(extCdr.CostDetails) != 0 { if err = json.Unmarshal([]byte(extCdr.CostDetails), storedCdr.CostDetails); err != nil { return nil, err @@ -72,6 +75,7 @@ type StoredCdr struct { SetupTime time.Time // set-up time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. AnswerTime time.Time // answer time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. Usage time.Duration // event usage information (eg: in case of tor=*voice this will represent the total duration of a call) + Pdd time.Duration // PDD value Supplier string // Supplier information when available DisconnectCause string // Disconnect cause of the event ExtraFields map[string]string // Extra fields to be stored in CDR @@ -157,6 +161,8 @@ func (storedCdr *StoredCdr) FieldAsString(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(storedCdr.AnswerTime.Format(time.RFC3339)) case utils.USAGE: return strconv.FormatFloat(utils.Round(storedCdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64) + case utils.PDD: + return strconv.FormatFloat(utils.Round(storedCdr.Pdd.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64) case utils.SUPPLIER: return rsrFld.ParseValue(storedCdr.Supplier) case utils.DISCONNECT_CAUSE: @@ -220,6 +226,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values { v.Set(utils.SETUP_TIME, storedCdr.SetupTime.Format(time.RFC3339)) v.Set(utils.ANSWER_TIME, storedCdr.AnswerTime.Format(time.RFC3339)) v.Set(utils.USAGE, storedCdr.FormatUsage(utils.SECONDS)) + v.Set(utils.PDD, storedCdr.FieldAsString(&utils.RSRField{Id: utils.PDD})) v.Set(utils.SUPPLIER, storedCdr.Supplier) v.Set(utils.DISCONNECT_CAUSE, storedCdr.DisconnectCause) if storedCdr.CostDetails != nil { @@ -230,7 +237,7 @@ func (storedCdr *StoredCdr) AsHttpForm() url.Values { // Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, - answerTimeFld, durationFld, supplierFld, disconnectCauseFld *utils.RSRField, + answerTimeFld, durationFld, pddFld, supplierFld, disconnectCauseFld *utils.RSRField, extraFlds []*utils.RSRField, primaryMandatory bool) (*StoredCdr, error) { if reqTypeFld == nil { reqTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT) @@ -292,6 +299,12 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena if durationFld.Id == utils.META_DEFAULT { durationFld.Id = utils.USAGE } + if pddFld == nil { + pddFld, _ = utils.NewRSRField(utils.META_DEFAULT) + } + if pddFld.Id == utils.META_DEFAULT { + pddFld.Id = utils.PDD + } if supplierFld == nil { supplierFld, _ = utils.NewRSRField(utils.META_DEFAULT) } @@ -359,6 +372,12 @@ func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tena } else if frkStorCdr.Usage, err = utils.ParseDurationWithSecs(durStr); err != nil { return nil, err } + pddStr := storedCdr.FieldAsString(pddFld) + if primaryMandatory && len(pddStr) == 0 { + return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.PDD, pddFld.Id)) + } else if frkStorCdr.Pdd, err = utils.ParseDurationWithSecs(pddStr); err != nil { + return nil, err + } frkStorCdr.Supplier = storedCdr.FieldAsString(supplierFld) frkStorCdr.DisconnectCause = storedCdr.FieldAsString(disconnectCauseFld) frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds)) @@ -385,6 +404,7 @@ func (storedCdr *StoredCdr) AsExternalCdr() *ExternalCdr { SetupTime: storedCdr.SetupTime.Format(time.RFC3339), AnswerTime: storedCdr.AnswerTime.Format(time.RFC3339), Usage: storedCdr.FormatUsage(utils.SECONDS), + Pdd: storedCdr.FieldAsString(&utils.RSRField{Id: utils.PDD}), Supplier: storedCdr.Supplier, DisconnectCause: storedCdr.DisconnectCause, ExtraFields: storedCdr.ExtraFields, @@ -523,6 +543,18 @@ func (storedCdr *StoredCdr) GetDuration(fieldName string) (time.Duration, error) } return utils.ParseDurationWithSecs(durVal) } +func (storedCdr *StoredCdr) GetPdd(fieldName string) (time.Duration, error) { + if utils.IsSliceMember([]string{utils.PDD, utils.META_DEFAULT}, fieldName) { + return storedCdr.Pdd, nil + } + var pddVal string + if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + pddVal = fieldName[len(utils.STATIC_VALUE_PREFIX):] + } else { + pddVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName}) + } + return utils.ParseDurationWithSecs(pddVal) +} func (storedCdr *StoredCdr) GetSupplier(fieldName string) string { if utils.IsSliceMember([]string{utils.SUPPLIER, utils.META_DEFAULT}, fieldName) { return storedCdr.Supplier @@ -576,6 +608,7 @@ type ExternalCdr struct { SetupTime string AnswerTime string Usage string + Pdd string Supplier string DisconnectCause string ExtraFields map[string]string diff --git a/engine/storedcdr_test.go b/engine/storedcdr_test.go index bb58cfadb..6ec276849 100644 --- a/engine/storedcdr_test.go +++ b/engine/storedcdr_test.go @@ -37,13 +37,13 @@ func TestNewStoredCdrFromExternalCdr(t *testing.T) { AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", Supplier: "SUPPL1", SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", MediationRunId: utils.DEFAULT_RUNID, - Usage: "0.00000001", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true, + Usage: "0.00000001", Pdd: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true, } eStorCdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", Supplier: "SUPPL1", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, - Usage: time.Duration(10), ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true, + Usage: time.Duration(10), Pdd: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", Rated: true, } if storedCdr, err := NewStoredCdrFromExternalCdr(extCdr); err != nil { t.Error(err) @@ -57,7 +57,7 @@ func TestFieldAsString(t *testing.T) { CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: time.Duration(10) * time.Second, Pdd: time.Duration(5) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", } if cdr.FieldAsString(&utils.RSRField{Id: utils.CGRID}) != cdr.CgrId || @@ -75,6 +75,7 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&utils.RSRField{Id: utils.SETUP_TIME}) != cdr.SetupTime.Format(time.RFC3339) || cdr.FieldAsString(&utils.RSRField{Id: utils.ANSWER_TIME}) != cdr.AnswerTime.Format(time.RFC3339) || cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10" || + cdr.FieldAsString(&utils.RSRField{Id: utils.PDD}) != "5" || cdr.FieldAsString(&utils.RSRField{Id: utils.SUPPLIER}) != cdr.Supplier || cdr.FieldAsString(&utils.RSRField{Id: utils.MEDI_RUNID}) != cdr.MediationRunId || cdr.FieldAsString(&utils.RSRField{Id: utils.COST}) != "1.01" || @@ -99,6 +100,7 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&utils.RSRField{Id: utils.SETUP_TIME}) != cdr.SetupTime.Format(time.RFC3339), cdr.FieldAsString(&utils.RSRField{Id: utils.ANSWER_TIME}) != cdr.AnswerTime.Format(time.RFC3339), cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10", + cdr.FieldAsString(&utils.RSRField{Id: utils.PDD}) != "5", cdr.FieldAsString(&utils.RSRField{Id: utils.SUPPLIER}) != cdr.Supplier, cdr.FieldAsString(&utils.RSRField{Id: utils.MEDI_RUNID}) != cdr.MediationRunId, cdr.FieldAsString(&utils.RSRField{Id: utils.RATED_ACCOUNT}) != "dan", @@ -341,8 +343,9 @@ func TestStoredCdrForkCdr(t *testing.T) { Cost: 1.01, RatedSubject: "dans"} rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &utils.RSRField{Id: utils.REQTYPE}, &utils.RSRField{Id: utils.DIRECTION}, &utils.RSRField{Id: utils.TENANT}, &utils.RSRField{Id: utils.CATEGORY}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, - &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, &utils.RSRField{Id: utils.SUPPLIER}, - &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true) + &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, &utils.RSRField{Id: utils.PDD}, + &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, + []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true) if err != nil { t.Error("Unexpected error received", err) } @@ -374,22 +377,24 @@ func TestStoredCdrForkCdrStaticVals(t *testing.T) { rsrStDur, _ := utils.NewRSRField("^12s") rsrStSuppl, _ := utils.NewRSRField("^supplier1") rsrStDCause, _ := utils.NewRSRField("^HANGUP_COMPLETE") + rsrPdd, _ := utils.NewRSRField("^3") rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: "destination"}, - rsrStST, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true) + rsrStST, rsrStAT, rsrStDur, rsrPdd, rsrStSuppl, rsrStDCause, []*utils.RSRField{}, true) if err != nil { t.Error("Unexpected error received", err) } expctRatedCdr2 := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_POSTPAID, Direction: "*in", Tenant: "cgrates.com", Category: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002", SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), - AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(12) * time.Second, Supplier: "supplier1", DisconnectCause: "HANGUP_COMPLETE", + AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(12) * time.Second, Pdd: time.Duration(3) * time.Second, + Supplier: "supplier1", DisconnectCause: "HANGUP_COMPLETE", ExtraFields: map[string]string{}, MediationRunId: "wholesale_run", Cost: -1} if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2) } _, err = storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: "dummy_header"}, &utils.RSRField{Id: "direction"}, &utils.RSRField{Id: "tenant"}, &utils.RSRField{Id: "tor"}, &utils.RSRField{Id: "account"}, &utils.RSRField{Id: "subject"}, &utils.RSRField{Id: "destination"}, - &utils.RSRField{Id: "setup_time"}, &utils.RSRField{Id: "answer_time"}, &utils.RSRField{Id: "duration"}, &utils.RSRField{Id: utils.SUPPLIER}, + &utils.RSRField{Id: "setup_time"}, &utils.RSRField{Id: "answer_time"}, &utils.RSRField{Id: "duration"}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, []*utils.RSRField{}, true) if err == nil { t.Error("Failed to detect missing header") @@ -401,17 +406,18 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, Supplier: "SUPPL3", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, } expctCdr := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, Supplier: "SUPPL3", + Usage: time.Duration(10) * time.Second, Pdd: time.Duration(4) * time.Second, Supplier: "SUPPL3", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1} cdrOut, err := storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - &utils.RSRField{Id: utils.META_DEFAULT}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true) + &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true) if err != nil { t.Fatal("Unexpected error received", err) } @@ -420,7 +426,7 @@ func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) { t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut) } // Should also accept nil as defaults - if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true); err != nil { t.Fatal("Unexpected error received", err) } else if !reflect.DeepEqual(expctCdr, cdrOut) { @@ -433,14 +439,14 @@ func TestStoredCdrAsExternalCdr(t *testing.T) { AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID, - Usage: time.Duration(10), Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: time.Duration(10), Pdd: time.Duration(7) * time.Second, Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", } expectOutCdr := &ExternalCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", MediationRunId: utils.DEFAULT_RUNID, - Usage: "0.00000001", Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: "0.00000001", Pdd: "7", Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans", } if cdrOut := storCdr.AsExternalCdr(); !reflect.DeepEqual(expectOutCdr, cdrOut) { diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index 6b881e8ee..da11912bd 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -37,36 +37,39 @@ type FSEvent map[string]string const ( // Freswitch event proprities names - DIRECTION = "Call-Direction" - SUBJECT = "variable_cgr_subject" - ACCOUNT = "variable_cgr_account" - DESTINATION = "variable_cgr_destination" - REQTYPE = "variable_cgr_reqtype" //prepaid or postpaid - Category = "variable_cgr_category" - VAR_CGR_SUPPLIER = "variable_" + utils.CGR_SUPPLIER - UUID = "Unique-ID" // -Unique ID for this call leg - CSTMID = "variable_cgr_tenant" - CALL_DEST_NR = "Caller-Destination-Number" - SIP_REQ_USER = "variable_sip_req_user" - PARK_TIME = "Caller-Profile-Created-Time" - SETUP_TIME = "Caller-Channel-Created-Time" - ANSWER_TIME = "Caller-Channel-Answered-Time" - END_TIME = "Caller-Channel-Hangup-Time" - DURATION = "variable_billsec" - NAME = "Event-Name" - HEARTBEAT = "HEARTBEAT" - ANSWER = "CHANNEL_ANSWER" - HANGUP = "CHANNEL_HANGUP_COMPLETE" - PARK = "CHANNEL_PARK" - AUTH_OK = "+AUTH_OK" - DISCONNECT = "+SWITCH DISCONNECT" - INSUFFICIENT_FUNDS = "-INSUFFICIENT_FUNDS" - MISSING_PARAMETER = "-MISSING_PARAMETER" - SYSTEM_ERROR = "-SYSTEM_ERROR" - MANAGER_REQUEST = "+MANAGER_REQUEST" - USERNAME = "Caller-Username" - FS_IPv4 = "FreeSWITCH-IPv4" - HANGUP_CAUSE = "Hangup-Cause" + DIRECTION = "Call-Direction" + SUBJECT = "variable_cgr_subject" + ACCOUNT = "variable_cgr_account" + DESTINATION = "variable_cgr_destination" + REQTYPE = "variable_cgr_reqtype" //prepaid or postpaid + Category = "variable_cgr_category" + VAR_CGR_SUPPLIER = "variable_" + utils.CGR_SUPPLIER + UUID = "Unique-ID" // -Unique ID for this call leg + CSTMID = "variable_cgr_tenant" + CALL_DEST_NR = "Caller-Destination-Number" + SIP_REQ_USER = "variable_sip_req_user" + PARK_TIME = "Caller-Profile-Created-Time" + SETUP_TIME = "Caller-Channel-Created-Time" + ANSWER_TIME = "Caller-Channel-Answered-Time" + END_TIME = "Caller-Channel-Hangup-Time" + DURATION = "variable_billsec" + NAME = "Event-Name" + HEARTBEAT = "HEARTBEAT" + ANSWER = "CHANNEL_ANSWER" + HANGUP = "CHANNEL_HANGUP_COMPLETE" + PARK = "CHANNEL_PARK" + AUTH_OK = "+AUTH_OK" + DISCONNECT = "+SWITCH DISCONNECT" + INSUFFICIENT_FUNDS = "-INSUFFICIENT_FUNDS" + MISSING_PARAMETER = "-MISSING_PARAMETER" + SYSTEM_ERROR = "-SYSTEM_ERROR" + MANAGER_REQUEST = "+MANAGER_REQUEST" + USERNAME = "Caller-Username" + FS_IPv4 = "FreeSWITCH-IPv4" + HANGUP_CAUSE = "Hangup-Cause" + PDD_MEDIA_MS = "variable_progress_mediamsec" + PDD_NOMEDIA_MS = "variable_progressmsec" + VAR_CGR_DISCONNECT_CAUSE = "variable_" + utils.CGR_DISCONNECT_CAUSE ) @@ -206,7 +209,7 @@ func (fsev FSEvent) GetEndTime() (t time.Time, err error) { return utils.ParseTimeDetectLayout(fsev[END_TIME]) } -func (fsev FSEvent) GetDuration(fieldName string) (dur time.Duration, err error) { +func (fsev FSEvent) GetDuration(fieldName string) (time.Duration, error) { durStr := utils.FirstNonEmpty(fsev[fieldName], fsev[DURATION]) if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value durStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] @@ -214,6 +217,21 @@ func (fsev FSEvent) GetDuration(fieldName string) (dur time.Duration, err error) return utils.ParseDurationWithSecs(durStr) } +func (fsev FSEvent) GetPdd(fieldName string) (time.Duration, error) { + var pddStr string + if utils.IsSliceMember([]string{utils.PDD, utils.META_DEFAULT}, fieldName) { + pddStr = utils.FirstNonEmpty(fsev[PDD_MEDIA_MS], fsev[PDD_MEDIA_MS]) + if len(pddStr) != 0 { + pddStr = "0." + pddStr // PDD is in milliseconds and CGR expects it in seconds + } + } else if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + pddStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] + } else { + pddStr = fsev[fieldName] + } + return utils.ParseDurationWithSecs(pddStr) +} + func (fsev FSEvent) GetSupplier(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] @@ -281,6 +299,9 @@ func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField) string { case utils.USAGE: dur, _ := fsev.GetDuration("") return rsrFld.ParseValue(strconv.FormatInt(dur.Nanoseconds(), 10)) + case utils.PDD: + pdd, _ := fsev.GetPdd(utils.META_DEFAULT) + return rsrFld.ParseValue(strconv.FormatFloat(pdd.Seconds(), 'f', -1, 64)) case utils.SUPPLIER: return rsrFld.ParseValue(fsev.GetSupplier("")) case utils.DISCONNECT_CAUSE: @@ -332,6 +353,7 @@ func (fsev FSEvent) AsStoredCdr() *engine.StoredCdr { storCdr.SetupTime, _ = fsev.GetSetupTime(utils.META_DEFAULT) storCdr.AnswerTime, _ = fsev.GetAnswerTime(utils.META_DEFAULT) storCdr.Usage, _ = fsev.GetDuration(utils.META_DEFAULT) + storCdr.Pdd, _ = fsev.GetPdd(utils.META_DEFAULT) storCdr.ExtraFields = fsev.GetExtraFields() storCdr.Cost = -1 storCdr.Supplier = fsev.GetSupplier(utils.META_DEFAULT) diff --git a/sessionmanager/fsevent_test.go b/sessionmanager/fsevent_test.go index 52c193a90..6d880e9a0 100644 --- a/sessionmanager/fsevent_test.go +++ b/sessionmanager/fsevent_test.go @@ -566,6 +566,9 @@ func TestParseEventValue(t *testing.T) { if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.USAGE}); parsed != "5000000000" { t.Error("Unexpected result parsed", parsed) } + if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.PDD}); parsed != "0.28" { + t.Error("Unexpected result parsed", parsed) + } if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUPPLIER}); parsed != "supplier1" { t.Error("Unexpected result parsed", parsed) } @@ -639,7 +642,7 @@ func TestFsEvAsStoredCdr(t *testing.T) { TOR: utils.VOICE, AccId: "37e9b766-5256-4e4b-b1ed-3767b930fec8", CdrHost: "10.0.2.15", CdrSource: "FS_CHANNEL_HANGUP_COMPLETE", ReqType: utils.META_PSEUDOPREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002", SetupTime: setupTime, AnswerTime: aTime, - Usage: time.Duration(5) * time.Second, Supplier: "supplier1", DisconnectCause: "NORMAL_CLEARING", ExtraFields: make(map[string]string), Cost: -1} + Usage: time.Duration(5) * time.Second, Pdd: time.Duration(280) * time.Millisecond, Supplier: "supplier1", DisconnectCause: "NORMAL_CLEARING", ExtraFields: make(map[string]string), Cost: -1} if storedCdr := ev.AsStoredCdr(); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 3ba62cfa6..c67362abe 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -43,6 +43,7 @@ const ( CGR_ANSWERTIME = "cgr_answertime" CGR_STOPTIME = "cgr_stoptime" CGR_DURATION = "cgr_duration" + CGR_PDD = "cgr_pdd" KAM_TR_INDEX = "tr_index" KAM_TR_LABEL = "tr_label" @@ -51,7 +52,7 @@ const ( ) var primaryFields = []string{EVENT, CALLID, FROM_TAG, HASH_ENTRY, HASH_ID, CGR_ACCOUNT, CGR_SUBJECT, CGR_DESTINATION, - CGR_CATEGORY, CGR_TENANT, CGR_REQTYPE, CGR_ANSWERTIME, CGR_SETUPTIME, CGR_STOPTIME, CGR_DURATION, utils.CGR_SUPPLIER, utils.CGR_DISCONNECT_CAUSE} + CGR_CATEGORY, CGR_TENANT, CGR_REQTYPE, CGR_ANSWERTIME, CGR_SETUPTIME, CGR_STOPTIME, CGR_DURATION, CGR_PDD, utils.CGR_SUPPLIER, utils.CGR_DISCONNECT_CAUSE} type KamAuthReply struct { Event string // Kamailio will use this to differentiate between requests and replies @@ -186,7 +187,17 @@ func (kev KamEvent) GetDuration(fieldName string) (time.Duration, error) { } return utils.ParseDurationWithSecs(durStr) } - +func (kev KamEvent) GetPdd(fieldName string) (time.Duration, error) { + var pddStr string + if utils.IsSliceMember([]string{utils.PDD, utils.META_DEFAULT}, fieldName) { + pddStr = kev[CGR_PDD] + } else if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + pddStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] + } else { + pddStr = kev[fieldName] + } + return utils.ParseDurationWithSecs(pddStr) +} func (kev KamEvent) GetSupplier(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] @@ -292,6 +303,8 @@ func (kev KamEvent) ParseEventValue(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(aTime.String()) case utils.USAGE: return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)) + case utils.PDD: + return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)) case utils.SUPPLIER: return rsrFld.ParseValue(kev.GetSupplier(utils.META_DEFAULT)) case utils.DISCONNECT_CAUSE: @@ -325,6 +338,7 @@ func (kev KamEvent) AsStoredCdr() *engine.StoredCdr { storCdr.SetupTime, _ = kev.GetSetupTime(utils.META_DEFAULT) storCdr.AnswerTime, _ = kev.GetAnswerTime(utils.META_DEFAULT) storCdr.Usage, _ = kev.GetDuration(utils.META_DEFAULT) + storCdr.Pdd, _ = kev.GetPdd(utils.META_DEFAULT) storCdr.Supplier = kev.GetSupplier(utils.META_DEFAULT) storCdr.DisconnectCause = kev.GetDisconnectCause(utils.META_DEFAULT) storCdr.ExtraFields = kev.GetExtraFields() diff --git a/sessionmanager/kamevent_test.go b/sessionmanager/kamevent_test.go index b06057e48..dbfdd908f 100644 --- a/sessionmanager/kamevent_test.go +++ b/sessionmanager/kamevent_test.go @@ -47,9 +47,11 @@ func TestNewKamEvent(t *testing.T) { "cgr_answertime":"1419839310", "cgr_duration":"3", "cgr_supplier":"supplier2", - "cgr_disconnectcause": "200"}` + "cgr_disconnectcause": "200", + "cgr_pdd": "4"}` eKamEv := KamEvent{"event": "CGR_CALL_END", "callid": "46c01a5c249b469e76333fc6bfa87f6a@0:0:0:0:0:0:0:0", "from_tag": "bf71ad59", "to_tag": "7351fecf", - "cgr_reqtype": utils.META_POSTPAID, "cgr_account": "1001", "cgr_destination": "1002", "cgr_answertime": "1419839310", "cgr_duration": "3", utils.CGR_SUPPLIER: "supplier2", + "cgr_reqtype": utils.META_POSTPAID, "cgr_account": "1001", "cgr_destination": "1002", "cgr_answertime": "1419839310", "cgr_duration": "3", CGR_PDD: "4", + utils.CGR_SUPPLIER: "supplier2", utils.CGR_DISCONNECT_CAUSE: "200"} if kamEv, err := NewKamEvent([]byte(evStr)); err != nil { t.Error(err) diff --git a/sessionmanager/osipsevent.go b/sessionmanager/osipsevent.go index e5352dfcb..63f8700db 100644 --- a/sessionmanager/osipsevent.go +++ b/sessionmanager/osipsevent.go @@ -175,6 +175,17 @@ func (osipsev *OsipsEvent) GetDuration(fieldName string) (time.Duration, error) } return utils.ParseDurationWithSecs(durStr) } +func (osipsev *OsipsEvent) GetPdd(fieldName string) (time.Duration, error) { + var pddStr string + if utils.IsSliceMember([]string{utils.PDD, utils.META_DEFAULT}, fieldName) { + pddStr = osipsev.osipsEvent.AttrValues[CGR_PDD] + } else if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + pddStr = fieldName[len(utils.STATIC_VALUE_PREFIX):] + } else { + pddStr = osipsev.osipsEvent.AttrValues[fieldName] + } + return utils.ParseDurationWithSecs(pddStr) +} func (osipsev *OsipsEvent) GetSupplier(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] @@ -236,7 +247,7 @@ func (osipsev *OsipsEvent) PassesFieldFilter(*utils.RSRField) (bool, string) { } func (osipsev *OsipsEvent) GetExtraFields() map[string]string { primaryFields := []string{TO_TAG, SETUP_DURATION, OSIPS_SETUP_TIME, "method", "callid", "sip_reason", OSIPS_EVENT_TIME, "sip_code", "duration", "from_tag", "dialog_id", - CGR_TENANT, CGR_CATEGORY, CGR_REQTYPE, CGR_ACCOUNT, CGR_SUBJECT, CGR_DESTINATION, utils.CGR_SUPPLIER} + CGR_TENANT, CGR_CATEGORY, CGR_REQTYPE, CGR_ACCOUNT, CGR_SUBJECT, CGR_DESTINATION, utils.CGR_SUPPLIER, CGR_PDD} extraFields := make(map[string]string) for field, val := range osipsev.osipsEvent.AttrValues { if !utils.IsSliceMember(primaryFields, field) { @@ -267,6 +278,7 @@ func (osipsEv *OsipsEvent) AsStoredCdr() *engine.StoredCdr { storCdr.SetupTime, _ = osipsEv.GetSetupTime(utils.META_DEFAULT) storCdr.AnswerTime, _ = osipsEv.GetAnswerTime(utils.META_DEFAULT) storCdr.Usage, _ = osipsEv.GetDuration(utils.META_DEFAULT) + storCdr.Pdd, _ = osipsEv.GetPdd(utils.META_DEFAULT) storCdr.Supplier = osipsEv.GetSupplier(utils.META_DEFAULT) storCdr.DisconnectCause = osipsEv.GetDisconnectCause(utils.META_DEFAULT) storCdr.ExtraFields = osipsEv.GetExtraFields() diff --git a/sessionmanager/osipsevent_test.go b/sessionmanager/osipsevent_test.go index daa097752..351946317 100644 --- a/sessionmanager/osipsevent_test.go +++ b/sessionmanager/osipsevent_test.go @@ -34,7 +34,7 @@ var addr, _ = net.ResolveUDPAddr("udp", "172.16.254.77:42574") var osipsEv = &OsipsEvent{osipsEvent: &osipsdagram.OsipsEvent{Name: "E_ACC_CDR", AttrValues: map[string]string{"to_tag": "4ea9687f", "cgr_account": "dan", "setuptime": "7", "created": "1406370492", "method": "INVITE", "callid": "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", "sip_reason": "OK", "time": "1406370499", "cgr_reqtype": utils.META_PREPAID, "cgr_subject": "dan", "cgr_destination": "+4986517174963", "cgr_tenant": "itsyscom.com", "sip_code": "200", - "duration": "20", "from_tag": "eb082607", "extra1": "val1", "extra2": "val2", "cgr_supplier": "supplier3"}, OriginatorAddress: addr}} + "duration": "20", CGR_PDD: "3s", "from_tag": "eb082607", "extra1": "val1", "extra2": "val2", "cgr_supplier": "supplier3"}, OriginatorAddress: addr}} func TestOsipsEventInterface(t *testing.T) { var _ engine.Event = engine.Event(osipsEv) @@ -44,6 +44,7 @@ func TestOsipsEventParseStatic(t *testing.T) { setupTime, _ := osipsEv.GetSetupTime("^2013-12-07 08:42:24") answerTime, _ := osipsEv.GetAnswerTime("^2013-12-07 08:42:24") dur, _ := osipsEv.GetDuration("^60s") + pdd, _ := osipsEv.GetPdd("^10s") if osipsEv.GetReqType("^test") != "test" || osipsEv.GetDirection("^test") != "test" || osipsEv.GetTenant("^test") != "test" || @@ -54,6 +55,7 @@ func TestOsipsEventParseStatic(t *testing.T) { setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) || answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) || dur != time.Duration(60)*time.Second || + pdd != time.Duration(10)*time.Second || osipsEv.GetSupplier("^test") != "test" || osipsEv.GetDisconnectCause("^test") != "test" { t.Error("Values out of static not matching", @@ -67,6 +69,7 @@ func TestOsipsEventParseStatic(t *testing.T) { setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), dur != time.Duration(60)*time.Second, + pdd != time.Duration(10)*time.Second, osipsEv.GetSupplier("^test") != "test", osipsEv.GetDisconnectCause("^test") != "test") } @@ -80,6 +83,7 @@ func TestOsipsEventGetValues(t *testing.T) { answerTime, _ := osipsEv.GetAnswerTime(utils.META_DEFAULT) eAnswerTime, _ := utils.ParseTimeDetectLayout("1406370499") dur, _ := osipsEv.GetDuration(utils.META_DEFAULT) + pdd, _ := osipsEv.GetPdd(utils.META_DEFAULT) endTime, _ := osipsEv.GetEndTime() if osipsEv.GetName() != "E_ACC_CDR" || osipsEv.GetCgrId() != utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()) || @@ -96,6 +100,7 @@ func TestOsipsEventGetValues(t *testing.T) { !answerTime.Equal(eAnswerTime) || !endTime.Equal(eAnswerTime.Add(dur)) || dur != time.Duration(20*time.Second) || + pdd != time.Duration(3)*time.Second || osipsEv.GetSupplier(utils.META_DEFAULT) != "supplier3" || osipsEv.GetDisconnectCause(utils.META_DEFAULT) != "200" || osipsEv.GetOriginatorIP(utils.META_DEFAULT) != "172.16.254.77" { @@ -114,6 +119,7 @@ func TestOsipsEventGetValues(t *testing.T) { !answerTime.Equal(time.Date(2014, 7, 26, 12, 28, 19, 0, time.Local)), !endTime.Equal(time.Date(2014, 7, 26, 12, 28, 39, 0, time.Local)), dur != time.Duration(20*time.Second), + pdd != time.Duration(3)*time.Second, osipsEv.GetSupplier(utils.META_DEFAULT) != "supplier3", osipsEv.GetDisconnectCause(utils.META_DEFAULT) != "200", osipsEv.GetOriginatorIP(utils.META_DEFAULT) != "172.16.254.77", @@ -142,7 +148,7 @@ func TestOsipsEventAsStoredCdr(t *testing.T) { ReqType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "itsyscom.com", Category: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963", SetupTime: setupTime, AnswerTime: answerTime, - Usage: time.Duration(20) * time.Second, Supplier: "supplier3", DisconnectCause: "200", ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} + Usage: time.Duration(20) * time.Second, Pdd: time.Duration(3) * time.Second, Supplier: "supplier3", DisconnectCause: "200", ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} if storedCdr := osipsEv.AsStoredCdr(); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } diff --git a/utils/consts.go b/utils/consts.go index 2aa4571e6..18168dadf 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -106,6 +106,7 @@ const ( SETUP_TIME = "setup_time" ANSWER_TIME = "answer_time" USAGE = "usage" + PDD = "pdd" SUPPLIER = "supplier" MEDI_RUNID = "mediation_runid" RATED_ACCOUNT = "rated_account"