diff --git a/cmd/cgr-balancer/cgr-balanncer.go b/cmd/cgr-balancer/cgr-balanncer.go
index 9991f9243..cda473949 100644
--- a/cmd/cgr-balancer/cgr-balanncer.go
+++ b/cmd/cgr-balancer/cgr-balanncer.go
@@ -21,9 +21,9 @@ package main
import (
"errors"
"flag"
+ "github.com/cgrates/cgrates/balancer"
"github.com/cgrates/cgrates/sessionmanager"
"github.com/cgrates/cgrates/timespans"
- "github.com/cgrates/cgrates/balancer"
"log"
"runtime"
"time"
@@ -32,7 +32,7 @@ import (
var (
raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)")
rpcAddress = flag.String("rpcaddr", "127.0.0.1:2001", "Json RPC server address (localhost:2001)")
- httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:2002)")
+ httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:8000)")
freeswitch = flag.Bool("freeswitch", false, "connect to freeswitch server")
freeswitchsrv = flag.String("freeswitchsrv", "localhost:8021", "freeswitch address host:port")
freeswitchpass = flag.String("freeswitchpass", "ClueCon", "freeswitch address host:port")
diff --git a/cmd/cgr-balancer/status_responder.go b/cmd/cgr-balancer/status_responder.go
index c9e15cfb5..92c9e1593 100644
--- a/cmd/cgr-balancer/status_responder.go
+++ b/cmd/cgr-balancer/status_responder.go
@@ -29,7 +29,7 @@ import (
Handler for the statistics web client
*/
func statusHandler(w http.ResponseWriter, r *http.Request) {
- if t, err := template.ParseFiles("templates/status.html"); err == nil {
+ if t, err := template.ParseFiles("templates/base.html", "templates/status.html"); err == nil {
t.Execute(w, bal.GetClientAddresses())
} else {
log.Print("Error rendering status: ", err)
diff --git a/cmd/cgr-balancer/templates/actiontimings.html b/cmd/cgr-balancer/templates/actiontimings.html
new file mode 100644
index 000000000..8aa790557
--- /dev/null
+++ b/cmd/cgr-balancer/templates/actiontimings.html
@@ -0,0 +1,45 @@
+{{define "title"}}CGRateS Action Timings{{end}}
+{{define "content"}}
+
+
+
+
Action timings
+
+ {{range .}}
+
+ {{.}}
+ Edit
+
+ {{end}}
+
+
+
+
Heading
+
Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
+
View details »
+
+
+{{end}}
+
+{{define "extrascripts"}}
+
+
+{{end}}
\ No newline at end of file
diff --git a/cmd/cgr-balancer/templates/base.html b/cmd/cgr-balancer/templates/base.html
new file mode 100644
index 000000000..a83df1031
--- /dev/null
+++ b/cmd/cgr-balancer/templates/base.html
@@ -0,0 +1,61 @@
+
+
+
+
+ {{template "title" .}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{template "content" .}}
+
+
+
+
+
+
+
+
+
+
+
+ {{template "extrascripts" .}}
+
+
diff --git a/cmd/cgr-balancer/templates/status.html b/cmd/cgr-balancer/templates/status.html
index 35e4b5aa4..d1e30569e 100644
--- a/cmd/cgr-balancer/templates/status.html
+++ b/cmd/cgr-balancer/templates/status.html
@@ -1,48 +1,5 @@
-
-
-
-
- CGRateS Status
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+{{define "title"}}CGRateS Status{{end}}
+{{define "content"}}
-
-
+
+{{end}}
-
-
-
-
-
-
-
-
-
-
-
+{{define "extrascripts"}}
+
-
-
+{{end}}
\ No newline at end of file
diff --git a/cmd/cgr-loader/actions.go b/cmd/cgr-loader/actions.go
index f16dc1498..02d5328a3 100644
--- a/cmd/cgr-loader/actions.go
+++ b/cmd/cgr-loader/actions.go
@@ -19,9 +19,9 @@ along with this program. If not, see
package main
import (
+ "fmt"
"github.com/cgrates/cgrates/timespans"
"log"
- "fmt"
"strconv"
)
@@ -127,6 +127,7 @@ func (csvr *CSVReader) loadActionTimings(fn string) {
}
for _, t := range ts {
at := ×pans.ActionTiming{
+ Id: timespans.GenUUID(),
Tag: record[2],
Weight: weight,
Timing: ×pans.Interval{
@@ -139,7 +140,6 @@ func (csvr *CSVReader) loadActionTimings(fn string) {
}
actionsTimings[tag] = append(actionsTimings[tag], at)
}
-
}
}
diff --git a/cmd/cgr-loader/helpers.go b/cmd/cgr-loader/helpers.go
index aa278b469..5c2fd8e54 100644
--- a/cmd/cgr-loader/helpers.go
+++ b/cmd/cgr-loader/helpers.go
@@ -22,8 +22,8 @@ import (
"github.com/cgrates/cgrates/timespans"
"log"
"strconv"
- "time"
"strings"
+ "time"
)
type Rate struct {
diff --git a/cmd/cgr-scheduler/actiontimings_webapp.go b/cmd/cgr-scheduler/actiontimings_webapp.go
new file mode 100644
index 000000000..8704e31bc
--- /dev/null
+++ b/cmd/cgr-scheduler/actiontimings_webapp.go
@@ -0,0 +1,46 @@
+/*
+Rating system designed to be used in VoIP Carriers World
+Copyright (C) 2012 Radu Ioan Fericean
+
+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 main
+
+import (
+ "html/template"
+ "log"
+ "net/http"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+ actionTimings, err := storage.GetAllActionTimings()
+ if err != nil {
+ log.Print("Cannot get action timings:", err)
+ }
+ if t, err := template.ParseFiles("templates/base.html", "templates/actiontimings.html"); err == nil {
+ t.Execute(w, actionTimings)
+ } else {
+ log.Print("Error rendering status: ", err)
+ }
+}
+
+func startWebApp() {
+ http.Handle("/static/", http.FileServer(http.Dir("")))
+ http.HandleFunc("/", handler)
+ err := http.ListenAndServe(*httpAddress, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/cmd/cgr-scheduler/cgr-scheduler.go b/cmd/cgr-scheduler/cgr-scheduler.go
index 47a51e58c..a3373120c 100644
--- a/cmd/cgr-scheduler/cgr-scheduler.go
+++ b/cmd/cgr-scheduler/cgr-scheduler.go
@@ -23,16 +23,17 @@ import (
"github.com/cgrates/cgrates/timespans"
"log"
"os"
- "sort"
- "time"
"os/signal"
+ "sort"
"syscall"
+ "time"
)
var (
redisserver = flag.String("redisserver", "127.0.0.1:6379", "redis server address (tcp:127.0.0.1:6379)")
redisdb = flag.Int("rdb", 10, "redis database number (10)")
redispass = flag.String("pass", "", "redis database password")
+ httpAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:8000)")
storage timespans.StorageGetter
timer *time.Timer
restartLoop = make(chan byte)
@@ -115,5 +116,6 @@ func main() {
timespans.SetStorageGetter(storage)
loadActionTimings()
go stopSingnalHandler()
+ // go startWebApp()
s.loop()
}
diff --git a/cmd/cgr-scheduler/static b/cmd/cgr-scheduler/static
new file mode 120000
index 000000000..01320e4fc
--- /dev/null
+++ b/cmd/cgr-scheduler/static
@@ -0,0 +1 @@
+../cgr-balancer/static/
\ No newline at end of file
diff --git a/cmd/cgr-scheduler/templates b/cmd/cgr-scheduler/templates
new file mode 120000
index 000000000..8c716e5ad
--- /dev/null
+++ b/cmd/cgr-scheduler/templates
@@ -0,0 +1 @@
+../cgr-balancer/templates/
\ No newline at end of file
diff --git a/timespans/action_timing.go b/timespans/action_timing.go
index 1ffb2658d..7a874fe9f 100644
--- a/timespans/action_timing.go
+++ b/timespans/action_timing.go
@@ -19,6 +19,8 @@ along with this program. If not, see
package timespans
import (
+ "crypto/rand"
+ "encoding/hex"
"fmt"
"log"
"sort"
@@ -32,6 +34,7 @@ const (
)
type ActionTiming struct {
+ Id string // identify the timing
Tag string // informative purpos only
UserBalanceIds []string
Timing *Interval
@@ -210,6 +213,7 @@ func (at *ActionTiming) String() string {
Serializes the action timing for the storage. Used for key-value storages.
*/
func (at *ActionTiming) store() (result string) {
+ result += at.Id + "|"
result += at.Tag + "|"
for _, ubi := range at.UserBalanceIds {
result += ubi + ","
@@ -226,13 +230,28 @@ De-serializes the action timing for the storage. Used for key-value storages.
*/
func (at *ActionTiming) restore(input string) {
elements := strings.Split(input, "|")
- at.Tag = elements[0]
- for _, ubi := range strings.Split(elements[1], ",") {
+ at.Id = elements[0]
+ at.Tag = elements[1]
+ for _, ubi := range strings.Split(elements[2], ",") {
at.UserBalanceIds = append(at.UserBalanceIds, ubi)
}
at.Timing = &Interval{}
- at.Timing.restore(elements[2])
- at.Weight, _ = strconv.ParseFloat(elements[3], 64)
- at.ActionsId = elements[4]
+ at.Timing.restore(elements[3])
+ at.Weight, _ = strconv.ParseFloat(elements[4], 64)
+ at.ActionsId = elements[5]
+}
+
+// helper function for uuid generation
+func GenUUID() string {
+ uuid := make([]byte, 16)
+ n, err := rand.Read(uuid)
+ if n != len(uuid) || err != nil {
+ return strconv.FormatInt(time.Now().UnixNano(), 10)
+ }
+ // TODO: verify the two lines implement RFC 4122 correctly
+ uuid[8] = 0x80 // variant bits see page 5
+ uuid[4] = 0x40 // version 4 Pseudo Random, see page 7
+
+ return hex.EncodeToString(uuid)
}
diff --git a/timespans/actions_test.go b/timespans/actions_test.go
index b3fe5a441..0d82d69b7 100644
--- a/timespans/actions_test.go
+++ b/timespans/actions_test.go
@@ -42,6 +42,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
BillingUnit: 1.0,
}
at := &ActionTiming{
+ Id: "some uuid",
Tag: "test",
UserBalanceIds: []string{"one", "two", "three"},
Timing: i,
@@ -49,7 +50,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
ActionsId: "Commando",
}
r := at.store()
- if string(r) != "test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" {
+ if string(r) != "some uuid|test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" {
t.Errorf("Error serializing action timing: %v", string(r))
}
o := &ActionTiming{}
@@ -630,3 +631,30 @@ func TestActionResetCounterCREDIT(t *testing.T) {
t.Error("Reset counters action failed!", ub.UnitCounters)
}
}
+
+func TestUUID(t *testing.T) {
+ uuid := GenUUID()
+ if len(uuid) == 0 {
+ t.Fatalf("GenUUID error %s", uuid)
+ }
+ t.Logf("uuid[%s]\n", uuid)
+}
+
+/********************************** Benchmarks ********************************/
+
+func BenchmarkUUID(b *testing.B) {
+ m := make(map[string]int, 1000)
+ for i := 0; i < b.N; i++ {
+ uuid := GenUUID()
+ if len(uuid) == 0 {
+ b.Fatalf("GenUUID error %s", uuid)
+ }
+ b.StopTimer()
+ c := m[uuid]
+ if c > 0 {
+ b.Fatalf("duplicate uuid[%s] count %d", uuid, c)
+ }
+ m[uuid] = c + 1
+ b.StartTimer()
+ }
+}
diff --git a/timespans/calldesc.go b/timespans/calldesc.go
index 60c745a7c..81077355b 100644
--- a/timespans/calldesc.go
+++ b/timespans/calldesc.go
@@ -22,10 +22,20 @@ import (
"errors"
"fmt"
"log"
+ "log/syslog"
"math"
+ "os"
"time"
)
+func init() {
+ var err error
+ logger, err = syslog.NewLogger(syslog.LOG_ALERT, log.LstdFlags)
+ if err != nil {
+ logger = log.New(os.Stderr, "", log.LstdFlags)
+ }
+}
+
const (
// the minimum length for a destination prefix to be matched.
MinPrefixLength = 2
@@ -33,6 +43,11 @@ const (
FallbackDestination = "fallback" // the string to be used to mark the fallback destination
)
+var (
+ storageGetter StorageGetter
+ logger *log.Logger
+)
+
/*
Utility function for rounding a float to a certain number of decimals (not present in math).
*/
@@ -270,6 +285,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
Cost: cost,
ConnectFee: connectionFee,
Timespans: timespans}
+ logger.Printf("Get Cost: %v => %v", cd, cc)
return cc, err
}
@@ -309,7 +325,7 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
}
cost += ts.getCost(cd)
}
- //log.Print(availableCredit, availableSeconds, cost)
+ //logger.Print(availableCredit, availableSeconds, cost)
if cost < availableCredit {
return maxSessionSeconds, nil
} else { //decrease the period by 10% and try again
@@ -324,7 +340,7 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
cc, err = cd.GetCost()
if err != nil {
- log.Printf("error getting cost %v", err)
+ logger.Printf("error getting cost %v", err)
}
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
defer storageGetter.SetUserBalance(userBalance)
diff --git a/timespans/userbalance.go b/timespans/userbalance.go
index 5e2acd6ed..81eeb5e7d 100644
--- a/timespans/userbalance.go
+++ b/timespans/userbalance.go
@@ -41,10 +41,6 @@ const (
ABSOLUTE = "ABSOLUTE"
)
-var (
- storageGetter StorageGetter
-)
-
/*
Structure containing information about user's credit (minutes, cents, sms...).'
*/