diff --git a/cmd/inquirer/http_responder.go b/cmd/inquirer/http_responder.go index a699242a1..f2d0c73da 100644 --- a/cmd/inquirer/http_responder.go +++ b/cmd/inquirer/http_responder.go @@ -1,11 +1,12 @@ package main -import( +import ( + "encoding/json" "fmt" + "github.com/rif/cgrates/timespans" "log" "net/http" - "encoding/json" - "github.com/rif/cgrates/timespans" + "strconv" ) type IncorrectParameters struct { @@ -30,20 +31,83 @@ func getCostHandler(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) r.ParseForm() cstmid, ok1 := r.Form["cstmid"] - subj, ok2 :=r.Form["subj"] + subj, ok2 := r.Form["subj"] dest, ok3 := r.Form["dest"] if !ok1 || !ok2 || !ok3 { enc.Encode(IncorrectParameters{"Incorrect parameters"}) return } arg := ×pans.CallDescriptor{CstmId: cstmid[0], Subject: subj[0], DestinationPrefix: dest[0]} - callCost := CallRater(arg) + callCost := GetCost(arg) enc.Encode(callCost) } -func listenToHttpRequests(){ +/* +curl "http://127.0.0.1:8000/getcost?cstmid=vdf&subj=rif&dest=0257@amount=100" +*/ +func debitBalanceHandler(w http.ResponseWriter, r *http.Request) { + enc := json.NewEncoder(w) + r.ParseForm() + cstmid, ok1 := r.Form["cstmid"] + subj, ok2 := r.Form["subj"] + dest, ok3 := r.Form["dest"] + amount_s, ok4 := r.Form["amount"] + amount, err := strconv.ParseFloat(amount_s[0], 64) + if !ok1 || !ok2 || !ok3 || ok4 || err != nil { + enc.Encode(IncorrectParameters{"Incorrect parameters"}) + return + } + arg := ×pans.CallDescriptor{CstmId: cstmid[0], Subject: subj[0], DestinationPrefix: dest[0], Amount: amount} + result := Debit(arg, "Storage.DebitCents") + enc.Encode(result) +} + +/* +curl "http://127.0.0.1:8000/getcost?cstmid=vdf&subj=rif&dest=0257@amount=100" +*/ +func debitSMSHandler(w http.ResponseWriter, r *http.Request) { + enc := json.NewEncoder(w) + r.ParseForm() + cstmid, ok1 := r.Form["cstmid"] + subj, ok2 := r.Form["subj"] + dest, ok3 := r.Form["dest"] + amount_s, ok4 := r.Form["amount"] + amount, err := strconv.ParseFloat(amount_s[0], 64) + if !ok1 || !ok2 || !ok3 || !ok4 || err != nil { + enc.Encode(IncorrectParameters{"Incorrect parameters"}) + return + } + arg := ×pans.CallDescriptor{CstmId: cstmid[0], Subject: subj[0], DestinationPrefix: dest[0], Amount: amount} + result := Debit(arg, "Storage.DebitSMS") + enc.Encode(result) +} + +/* +curl "http://127.0.0.1:8000/getcost?cstmid=vdf&subj=rif&dest=0257@amount=100" +*/ +func debitSecondsHandler(w http.ResponseWriter, r *http.Request) { + enc := json.NewEncoder(w) + r.ParseForm() + cstmid, ok1 := r.Form["cstmid"] + subj, ok2 := r.Form["subj"] + dest, ok3 := r.Form["dest"] + amount_s, ok4 := r.Form["amount"] + amount, err := strconv.ParseFloat(amount_s[0], 64) + if !ok1 || !ok2 || !ok3 || !ok4 || err != nil { + enc.Encode(IncorrectParameters{"Incorrect parameters"}) + return + } + arg := ×pans.CallDescriptor{CstmId: cstmid[0], Subject: subj[0], DestinationPrefix: dest[0], Amount: amount} + result := Debit(arg, "Storage.DebitSeconds") + enc.Encode(result) +} + +func listenToHttpRequests() { http.HandleFunc("/", statusHandler) http.HandleFunc("/getcost", getCostHandler) + http.HandleFunc("/debitbalance", debitBalanceHandler) + http.HandleFunc("/debitsms", debitSMSHandler) + http.HandleFunc("/debitseconds", debitSecondsHandler) log.Print("The server is listening on ", *httpApiAddress) http.ListenAndServe(*httpApiAddress, nil) } diff --git a/cmd/inquirer/inquirer.go b/cmd/inquirer/inquirer.go index 11bbed56b..5dfcb6d16 100644 --- a/cmd/inquirer/inquirer.go +++ b/cmd/inquirer/inquirer.go @@ -2,25 +2,24 @@ package main import ( "errors" + "flag" "github.com/rif/cgrates/timespans" "log" - //"runtime" + "runtime" "time" - "flag" ) var ( - raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)") - jsonRpcAddress = flag.String("jsonrpcaddr", "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)") - raterList *RaterList + raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)") + jsonRpcAddress = flag.String("jsonrpcaddr", "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)") + raterList *RaterList ) - /* The function that gets the information from the raters using balancer. */ -func CallRater(key *timespans.CallDescriptor) (reply *timespans.CallCost) { +func GetCost(key *timespans.CallDescriptor) (reply *timespans.CallCost) { err := errors.New("") //not nil value for err != nil { client := raterList.Balance() @@ -38,11 +37,29 @@ func CallRater(key *timespans.CallDescriptor) (reply *timespans.CallCost) { return } - +/* +The function that gets the information from the raters using balancer. +*/ +func Debit(key *timespans.CallDescriptor, method string) (reply float64) { + err := errors.New("") //not nil value + for err != nil { + client := raterList.Balance() + if client == nil { + log.Print("Waiting for raters to register...") + time.Sleep(1 * time.Second) // wait one second and retry + } else { + err = client.Call(method, *key, &reply) + if err != nil { + log.Printf("Got en error from rater: %v", err) + } + } + } + return +} func main() { flag.Parse() - //runtime.GOMAXPROCS(runtime.NumCPU() - 1) + runtime.GOMAXPROCS(runtime.NumCPU() - 1) raterList = NewRaterList() go StopSingnalHandler() diff --git a/cmd/inquirer/jsonrpc_responder.go b/cmd/inquirer/jsonrpc_responder.go index e451a7981..8c86037fd 100644 --- a/cmd/inquirer/jsonrpc_responder.go +++ b/cmd/inquirer/jsonrpc_responder.go @@ -2,10 +2,10 @@ package main import ( "github.com/rif/cgrates/timespans" + "log" "net" "net/rpc" "net/rpc/jsonrpc" - "log" ) type Responder byte @@ -13,9 +13,24 @@ type Responder byte /* RPC method thet provides the external RPC interface for getting the rating information. */ -func (r *Responder) Get(arg timespans.CallDescriptor, replay *timespans.CallCost) error { - *replay = *CallRater(&arg) - return nil +func (r *Responder) GetGost(arg timespans.CallDescriptor, replay *timespans.CallCost) (err error) { + *replay = *GetCost(&arg) + return +} + +func (r *Responder) DebitBalance(arg timespans.CallDescriptor, replay *float64) (err error) { + *replay = Debit(&arg, "Storage.DebitCents") + return +} + +func (r *Responder) DebitSMS(arg timespans.CallDescriptor, replay *float64) (err error) { + *replay = Debit(&arg, "Storage.DebitSMS") + return +} + +func (r *Responder) DebitSeconds(arg timespans.CallDescriptor, replay *float64) (err error) { + *replay = Debit(&arg, "Storage.DebitSeconds") + return } /* diff --git a/cmd/rater/rater.go b/cmd/rater/rater.go index fe6bb6cc6..568f71891 100644 --- a/cmd/rater/rater.go +++ b/cmd/rater/rater.go @@ -31,7 +31,31 @@ func (s *Storage) GetCost(cd timespans.CallDescriptor, reply *timespans.CallCost descriptor.StorageGetter = s.sg r, e := descriptor.GetCost() *reply, err = *r, e - return nil + return err +} + +func (s *Storage) DebitCents(cd timespans.CallDescriptor, reply *float64) (err error) { + descriptor := &cd + descriptor.StorageGetter = s.sg + r, e := descriptor.DebitCents() + *reply, err = r, e + return err +} + +func (s *Storage) DebitSMS(cd timespans.CallDescriptor, reply *float64) (err error) { + descriptor := &cd + descriptor.StorageGetter = s.sg + r, e := descriptor.DebitSMS() + *reply, err = r, e + return err +} + +func (s *Storage) DebitSeconds(cd timespans.CallDescriptor, reply *float64) (err error) { + descriptor := &cd + descriptor.StorageGetter = s.sg + e := descriptor.DebitSeconds() + *reply, err = 0.0, e + return err } /* diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 026645a3a..9197e9022 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -36,6 +36,7 @@ type CallDescriptor struct { TOR int CstmId, Subject, DestinationPrefix string TimeStart, TimeEnd time.Time + Amount float64 ActivationPeriods []*ActivationPeriod StorageGetter StorageGetter } @@ -237,6 +238,27 @@ func (cd *CallDescriptor) GetMaxSessionTime(maxSessionSeconds int) (seconds int, return 0, nil } +func (cd *CallDescriptor) DebitCents() (left float64, err error) { + if userBudget, err := cd.StorageGetter.GetUserBudget(cd.Subject); err == nil && userBudget != nil { + return userBudget.debitMoneyBudget(cd.StorageGetter, cd.Amount), nil + } + return 0.0, err +} + +func (cd *CallDescriptor) DebitSMS() (left float64, err error) { + if userBudget, err := cd.StorageGetter.GetUserBudget(cd.Subject); err == nil && userBudget != nil { + return userBudget.debitSMSBuget(cd.StorageGetter, cd.Amount) + } + return 0, err +} + +func (cd *CallDescriptor) DebitSeconds() (err error) { + if userBudget, err := cd.StorageGetter.GetUserBudget(cd.Subject); err == nil && userBudget != nil { + return userBudget.debitMinutesBudget(cd.StorageGetter, cd.Amount, cd.DestinationPrefix) + } + return err +} + /* The output structure that will be returned with the call cost information. */ diff --git a/timespans/userbudget.go b/timespans/userbudget.go index 344751363..75fe9fe80 100644 --- a/timespans/userbudget.go +++ b/timespans/userbudget.go @@ -14,7 +14,7 @@ Structure conatining information about user's credit (minutes, cents, sms...).' type UserBudget struct { Id string Credit float64 - SmsCredit int + SmsCredit float64 ResetDayOfTheMonth int TariffPlanId string tariffPlan *TariffPlan @@ -22,6 +22,9 @@ type UserBudget struct { mux sync.RWMutex } +/* +Error type for overflowed debit methods. +*/ type AmountTooBig byte func (a AmountTooBig) Error() string { @@ -52,7 +55,7 @@ Serializes the user budget for the storage. Used for key-value storages. */ func (ub *UserBudget) store() (result string) { result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";" - result += strconv.Itoa(ub.SmsCredit) + ";" + result += strconv.FormatFloat(ub.SmsCredit, 'f', -1, 64) + ";" result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";" result += ub.TariffPlanId + ";" for _, mb := range ub.MinuteBuckets { @@ -72,7 +75,7 @@ De-serializes the user budget for the storage. Used for key-value storages. func (ub *UserBudget) restore(input string) { elements := strings.Split(input, ";") ub.Credit, _ = strconv.ParseFloat(elements[0], 64) - ub.SmsCredit, _ = strconv.Atoi(elements[1]) + ub.SmsCredit, _ = strconv.ParseFloat(elements[1], 64) ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[2]) ub.TariffPlanId = elements[3] for _, mbs := range elements[4 : len(elements)-1] { @@ -193,7 +196,7 @@ func (ub *UserBudget) debitMinutesBudget(sg StorageGetter, amount float64, prefi Debits some amount of user's SMS budget. Returns the remaining SMS in user's budget. If the amount is bigger than the budget than nothing wil be debited and an error will be returned */ -func (ub *UserBudget) debitSMSBuget(sg StorageGetter, amount int) (int, error) { +func (ub *UserBudget) debitSMSBuget(sg StorageGetter, amount float64) (float64, error) { ub.mux.Lock() defer ub.mux.Unlock() if ub.SmsCredit < amount {