Commit b47a3071 by bergquist

Merge branch 'master' into influxdb_stop_hidden_queries

parents 6af2d998 603e5fe0
# 3.1.0 (unreleased)
# 3.2.0-pre (unreleased)
### Enhancements
* **Login**: Adds option to disable username/password logins, closes [#4674](https://github.com/grafana/grafana/issues/4674)
* **SingleStat**: Add seriename as option in singlestat panel, closes [#4740](https://github.com/grafana/grafana/issues/4740)
# 3.1.0 stable (unreleased)
### Bugfixes
* **User Alert Notices**: Backend error alert popups did not show properly, fixes [#5435](https://github.com/grafana/grafana/issues/5435)
# 3.1.0-beta1 (2016-06-23)
### Enhancements
* **Dashboard Export/Import**: Dashboard export now templetize data sources and constant variables, users pick these on import, closes [#5084](https://github.com/grafana/grafana/issues/5084)
......@@ -11,7 +23,6 @@
* **InfluxDB**: Add spread function, closes [#5211](https://github.com/grafana/grafana/issues/5211)
* **Scripts**: Use restart instead of start for deb package script, closes [#5282](https://github.com/grafana/grafana/pull/5282)
* **Logging**: Moved to structured logging lib, and moved to component specific level filters via config file, closes [#4590](https://github.com/grafana/grafana/issues/4590)
* **Search**: Add search limit query parameter, closes [#5292](https://github.com/grafana/grafana/pull/5292)
* **OpenTSDB**: Support nested template variables in tag_values function, closes [#4398](https://github.com/grafana/grafana/issues/4398)
* **Datasource**: Pending data source requests are cancelled before new ones are issues (Graphite & Prometheus), closes [#5321](https://github.com/grafana/grafana/issues/5321)
......
......@@ -178,6 +178,9 @@ login_hint = email or username
# Default UI theme ("dark" or "light")
default_theme = dark
# Allow users to sign in using username and password
allow_user_pass_login = true
#################################### Anonymous Auth ##########################
[auth.anonymous]
# enable anonymous access
......
......@@ -10,7 +10,8 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download
------------ | -------------
Stable .deb for Debian-based Linux | [grafana_3.0.4-1464167696.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.4-1464167696_amd64.deb)
Stable .deb for Debian-based Linux | [3.0.4](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.4-1464167696_amd64.deb)
Beta .deb for Debian-based Linux | [3.1.0-beta1](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1466666977beta1_amd64.deb)
## Install Stable
......@@ -18,17 +19,23 @@ Stable .deb for Debian-based Linux | [grafana_3.0.4-1464167696.deb](https://graf
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.0.4-1464167696_amd64.deb
## Install 3.1 beta
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1466666977beta1_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.1.0-1466666977beta1_amd64.deb
## APT Repository
Add the following line to your `/etc/apt/sources.list` file.
deb https://packagecloud.io/grafana/stable/debian/ wheezy main
deb https://packagecloud.io/grafana/stable/debian/ jessie main
Use the above line even if you are on Ubuntu or another Debian version.
There is also a testing repository if you want beta or release
candidates.
deb https://packagecloud.io/grafana/testing/debian/ wheezy main
deb https://packagecloud.io/grafana/testing/debian/ jessie main
Then add the [Package Cloud](https://packagecloud.io/grafana) key. This
allows you to install signed packages.
......
......@@ -10,9 +10,10 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download
------------ | -------------
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-3.0.4-1464167696.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.4-1464167696.x86_64.rpm)
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.0.4 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.4-1464167696.x86_64.rpm)
Beta .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.0-beta1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1466666977beta1.x86_64.rpm)
## Install Stable Release from package file
## Install Latest Stable
You can install Grafana using Yum directly.
......@@ -29,6 +30,23 @@ Or install manually using `rpm`.
$ sudo rpm -i --nodeps grafana-3.0.4-1464167696.x86_64.rpm
## Install 3.1 Beta
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1466666977beta1.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1466666977beta1.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1466666977beta1.x86_64.rpm
## Install via YUM Repository
Add the following to a new file at `/etc/yum.repos.d/grafana.repo`
......
......@@ -14,9 +14,17 @@ The export feature is accessed from the share menu.
<img src="/img/v31/export_menu.png">
### Making a dashboard portable
If you want to export a dashboard for others to use then it could be a good idea to
add template variables for things like a metric prefix (use contant variable) and server name.
A template varible of the type `Constant` will automatically be hidden in
the dashboard, and will also be added as an required input when the dashboard is imported.
## Importing a dashboard
To import a dasbhoard open dashboard search and then hit the import button.
To import a dashboard open dashboard search and then hit the import button.
<img src="/img/v31/import_step1.png">
......
<li><a class='version' href='/v3.1'>Version v3.1</a></li>
<li><a class='version' href='/v3.0'>Version v3.0</a></li>
<li><a class='version' href='/v2.6'>Version v2.6</a></li>
<li><a class='version' href='/v2.5'>Version v2.5</a></li>
......
......@@ -4,7 +4,7 @@
"company": "Coding Instinct AB"
},
"name": "grafana",
"version": "3.1.0",
"version": "3.2.0-pre1",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git"
......@@ -57,7 +57,7 @@
"systemjs": "0.19.24"
},
"engines": {
"node": "0.4.x",
"node": "4.x",
"npm": "2.14.x"
},
"scripts": {
......
#! /usr/bin/env bash
deb_ver=3.0.4-1464167696
rpm_ver=3.0.4-1464167696
deb_ver=3.1.0-1466666977beta1
rpm_ver=3.1.0-1466666977beta1
wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
# wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
# wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm
......@@ -29,6 +29,7 @@ func LoginView(c *middleware.Context) {
viewData.Settings["githubAuthEnabled"] = setting.OAuthService.GitHub
viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp
viewData.Settings["loginHint"] = setting.LoginHint
viewData.Settings["allowUserPassLogin"] = setting.AllowUserPassLogin
if !tryLoginUsingRememberCookie(c) {
c.HTML(200, VIEW_INDEX, viewData)
......
......@@ -16,6 +16,9 @@ type CommandLine interface {
GlobalString(name string) string
FlagNames() (names []string)
Generic(name string) interface{}
PluginDirectory() string
RepoDirectory() string
}
type contextCommandLine struct {
......@@ -33,3 +36,11 @@ func (c *contextCommandLine) ShowVersion() {
func (c *contextCommandLine) Application() *cli.App {
return c.App
}
func (c *contextCommandLine) PluginDirectory() string {
return c.GlobalString("pluginsDir")
}
func (c *contextCommandLine) RepoDirectory() string {
return c.GlobalString("repo")
}
......@@ -93,3 +93,11 @@ func (fcli *FakeCommandLine) Args() cli.Args {
func (fcli *FakeCommandLine) ShowVersion() {
fcli.VersionShown = true
}
func (fcli *FakeCommandLine) RepoDirectory() string {
return fcli.GlobalString("repo")
}
func (fcli *FakeCommandLine) PluginDirectory() string {
return fcli.GlobalString("pluginsDir")
}
......@@ -25,7 +25,7 @@ func validateInput(c CommandLine, pluginFolder string) error {
return errors.New("please specify plugin to install")
}
pluginsDir := c.GlobalString("pluginsDir")
pluginsDir := c.PluginDirectory()
if pluginsDir == "" {
return errors.New("missing pluginsDir flag")
}
......@@ -46,7 +46,7 @@ func validateInput(c CommandLine, pluginFolder string) error {
}
func installCommand(c CommandLine) error {
pluginFolder := c.GlobalString("pluginsDir")
pluginFolder := c.PluginDirectory()
if err := validateInput(c, pluginFolder); err != nil {
return err
}
......@@ -58,8 +58,8 @@ func installCommand(c CommandLine) error {
}
func InstallPlugin(pluginName, version string, c CommandLine) error {
plugin, err := s.GetPlugin(pluginName, c.GlobalString("repo"))
pluginFolder := c.GlobalString("pluginsDir")
plugin, err := s.GetPlugin(pluginName, c.RepoDirectory())
pluginFolder := c.PluginDirectory()
if err != nil {
return err
}
......
......@@ -6,7 +6,7 @@ import (
)
func listremoteCommand(c CommandLine) error {
plugin, err := s.ListAllPlugins(c.GlobalString("repo"))
plugin, err := s.ListAllPlugins(c.RepoDirectory())
if err != nil {
return err
......
......@@ -32,7 +32,7 @@ var validateLsCommand = func(pluginDir string) error {
}
func lsCommand(c CommandLine) error {
pluginDir := c.GlobalString("pluginsDir")
pluginDir := c.PluginDirectory()
if err := validateLsCommand(pluginDir); err != nil {
return err
}
......
......@@ -2,30 +2,32 @@ package commands
import (
"errors"
"fmt"
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
"strings"
)
var getPluginss func(path string) []m.InstalledPlugin = services.GetLocalPlugins
var removePlugin func(pluginPath, id string) error = services.RemoveInstalledPlugin
func removeCommand(c CommandLine) error {
pluginPath := c.GlobalString("pluginsDir")
localPlugins := getPluginss(pluginPath)
pluginPath := c.PluginDirectory()
plugin := c.Args().First()
if plugin == "" {
return errors.New("Missing plugin parameter")
}
for _, p := range localPlugins {
if p.Id == c.Args().First() {
removePlugin(pluginPath, p.Id)
return nil
err := removePlugin(pluginPath, plugin)
if err != nil {
if strings.Contains(err.Error(), "no such file or directory") {
return fmt.Errorf("Plugin does not exist")
}
return err
}
return fmt.Errorf("Could not find plugin named %s", c.Args().First())
return nil
}
......@@ -28,7 +28,7 @@ func ShouldUpgrade(installed string, remote m.Plugin) bool {
}
func upgradeAllCommand(c CommandLine) error {
pluginsDir := c.GlobalString("pluginsDir")
pluginsDir := c.PluginDirectory()
localPlugins := s.GetLocalPlugins(pluginsDir)
......
......@@ -7,7 +7,7 @@ import (
)
func upgradeCommand(c CommandLine) error {
pluginsDir := c.GlobalString("pluginsDir")
pluginsDir := c.PluginDirectory()
pluginName := c.Args().First()
localPlugin, err := s.ReadPlugin(pluginsDir, pluginName)
......@@ -16,7 +16,7 @@ func upgradeCommand(c CommandLine) error {
return err
}
v, err2 := s.GetPlugin(localPlugin.Id, c.GlobalString("repo"))
v, err2 := s.GetPlugin(localPlugin.Id, c.RepoDirectory())
if err2 != nil {
return err2
......
......@@ -75,9 +75,16 @@ func GetLocalPlugins(pluginDir string) []m.InstalledPlugin {
return result
}
func RemoveInstalledPlugin(pluginPath, id string) error {
logger.Infof("Removing plugin: %v\n", id)
return IoHelper.RemoveAll(path.Join(pluginPath, id))
func RemoveInstalledPlugin(pluginPath, pluginName string) error {
logger.Infof("Removing plugin: %v\n", pluginName)
pluginDir := path.Join(pluginPath, pluginName)
_, err := IoHelper.Stat(pluginDir)
if err != nil {
return err
}
return IoHelper.RemoveAll(pluginDir)
}
func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) {
......
......@@ -191,9 +191,7 @@ func TestMiddlewareContext(t *testing.T) {
}
})
var createUserCmd *m.CreateUserCommand
bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
createUserCmd = cmd
cmd.Result = m.User{Id: 33}
return nil
})
......
......@@ -89,6 +89,7 @@ var (
VerifyEmailEnabled bool
LoginHint string
DefaultTheme string
AllowUserPassLogin bool
// Http auth
AdminUser string
......@@ -485,6 +486,7 @@ func NewConfigContext(args *CommandLineArgs) error {
VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
LoginHint = users.Key("login_hint").String()
DefaultTheme = users.Key("default_theme").String()
AllowUserPassLogin = users.Key("allow_user_pass_login").MustBool(true)
// anonymous access
AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
......
......@@ -18,6 +18,7 @@ function (angular, coreModule, config) {
$scope.googleAuthEnabled = config.googleAuthEnabled;
$scope.githubAuthEnabled = config.githubAuthEnabled;
$scope.oauthEnabled = config.githubAuthEnabled || config.googleAuthEnabled;
$scope.allowUserPassLogin = config.allowUserPassLogin;
$scope.disableUserSignUp = config.disableUserSignUp;
$scope.loginHint = config.loginHint;
......
......@@ -28,10 +28,6 @@ export class AlertSrv {
}, this.$rootScope);
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
this.$rootScope.onAppEvent('confirm-modal', (e, data) => {
this.showConfirmModal(data);
}, this.$rootScope);
}
set(title, text, severity, timeout) {
......
......@@ -87,7 +87,7 @@ export class BackendSrv {
});
}
this.$timeout(this.requestErrorHandler.bind(this), 50);
this.$timeout(this.requestErrorHandler.bind(this, err), 50);
throw err;
});
};
......
......@@ -397,6 +397,9 @@ function($, _, moment) {
kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit('rps');
kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit('wps');
kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit('iops');
kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit('opm');
kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit('rpm');
kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit('wpm');
// Energy
kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix('W');
......@@ -664,6 +667,9 @@ function($, _, moment) {
{text: 'reads/sec (rps)', value: 'rps' },
{text: 'writes/sec (wps)', value: 'wps' },
{text: 'I/O ops/sec (iops)', value: 'iops'},
{text: 'ops/min (opm)', value: 'opm' },
{text: 'reads/min (rpm)', value: 'rpm' },
{text: 'writes/min (wpm)', value: 'wpm' },
]
},
{
......
......@@ -17,7 +17,7 @@
</button>
</div>
<form name="loginForm" class="login-form gf-form-group">
<form name="loginForm" class="login-form gf-form-group" ng-show="allowUserPassLogin">
<div class="gf-form" ng-if="loginMode">
<span class="gf-form-label width-7">User</span>
<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}}>
......@@ -40,7 +40,7 @@
</form>
<div ng-if="loginMode">
<div class="text-center login-divider" ng-if="oauthEnabled">
<div class="text-center login-divider" ng-show="oauthEnabled && allowUserPassLogin">
<div class="login-divider-line">
<span class="login-divider-text">
Or login with
......@@ -50,7 +50,7 @@
<div class="clearfix"></div>
<div class="login-oauth text-center" ng-if="oauthEnabled">
<div class="login-oauth text-center" ng-show="oauthEnabled">
<a class="btn btn-large btn-google" href="login/google" target="_self" ng-if="googleAuthEnabled">
<i class="fa fa-google"></i>
with Google
......@@ -64,7 +64,7 @@
<div class="clearfix"></div>
<div class="text-center password-recovery">
<div class="text-center password-recovery" ng-show="allowUserPassLogin">
<div class="text-center">
<a href="user/password/send-reset-email">
Forgot your password?
......
......@@ -77,7 +77,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
var query: any = {};
query.expr = templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
query.requestId = target.expr;
query.requestId = options.panelId + target.refId;
var interval = target.interval || options.interval;
var intervalFactor = target.intervalFactor || 1;
......
......@@ -105,7 +105,7 @@ define([
$scope.addOverrideOption('Stack', 'stack', [true, false, 'A', 'B', 'C', 'D']);
$scope.addOverrideOption('Color', 'color', ['change']);
$scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]);
$scope.addOverrideOption('Z-index', 'zindex', [-1,-2,-3,0,1,2,3]);
$scope.addOverrideOption('Z-index', 'zindex', [-3,-2,-1,0,1,2,3]);
$scope.addOverrideOption('Transform', 'transform', ['negative-Y']);
$scope.addOverrideOption('Legend', 'legend', [true, false]);
$scope.updateCurrentOverrides();
......
......@@ -16,7 +16,10 @@
Value
</li>
<li>
<select class="input-small tight-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ['min','max','avg', 'current', 'total']" ng-change="ctrl.render()"></select>
<select class="input-small tight-form-input"
ng-model="ctrl.panel.valueName"
ng-options="f for f in ctrl.valueNameOptions"
ng-change="ctrl.render()"></select>
</li>
<li class="tight-form-item">
Postfix
......
......@@ -19,6 +19,9 @@ class SingleStatCtrl extends MetricsPanelCtrl {
fontSizes: any[];
unitFormats: any[];
invalidGaugeRange: boolean;
panel: any;
events: any;
valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name'];
// Set and populate defaults
panelDefaults = {
......@@ -186,9 +189,13 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var lastPoint = _.last(this.series[0].datapoints);
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
if (_.isString(lastValue)) {
if (this.panel.valueName === 'name') {
data.value = 0;
data.valueFormated = lastValue;
data.valueRounded = 0;
data.valueFormated = this.series[0].alias;
} else if (_.isString(lastValue)) {
data.value = 0;
data.valueFormated = _.escape(lastValue);
data.valueRounded = 0;
} else {
data.value = this.series[0].stats[this.panel.valueName];
......@@ -199,6 +206,13 @@ class SingleStatCtrl extends MetricsPanelCtrl {
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
}
// Add $__name variable for using in prefix or postfix
data.scopedVars = {
__name: {
value: this.series[0].label
}
};
}
// check value to text mappings if its enabled
......@@ -296,7 +310,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}
function getSpan(className, fontSize, value) {
value = templateSrv.replace(value);
value = templateSrv.replace(value, data.scopedVars);
return '<span class="' + className + '" style="font-size:' + fontSize + '">' +
value + '</span>';
}
......@@ -395,7 +409,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
value: {
color: panel.colorValue ? getColorForValue(data, data.valueRounded) : null,
formatter: function() { return getValueText(); },
font: { size: fontSize, family: 'Helvetica Neue", Helvetica, Arial, sans-serif' }
font: { size: fontSize, family: '"Helvetica Neue", Helvetica, Arial, sans-serif' }
},
show: true
}
......
......@@ -51,6 +51,22 @@ describe('SingleStatCtrl', function() {
});
});
singleStatScenario('showing serie name instead of value', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[10,1], [20,2]];
ctx.ctrl.panel.valueName = 'name';
});
it('Should use series avg as default main value', function() {
expect(ctx.data.value).to.be(0);
expect(ctx.data.valueRounded).to.be(0);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('test.cpu1');
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[99.999,1], [99.99999,2]];
......
......@@ -5,6 +5,7 @@
// Media queries
// ---------------------
@include media-breakpoint-down(sm) {
div.panel {
width: 100% !important;
......@@ -33,6 +34,12 @@
}
}
@include media-breakpoint-down(xs) {
.page-dashboard .navbar-page-btn {
max-width: 150px;
}
}
// form styles
@include media-breakpoint-up(md) {
.page-dashboard .navbar-page-btn {
......
......@@ -102,6 +102,11 @@ $gf-form-margin: 0.25rem;
display: none;
}
&.gf-input-small {
padding: $input-padding-y/3 $input-padding-x/3;
font-size: $font-size-xs;
}
// Customize the `:focus` state to imitate native WebKit styles.
@include form-control-focus();
......
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