worked on the web interface for schedduler (dropped) and started new logging to syslog

This commit is contained in:
Radu Ioan Fericean
2012-07-18 20:17:58 +03:00
parent cc7e485b64
commit d185dc3556
15 changed files with 242 additions and 83 deletions

View File

@@ -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")

View File

@@ -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)

View File

@@ -0,0 +1,45 @@
{{define "title"}}CGRateS Action Timings{{end}}
{{define "content"}}
<div class="modal hide fade" id="myModal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Modal header</h3>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary">Save changes</a>
</div>
</div>
<div class="row">
<div class="span7">
<h1>Action timings</h1>
<table id="rater-table" class="table table-striped">
{{range .}}
<tr>
<td>{{.}}</td>
<td><a class="btn" data-toggle="modal" href="#myModal" >Edit</a></td>
</tr>
{{end}}
</table>
</div>
<div class="span5">
<h2>Heading</h2>
<p>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.</p>
<p><a class="btn" href="#">View details &raquo;</a></p>
</div>
</div>
{{end}}
{{define "extrascripts"}}
<script src="static/js/jquery.flot.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function(){
$('#myModal').modal({
show: false
})
});
</script>
{{end}}

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{template "title" .}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Describes the internal status of CGRateS server.">
<meta name="author" content="Radu Ioan Fericean">
<!-- Le styles -->
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
</style>
<link href="static/css/bootstrap-responsive.min.css" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#">CGRateS</a>
<div class="nav-collapse">
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div class="container">
{{template "content" .}}
<hr>
<footer>
<p>&copy; Radu Fericean 2012</p>
</footer>
</div> <!-- /container -->
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="static/js/jquery-1.7.2.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
</script>{{template "extrascripts" .}}
</body>
</html>

View File

@@ -1,48 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CGRateS Status</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Describes the internal status of CGRateS server.">
<meta name="author" content="Radu Ioan Fericean">
<!-- Le styles -->
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
</style>
<link href="static/css/bootstrap-responsive.min.css" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#">CGRateS</a>
<div class="nav-collapse">
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div class="container">
<!-- Main hero unit for a primary marketing message or call to action -->
{{define "title"}}CGRateS Status{{end}}
{{define "content"}}
<div class="hero-unit">
<h1>Conected raters</h1>
<table id="rater-table" class="table table-striped">
@@ -52,8 +9,7 @@
</table>
<p><a id="rater-refresh" href="#" class="btn btn-primary btn-large">Refresh</a></p>
</div>
<!-- Example row of columns -->
<div class="row">
<div class="span7">
<h2>Memory consumption (KB)</h2>
@@ -65,21 +21,10 @@
<p><a class="btn" href="#">View details &raquo;</a></p>
</div>
</div>
{{end}}
<hr>
<footer>
<p>&copy; Radu Fericean 2012</p>
</footer>
</div> <!-- /container -->
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="static/js/jquery-1.7.2.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<script src="static/js/jquery.flot.min.js" type="text/javascript"></script>
{{define "extrascripts"}}
<script src="static/js/jquery.flot.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
// we use an inline data source in the example, usually data would
@@ -134,5 +79,4 @@
})
});
</script>
</body>
</html>
{{end}}

View File

@@ -19,9 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
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 := &timespans.ActionTiming{
Id: timespans.GenUUID(),
Tag: record[2],
Weight: weight,
Timing: &timespans.Interval{
@@ -139,7 +140,6 @@ func (csvr *CSVReader) loadActionTimings(fn string) {
}
actionsTimings[tag] = append(actionsTimings[tag], at)
}
}
}

View File

@@ -22,8 +22,8 @@ import (
"github.com/cgrates/cgrates/timespans"
"log"
"strconv"
"time"
"strings"
"time"
)
type Rate struct {

View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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)
}
}

View File

@@ -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()
}

1
cmd/cgr-scheduler/static Symbolic link
View File

@@ -0,0 +1 @@
../cgr-balancer/static/

1
cmd/cgr-scheduler/templates Symbolic link
View File

@@ -0,0 +1 @@
../cgr-balancer/templates/

View File

@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
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)
}

View File

@@ -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()
}
}

View File

@@ -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)

View File

@@ -41,10 +41,6 @@ const (
ABSOLUTE = "ABSOLUTE"
)
var (
storageGetter StorageGetter
)
/*
Structure containing information about user's credit (minutes, cents, sms...).'
*/