Commit a8a94ef8 by Torkel Ödegaard

Merge branch 'master' into timepicker2

parents f93215f4 5d05de8b
...@@ -15,6 +15,7 @@ it allows you to add queries of differnet data source types & instances to the s ...@@ -15,6 +15,7 @@ it allows you to add queries of differnet data source types & instances to the s
- See [Issue #2353](https://github.com/grafana/grafana/issues/2354) for more info. - See [Issue #2353](https://github.com/grafana/grafana/issues/2354) for more info.
** Other new Features && Enhancements** ** Other new Features && Enhancements**
- [Pull #2720](https://github.com/grafana/grafana/pull/2720). Admin: Initial basic quota support (per Org)
- [Issue #2577](https://github.com/grafana/grafana/issues/2577). Panel: Resize handles in panel bottom right corners for easy width and height change - [Issue #2577](https://github.com/grafana/grafana/issues/2577). Panel: Resize handles in panel bottom right corners for easy width and height change
- [Issue #2457](https://github.com/grafana/grafana/issues/2457). Admin: admin page for all grafana organizations (list / edit view) - [Issue #2457](https://github.com/grafana/grafana/issues/2457). Admin: admin page for all grafana organizations (list / edit view)
- [Issue #1186](https://github.com/grafana/grafana/issues/1186). Time Picker: New option `today`, will set time range from midnight to now - [Issue #1186](https://github.com/grafana/grafana/issues/1186). Time Picker: New option `today`, will set time range from midnight to now
...@@ -184,6 +185,10 @@ Grunt & Watch tasks: ...@@ -184,6 +185,10 @@ Grunt & Watch tasks:
# 2.0.0-Beta1 (2015-03-30) # 2.0.0-Beta1 (2015-03-30)
**Important Note**
Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated backend server. Please read the [Documentation](http://docs.grafana.org) for more detailed about this SIGNIFCANT change to Grafana
**New features** **New features**
- [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site - [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site
- [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site - [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site
......
package middleware package middleware
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
...@@ -254,96 +253,3 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) { ...@@ -254,96 +253,3 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) {
ctx.JSON(status, resp) ctx.JSON(status, resp)
} }
func Quota(target string) macaron.Handler {
return func(c *Context) {
limitReached, err := QuotaReached(c, target)
if err != nil {
c.JsonApiErr(500, "failed to get quota", err)
return
}
if limitReached {
c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil)
return
}
}
}
func QuotaReached(c *Context, target string) (bool, error) {
if !setting.Quota.Enabled {
return false, nil
}
// get the list of scopes that this target is valid for. Org, User, Global
scopes, err := m.GetQuotaScopes(target)
if err != nil {
return false, err
}
log.Info(fmt.Sprintf("checking quota for %s in scopes %v", target, scopes))
for _, scope := range scopes {
log.Info(fmt.Sprintf("checking scope %s", scope.Name))
switch scope.Name {
case "global":
if scope.DefaultLimit < 0 {
continue
}
if scope.DefaultLimit == 0 {
return true, nil
}
if target == "session" {
usedSessions := sessionManager.Count()
if int64(usedSessions) > scope.DefaultLimit {
log.Info(fmt.Sprintf("%d sessions active, limit is %d", usedSessions, scope.DefaultLimit))
return true, nil
}
continue
}
query := m.GetGlobalQuotaByTargetQuery{Target: scope.Target}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Used >= scope.DefaultLimit {
return true, nil
}
case "org":
if !c.IsSignedIn {
continue
}
query := m.GetOrgQuotaByTargetQuery{OrgId: c.OrgId, Target: scope.Target, Default: scope.DefaultLimit}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil
}
case "user":
if !c.IsSignedIn || c.UserId == 0 {
continue
}
query := m.GetUserQuotaByTargetQuery{UserId: c.UserId, Target: scope.Target, Default: scope.DefaultLimit}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil
}
}
}
return false, nil
}
package middleware
import (
"fmt"
"github.com/Unknwon/macaron"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func Quota(target string) macaron.Handler {
return func(c *Context) {
limitReached, err := QuotaReached(c, target)
if err != nil {
c.JsonApiErr(500, "failed to get quota", err)
return
}
if limitReached {
c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil)
return
}
}
}
func QuotaReached(c *Context, target string) (bool, error) {
if !setting.Quota.Enabled {
return false, nil
}
// get the list of scopes that this target is valid for. Org, User, Global
scopes, err := m.GetQuotaScopes(target)
if err != nil {
return false, err
}
log.Debug(fmt.Sprintf("checking quota for %s in scopes %v", target, scopes))
for _, scope := range scopes {
log.Debug(fmt.Sprintf("checking scope %s", scope.Name))
switch scope.Name {
case "global":
if scope.DefaultLimit < 0 {
continue
}
if scope.DefaultLimit == 0 {
return true, nil
}
if target == "session" {
usedSessions := getSessionCount()
if int64(usedSessions) > scope.DefaultLimit {
log.Debug(fmt.Sprintf("%d sessions active, limit is %d", usedSessions, scope.DefaultLimit))
return true, nil
}
continue
}
query := m.GetGlobalQuotaByTargetQuery{Target: scope.Target}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Used >= scope.DefaultLimit {
return true, nil
}
case "org":
if !c.IsSignedIn {
continue
}
query := m.GetOrgQuotaByTargetQuery{OrgId: c.OrgId, Target: scope.Target, Default: scope.DefaultLimit}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil
}
case "user":
if !c.IsSignedIn || c.UserId == 0 {
continue
}
query := m.GetUserQuotaByTargetQuery{UserId: c.UserId, Target: scope.Target, Default: scope.DefaultLimit}
if err := bus.Dispatch(&query); err != nil {
return true, err
}
if query.Result.Limit < 0 {
continue
}
if query.Result.Limit == 0 {
return true, nil
}
if query.Result.Used >= query.Result.Limit {
return true, nil
}
}
}
return false, nil
}
package middleware package middleware
import ( import (
"testing"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"testing"
) )
func TestMiddlewareQuota(t *testing.T) { func TestMiddlewareQuota(t *testing.T) {
Convey("Given the grafana quota middleware", t, func() { Convey("Given the grafana quota middleware", t, func() {
getSessionCount = func() int {
return 4
}
setting.AnonymousEnabled = false
setting.Quota = setting.QuotaSettings{ setting.Quota = setting.QuotaSettings{
Enabled: true, Enabled: true,
Org: &setting.OrgQuota{ Org: &setting.OrgQuota{
......
...@@ -18,12 +18,16 @@ const ( ...@@ -18,12 +18,16 @@ const (
var sessionManager *session.Manager var sessionManager *session.Manager
var sessionOptions *session.Options var sessionOptions *session.Options
var startSessionGC func() var startSessionGC func()
var getSessionCount func() int
func init() { func init() {
startSessionGC = func() { startSessionGC = func() {
sessionManager.GC() sessionManager.GC()
time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC) time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC)
} }
getSessionCount = func() int {
return sessionManager.Count()
}
} }
func prepareOptions(opt *session.Options) *session.Options { func prepareOptions(opt *session.Options) *session.Options {
......
...@@ -242,7 +242,7 @@ function (_, $) { ...@@ -242,7 +242,7 @@ function (_, $) {
{ {
name: "function", name: "function",
type: "string", type: "string",
options: ['sum', 'avg'] options: ['sum', 'avg', 'maxSeries']
} }
], ],
defaultParams: [3, "sum"] defaultParams: [3, "sum"]
......
...@@ -93,11 +93,13 @@ function (angular, _, kbn) { ...@@ -93,11 +93,13 @@ function (angular, _, kbn) {
var m = metric + "{" + key + "=*}"; var m = metric + "{" + key + "=*}";
return this._get('/api/search/lookup', {m: m}).then(function(result) { return this._get('/api/search/lookup', {m: m, limit: 3000}).then(function(result) {
result = result.data.results; result = result.data.results;
var tagvs = []; var tagvs = [];
_.each(result, function(r) { _.each(result, function(r) {
tagvs.push(r.tags[key]); if (tagvs.indexOf(r.tags[key]) === -1) {
tagvs.push(r.tags[key]);
}
}); });
return tagvs; return tagvs;
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment