mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-20 22:58:44 +05:00
Merge branch 'master' into hapool
This commit is contained in:
@@ -486,7 +486,7 @@ func mailAsync(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) err
|
||||
message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on Balance: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nBalance:\r\n\t%s\r\n\r\nYours faithfully,\r\nCGR Balance Monitor\r\n", toAddrStr, ub.Id, time.Now(), balJsn))
|
||||
} else if sq != nil {
|
||||
message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on StatsQueueId: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nStatsQueueId:\r\n\t%s\r\n\r\nMetrics:\r\n\t%+v\r\n\r\nTrigger:\r\n\t%+v\r\n\r\nYours faithfully,\r\nCGR CDR Stats Monitor\r\n",
|
||||
toAddrStr, sq.Id, time.Now(), sq.Id, sq.metrics, sq.Trigger))
|
||||
toAddrStr, sq.Id, time.Now(), sq.Id, sq.Metrics, sq.Trigger))
|
||||
}
|
||||
auth := smtp.PlainAuth("", cgrCfg.MailerAuthUser, cgrCfg.MailerAuthPass, strings.Split(cgrCfg.MailerServer, ":")[0]) // We only need host part, so ignore port
|
||||
go func() {
|
||||
|
||||
@@ -349,22 +349,6 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// split on days
|
||||
/*for i := 0; i < len(timespans); i++ {
|
||||
if timespans[i].TimeStart.Day() != timespans[i].TimeEnd.Day() {
|
||||
//log.Print("TS: ", timespans[i].TimeStart, timespans[i].TimeEnd)
|
||||
start := timespans[i].TimeStart
|
||||
newTs := timespans[i].SplitByTime(time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location()).Add(24 * time.Hour))
|
||||
if newTs != nil {
|
||||
//log.Print("NEW TS: ", newTs.TimeStart, newTs.TimeEnd)
|
||||
// insert the new timespan
|
||||
index := i + 1
|
||||
timespans = append(timespans, nil)
|
||||
copy(timespans[index+1:], timespans[index:])
|
||||
timespans[index] = newTs
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// Logger.Debug(fmt.Sprintf("After SplitByRatingPlan: %+v", timespans))
|
||||
// split on rate intervals
|
||||
for i := 0; i < len(timespans); i++ {
|
||||
@@ -796,7 +780,7 @@ func (cd *CallDescriptor) GetLCRFromStorage() (*LCR, error) {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
|
||||
func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
func (cd *CallDescriptor) GetLCR(stats StatsInterface, p *utils.Paginator) (*LCRCost, error) {
|
||||
cd.account = nil // make sure it's not cached
|
||||
lcr, err := cd.GetLCRFromStorage()
|
||||
if err != nil {
|
||||
@@ -832,12 +816,13 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
lcrCD.Account = supplier
|
||||
lcrCD.Subject = supplier
|
||||
lcrCD.Category = lcrCost.Entry.RPCategory
|
||||
fullSupplier := utils.ConcatenatedKey(lcrCD.Direction, lcrCD.Tenant, lcrCD.Category, lcrCD.Subject)
|
||||
var cc *CallCost
|
||||
var err error
|
||||
if cd.account, err = accountingStorage.GetAccount(lcrCD.GetAccountKey()); err == nil {
|
||||
if cd.account.Disabled {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: fmt.Sprintf("supplier %s is disabled", supplier),
|
||||
})
|
||||
continue
|
||||
@@ -847,16 +832,15 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
cc, err = lcrCD.GetCost()
|
||||
|
||||
}
|
||||
supplier = utils.ConcatenatedKey(lcrCD.Direction, lcrCD.Tenant, lcrCD.Category, lcrCD.Subject)
|
||||
//log.Printf("CC: %+v", cc.Timespans[0].ratingInfo.RateIntervals[0].Rating.Rates[0])
|
||||
if err != nil || cc == nil {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Cost: cc.Cost,
|
||||
Duration: cc.GetDuration(),
|
||||
})
|
||||
@@ -879,6 +863,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
lcrCD.Category = category
|
||||
lcrCD.Account = supplier
|
||||
lcrCD.Subject = supplier
|
||||
fullSupplier := utils.ConcatenatedKey(lcrCD.Direction, lcrCD.Tenant, lcrCD.Category, lcrCD.Subject)
|
||||
var qosSortParams []string
|
||||
var asrValues sort.Float64Slice
|
||||
var pddValues sort.Float64Slice
|
||||
@@ -898,7 +883,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
if utils.IsSliceMember([]string{LCR_STRATEGY_QOS, LCR_STRATEGY_QOS_THRESHOLD, LCR_STRATEGY_LOAD}, lcrCost.Entry.Strategy) {
|
||||
if stats == nil {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: fmt.Sprintf("Cdr stats service not configured"),
|
||||
})
|
||||
continue
|
||||
@@ -906,7 +891,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
rpfKey := utils.ConcatenatedKey(ratingProfileSearchKey, supplier)
|
||||
if rpf, err := ratingStorage.GetRatingProfile(rpfKey, false); err != nil {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: fmt.Sprintf("Rating plan error: %s", err.Error()),
|
||||
})
|
||||
continue
|
||||
@@ -938,7 +923,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
statValues := make(map[string]float64)
|
||||
if err := stats.GetValues(qId, &statValues); err != nil {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: fmt.Sprintf("Get stats values for queue id %s, error %s", qId, err.Error()),
|
||||
})
|
||||
statsErr = true
|
||||
@@ -992,7 +977,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
if lcrCost.Entry.Strategy == LCR_STRATEGY_LOAD {
|
||||
if len(supplierQueues) > 0 {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
supplierQueues: supplierQueues,
|
||||
})
|
||||
}
|
||||
@@ -1072,7 +1057,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
//log.Print("ACCCOUNT")
|
||||
if cd.account.Disabled {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: fmt.Sprintf("supplier %s is disabled", supplier),
|
||||
})
|
||||
continue
|
||||
@@ -1083,16 +1068,15 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
cc, err = lcrCD.GetCost()
|
||||
}
|
||||
//log.Printf("CC: %+v", cc)
|
||||
supplier = utils.ConcatenatedKey(lcrCD.Direction, lcrCD.Tenant, lcrCD.Category, lcrCD.Subject)
|
||||
if err != nil || cc == nil {
|
||||
lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Error: err.Error(),
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
supplCost := &LCRSupplierCost{
|
||||
Supplier: supplier,
|
||||
Supplier: fullSupplier,
|
||||
Cost: cc.Cost,
|
||||
Duration: cc.GetDuration(),
|
||||
}
|
||||
@@ -1128,5 +1112,13 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
|
||||
// sort according to strategy
|
||||
lcrCost.Sort()
|
||||
}
|
||||
if p != nil {
|
||||
if p.Offset != nil && *p.Offset > 0 && *p.Offset < len(lcrCost.SupplierCosts) {
|
||||
lcrCost.SupplierCosts = lcrCost.SupplierCosts[*p.Offset:]
|
||||
}
|
||||
if p.Limit != nil && *p.Limit > 0 && *p.Limit < len(lcrCost.SupplierCosts) {
|
||||
lcrCost.SupplierCosts = lcrCost.SupplierCosts[:*p.Limit]
|
||||
}
|
||||
}
|
||||
return lcrCost, nil
|
||||
}
|
||||
|
||||
@@ -48,14 +48,16 @@ const (
|
||||
|
||||
// A request for LCR, used in APIer and SM where we need to expose it
|
||||
type LcrRequest struct {
|
||||
Direction string
|
||||
Tenant string
|
||||
Category string
|
||||
Account string
|
||||
Subject string
|
||||
Destination string
|
||||
StartTime string
|
||||
Duration string
|
||||
Direction string
|
||||
Tenant string
|
||||
Category string
|
||||
Account string
|
||||
Subject string
|
||||
Destination string
|
||||
SetupTime string
|
||||
Duration string
|
||||
IgnoreErrors bool
|
||||
*utils.Paginator
|
||||
}
|
||||
|
||||
func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) {
|
||||
@@ -77,9 +79,9 @@ func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) {
|
||||
}
|
||||
var timeStart time.Time
|
||||
var err error
|
||||
if len(self.StartTime) == 0 {
|
||||
if len(self.SetupTime) == 0 {
|
||||
timeStart = time.Now()
|
||||
} else if timeStart, err = utils.ParseTimeDetectLayout(self.StartTime); err != nil {
|
||||
} else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var callDur time.Duration
|
||||
@@ -433,6 +435,9 @@ func (lc *LCRCost) SuppliersSlice() ([]string, error) {
|
||||
}
|
||||
supps := []string{}
|
||||
for _, supplCost := range lc.SupplierCosts {
|
||||
if supplCost.Error != "" {
|
||||
continue // Do not add the supplier with cost errors to list of suppliers available
|
||||
}
|
||||
if dtcs, err := utils.NewDTCSFromRPKey(supplCost.Supplier); err != nil {
|
||||
return nil, err
|
||||
} else if len(dtcs.Subject) != 0 {
|
||||
|
||||
@@ -204,7 +204,7 @@ func TestLcrGet(t *testing.T) {
|
||||
Account: "rif",
|
||||
Subject: "rif",
|
||||
}
|
||||
lcr, err := cd.GetLCR(nil)
|
||||
lcr, err := cd.GetLCR(nil, nil)
|
||||
//jsn, _ := json.Marshal(lcr)
|
||||
//log.Print("LCR: ", string(jsn))
|
||||
if err != nil || lcr == nil {
|
||||
@@ -215,11 +215,11 @@ func TestLcrGet(t *testing.T) {
|
||||
func TestLcrRequestAsCallDescriptor(t *testing.T) {
|
||||
sTime := time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC)
|
||||
callDur := time.Duration(1) * time.Minute
|
||||
lcrReq := &LcrRequest{Account: "2001", StartTime: sTime.String()}
|
||||
lcrReq := &LcrRequest{Account: "2001", SetupTime: sTime.String()}
|
||||
if _, err := lcrReq.AsCallDescriptor(); err == nil || err != utils.ErrMandatoryIeMissing {
|
||||
t.Error("Unexpected error received: %v", err)
|
||||
}
|
||||
lcrReq = &LcrRequest{Account: "2001", Destination: "2002", StartTime: sTime.String()}
|
||||
lcrReq = &LcrRequest{Account: "2001", Destination: "2002", SetupTime: sTime.String()}
|
||||
eCd := &CallDescriptor{
|
||||
Direction: utils.OUT,
|
||||
Tenant: config.CgrConfig().DefaultTenant,
|
||||
|
||||
@@ -41,6 +41,11 @@ type SessionRun struct {
|
||||
CallCosts []*CallCost
|
||||
}
|
||||
|
||||
type AttrGetLcr struct {
|
||||
*CallDescriptor
|
||||
*utils.Paginator
|
||||
}
|
||||
|
||||
type Responder struct {
|
||||
Bal *balancer2go.Balancer
|
||||
ExitChan chan bool
|
||||
@@ -386,20 +391,25 @@ func (rs *Responder) LogCallCost(ccl *CallCostLog, reply *string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *Responder) GetLCR(cd *CallDescriptor, reply *LCRCost) error {
|
||||
if cd.Subject == "" {
|
||||
cd.Subject = cd.Account
|
||||
func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error {
|
||||
if attrs.CallDescriptor.Subject == "" {
|
||||
attrs.CallDescriptor.Subject = attrs.CallDescriptor.Account
|
||||
}
|
||||
if upData, err := LoadUserProfile(cd, "ExtraFields"); err != nil {
|
||||
if upData, err := LoadUserProfile(attrs.CallDescriptor, "ExtraFields"); err != nil {
|
||||
return err
|
||||
} else {
|
||||
udRcv := upData.(*CallDescriptor)
|
||||
*cd = *udRcv
|
||||
*attrs.CallDescriptor = *udRcv
|
||||
}
|
||||
lcrCost, err := cd.GetLCR(rs.Stats)
|
||||
lcrCost, err := attrs.CallDescriptor.GetLCR(rs.Stats, attrs.Paginator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lcrCost.Entry.Strategy == LCR_STRATEGY_LOAD {
|
||||
for _, suppl := range lcrCost.SupplierCosts {
|
||||
suppl.Cost = -1 // In case of load distribution we don't calculate costs
|
||||
}
|
||||
}
|
||||
*reply = *lcrCost
|
||||
return nil
|
||||
}
|
||||
@@ -570,7 +580,7 @@ type Connector interface {
|
||||
GetSessionRuns(*StoredCdr, *[]*SessionRun) error
|
||||
ProcessCdr(*StoredCdr, *string) error
|
||||
LogCallCost(*CallCostLog, *string) error
|
||||
GetLCR(*CallDescriptor, *LCRCost) error
|
||||
GetLCR(*AttrGetLcr, *LCRCost) error
|
||||
GetTimeout() time.Duration
|
||||
}
|
||||
|
||||
@@ -619,8 +629,8 @@ func (rcc *RPCClientConnector) LogCallCost(ccl *CallCostLog, reply *string) erro
|
||||
return rcc.Client.Call("CDRSV1.LogCallCost", ccl, reply)
|
||||
}
|
||||
|
||||
func (rcc *RPCClientConnector) GetLCR(cd *CallDescriptor, reply *LCRCost) error {
|
||||
return rcc.Client.Call("Responder.GetLCR", cd, reply)
|
||||
func (rcc *RPCClientConnector) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error {
|
||||
return rcc.Client.Call("Responder.GetLCR", attrs, reply)
|
||||
}
|
||||
|
||||
func (rcc *RPCClientConnector) GetTimeout() time.Duration {
|
||||
|
||||
@@ -395,7 +395,7 @@ func TestGetLCR(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var lcr LCRCost
|
||||
if err := rsponder.GetLCR(cdStatic, &lcr); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdStatic}, &lcr); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry)
|
||||
@@ -422,7 +422,7 @@ func TestGetLCR(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var lcrLc LCRCost
|
||||
if err := rsponder.GetLCR(cdLowestCost, &lcrLc); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdLowestCost}, &lcrLc); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eLcLcr.Entry, lcrLc.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eLcLcr.Entry, lcrLc.Entry)
|
||||
@@ -447,7 +447,7 @@ func TestGetLCR(t *testing.T) {
|
||||
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second},
|
||||
},
|
||||
}
|
||||
if err := rsponder.GetLCR(cdLowestCost, &lcrLc); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdLowestCost}, &lcrLc); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eLcLcr.Entry, lcrLc.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eLcLcr.Entry, lcrLc.Entry)
|
||||
@@ -476,7 +476,7 @@ func TestGetLCR(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var lcrQT LCRCost
|
||||
if err := rsponder.GetLCR(cdQosThreshold, &lcrQT); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry)
|
||||
@@ -495,7 +495,7 @@ func TestGetLCR(t *testing.T) {
|
||||
&LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, ACD: 300, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{"35", "4m"}},
|
||||
},
|
||||
}
|
||||
if err := rsponder.GetLCR(cdQosThreshold, &lcrQT); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry)
|
||||
@@ -524,7 +524,7 @@ func TestGetLCR(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var lcrQ LCRCost
|
||||
if err := rsponder.GetLCR(cdQos, &lcrQ); err != nil {
|
||||
if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQos}, &lcrQ); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(eQosLcr.Entry, lcrQ.Entry) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eQosLcr.Entry, lcrQ.Entry)
|
||||
|
||||
@@ -215,12 +215,12 @@ func (sq *StatsQueue) GetId() string {
|
||||
|
||||
// Convert data into a struct which can be used in actions based on triggers hit
|
||||
func (sq *StatsQueue) Triggered(at *ActionTrigger) *StatsQueueTriggered {
|
||||
return &StatsQueueTriggered{Id: sq.conf.Id, metrics: sq.getStats(), Trigger: at}
|
||||
return &StatsQueueTriggered{Id: sq.conf.Id, Metrics: sq.getStats(), Trigger: at}
|
||||
}
|
||||
|
||||
// Struct to be passed to triggered actions
|
||||
type StatsQueueTriggered struct {
|
||||
Id string // StatsQueueId
|
||||
metrics map[string]float64
|
||||
Metrics map[string]float64
|
||||
Trigger *ActionTrigger
|
||||
}
|
||||
|
||||
@@ -416,22 +416,6 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
|
||||
return
|
||||
}
|
||||
|
||||
/*func (ts *TimeSpan) SplitByTime(splitTime time.Time) (nts *TimeSpan) {
|
||||
if splitTime.Equal(ts.TimeEnd) {
|
||||
return
|
||||
}
|
||||
nts = &TimeSpan{
|
||||
TimeStart: splitTime,
|
||||
TimeEnd: ts.TimeEnd,
|
||||
}
|
||||
nts.copyRatingInfo(ts)
|
||||
ts.TimeEnd = splitTime
|
||||
nts.SetRateInterval(ts.RateInterval)
|
||||
nts.DurationIndex = ts.DurationIndex
|
||||
ts.SetNewDurationIndex(nts)
|
||||
return
|
||||
}*/
|
||||
|
||||
// Split the timespan at the given increment start
|
||||
func (ts *TimeSpan) SplitByIncrement(index int) *TimeSpan {
|
||||
if index <= 0 || index >= len(ts.Increments) {
|
||||
|
||||
Reference in New Issue
Block a user