Commit 052c9aca by Torkel Ödegaard

Merge branch 'master' of github.com:grafana/grafana

parents c1f77eea b0e975bf
...@@ -28,6 +28,7 @@ it allows you to add queries of differnet data source types & instances to the s ...@@ -28,6 +28,7 @@ it allows you to add queries of differnet data source types & instances to the s
- [Issue #2708](https://github.com/grafana/grafana/issues/2708). InfluxDB: You can now set math expression for select clauses. - [Issue #2708](https://github.com/grafana/grafana/issues/2708). InfluxDB: You can now set math expression for select clauses.
- [Issue #1575](https://github.com/grafana/grafana/issues/1575). Drilldown link: now you can click on the external link icon in the panel header to access drilldown links! - [Issue #1575](https://github.com/grafana/grafana/issues/1575). Drilldown link: now you can click on the external link icon in the panel header to access drilldown links!
- [Issue #1646](https://github.com/grafana/grafana/issues/1646). OpenTSDB: Fetch list of aggregators from OpenTSDB - [Issue #1646](https://github.com/grafana/grafana/issues/1646). OpenTSDB: Fetch list of aggregators from OpenTSDB
- [Issue #2955](https://github.com/grafana/grafana/issues/2955). Graph: More axis units (Length, Volume, Temperature, Pressure, etc), thanks @greglook
**Fixes** **Fixes**
- [Issue #2413](https://github.com/grafana/grafana/issues/2413). InfluxDB 0.9: Fix for handling empty series object in response from influxdb - [Issue #2413](https://github.com/grafana/grafana/issues/2413). InfluxDB 0.9: Fix for handling empty series object in response from influxdb
......
...@@ -89,8 +89,13 @@ func main() { ...@@ -89,8 +89,13 @@ func main() {
} }
func makeLatestDistCopies() { func makeLatestDistCopies() {
rpmIteration := "-1"
if linuxPackageIteration != "" {
rpmIteration = "-" + linuxPackageIteration
}
runError("cp", "dist/grafana_"+version+"_amd64.deb", "dist/grafana_latest_amd64.deb") runError("cp", "dist/grafana_"+version+"_amd64.deb", "dist/grafana_latest_amd64.deb")
runError("cp", "dist/grafana-"+strings.Replace(version, "-", "_", 5)+"-1.x86_64.rpm", "dist/grafana-latest-1.x86_64.rpm") runError("cp", "dist/grafana-"+linuxPackageVersion+rpmIteration+".x86_64.rpm", "dist/grafana-latest-1.x86_64.rpm")
runError("cp", "dist/grafana-"+version+".linux-x64.tar.gz", "dist/grafana-latest.linux-x64.tar.gz") runError("cp", "dist/grafana-"+version+".linux-x64.tar.gz", "dist/grafana-latest.linux-x64.tar.gz")
} }
......
...@@ -4,10 +4,6 @@ global: ...@@ -4,10 +4,6 @@ global:
evaluation_interval: 10s # By default, scrape targets every 15 seconds. evaluation_interval: 10s # By default, scrape targets every 15 seconds.
# scrape_timeout is set to the global default (10s). # scrape_timeout is set to the global default (10s).
# Attach these extra labels to all timeseries collected by this Prometheus instance.
labels:
monitor: 'codelab-monitor'
# Load and evaluate rules in this file every 'evaluation_interval' seconds. # Load and evaluate rules in this file every 'evaluation_interval' seconds.
rule_files: rule_files:
# - "first.rules" # - "first.rules"
......
...@@ -29,7 +29,7 @@ Url | The http protocol, ip and port of you graphite-web or graphite-api install ...@@ -29,7 +29,7 @@ Url | The http protocol, ip and port of you graphite-web or graphite-api install
Access | Proxy = access via Grafana backend, Direct = access directory from browser. Access | Proxy = access via Grafana backend, Direct = access directory from browser.
Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the brower. Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the browser.
Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source. Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source.
......
...@@ -28,12 +28,12 @@ Name | Description ...@@ -28,12 +28,12 @@ Name | Description
Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards.
Default | Default data source means that it will be pre-selected for new panels. Default | Default data source means that it will be pre-selected for new panels.
Url | The http protocol, ip and port of you influxdb api (influxdb api port is by default 8086) Url | The http protocol, ip and port of you influxdb api (influxdb api port is by default 8086)
Access | Proxy = access via Grafana backend, Direct = access directory from browser. Access | Proxy = access via Grafana backend, Direct = access directly from browser.
Database | Name of your influxdb database Database | Name of your influxdb database
User | Name of your database user User | Name of your database user
Password | Database user's password Password | Database user's password
> Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the brower. > Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the browser.
> Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source. > Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source.
......
...@@ -32,7 +32,7 @@ Open a graph in edit mode by click the title. ...@@ -32,7 +32,7 @@ Open a graph in edit mode by click the title.
![](/img/v2/kairos_query_editor.png) ![](/img/v2/kairos_query_editor.png)
For details on KairosDB metric queries checkout the offical. For details on KairosDB metric queries checkout the official.
- [Query Metrics - KairosDB 0.9.4 documentation](http://kairosdb.github.io/kairosdocs/restapi/QueryMetrics.html). - [Query Metrics - KairosDB 0.9.4 documentation](http://kairosdb.github.io/kairosdocs/restapi/QueryMetrics.html).
## Templated queries ## Templated queries
......
...@@ -28,7 +28,7 @@ Basic Auth | Enable basic authentication to the Prometheus datasource. ...@@ -28,7 +28,7 @@ Basic Auth | Enable basic authentication to the Prometheus datasource.
User | Name of your Prometheus user User | Name of your Prometheus user
Password | Database user's password Password | Database user's password
> Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the brower. > Proxy access means that the Grafana backend will proxy all requests from the browser, and send them on to the Data Source. This is useful because it can eliminate CORS (Cross Origin Site Resource) issues, as well as eliminate the need to disseminate authentication details to the Data Source to the browser.
> Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source. > Direct access is still supported because in some cases it may be useful to access a Data Source directly depending on the use case and topology of Grafana, the user, and the Data Source.
......
...@@ -29,7 +29,7 @@ The image above shows you the top header for a Dashboard. ...@@ -29,7 +29,7 @@ The image above shows you the top header for a Dashboard.
6. Settings: Manage Dashboard settings and features such as Templating and Annotations. 6. Settings: Manage Dashboard settings and features such as Templating and Annotations.
## Dashboards, Panels, Rows, the building blocks of Grafana... ## Dashboards, Panels, Rows, the building blocks of Grafana...
Dashboards are at the core of what Grafana is all about. Dashboards are composed of individual Panels arranged on a number of Rows. Grafana ships with a variety of Panels. Gafana makes it easy to construct the right queries, and customize the display properities so that you can create the perfect Dashboard for your need. Each Panel can interact with data from any configured Grafana Data Source (currently InfluxDB, Graphite, OpenTSDB, and KairosDB). The [Core Concepts](/guides/basic_concepts) guide explores these key ideas in detail. Dashboards are at the core of what Grafana is all about. Dashboards are composed of individual Panels arranged on a number of Rows. Grafana ships with a variety of Panels. Grafana makes it easy to construct the right queries, and customize the display properties so that you can create the perfect Dashboard for your need. Each Panel can interact with data from any configured Grafana Data Source (currently InfluxDB, Graphite, OpenTSDB, and KairosDB). The [Core Concepts](/guides/basic_concepts) guide explores these key ideas in detail.
## Adding & Editing Graphs and Panels ## Adding & Editing Graphs and Panels
......
...@@ -32,7 +32,7 @@ no_toc: true ...@@ -32,7 +32,7 @@ no_toc: true
</div> </div>
<div class="columns medium-6"> <div class="columns medium-6">
<h3><strong>Episode 4</strong> - Installation & Configuration on Ubuntu / Debian</h3> <h3><strong>Episode 4</strong> - Installation & Configuration on Ubuntu / Debian</h3>
Learn how to easily install the dependencies and packages to get Grafana 2.0 up and running on Ubuntu or Debian in just a few mintues. Learn how to easily install the dependencies and packages to get Grafana 2.0 up and running on Ubuntu or Debian in just a few minutes.
<div class="video-container" style="margin-top:10px;"> <div class="video-container" style="margin-top:10px;">
<iframe height="215" src="https://www.youtube.com/embed/JY22EBOR9hQ?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe> <iframe height="215" src="https://www.youtube.com/embed/JY22EBOR9hQ?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe>
</div> </div>
......
...@@ -112,7 +112,7 @@ for example make all series that contain the word **CPU** `red` and assigned to ...@@ -112,7 +112,7 @@ for example make all series that contain the word **CPU** `red` and assigned to
![Define series color using regex rule](/img/v2/regex_color_override.png "Define series color using regex rule") ![Define series color using regex rule](/img/v2/regex_color_override.png "Define series color using regex rule")
New series style override, negative-y transform and stack groups. Negative y tranform is New series style override, negative-y transform and stack groups. Negative y transform is
very useful if you want to plot a series on the negative y scale without affecting the legend values like min or max or very useful if you want to plot a series on the negative y scale without affecting the legend values like min or max or
the values shown in the hover tooltip. the values shown in the hover tooltip.
...@@ -126,5 +126,5 @@ string values. ...@@ -126,5 +126,5 @@ string values.
### Changelog ### Changelog
For a detailed list and link to github issues for everything included in the 2.1 release please For a detailed list and link to github issues for everything included in the 2.1 release please
view the [CHANGELOG.md]("https://github.com/grafana/grafana/blob/master/CHANGELOG.md") file. view the [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) file.
...@@ -266,7 +266,7 @@ automatically signed up. ...@@ -266,7 +266,7 @@ automatically signed up.
### team_ids ### team_ids
Require an active team membership for at least one of the given teams on Require an active team membership for at least one of the given teams on
GitHub. If the authenticated user isn't a member of at least one the GitHub. If the authenticated user isn't a member of at least one of the
teams they will not be able to register or authenticate with your teams they will not be able to register or authenticate with your
Grafana instance. For example: Grafana instance. For example:
...@@ -274,7 +274,7 @@ Grafana instance. For example: ...@@ -274,7 +274,7 @@ Grafana instance. For example:
enabled = true enabled = true
client_id = YOUR_GITHUB_APP_CLIENT_ID client_id = YOUR_GITHUB_APP_CLIENT_ID
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
scopes = user:email scopes = user:email,read:org
team_ids = 150,300 team_ids = 150,300
auth_url = https://github.com/login/oauth/authorize auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token token_url = https://github.com/login/oauth/access_token
......
...@@ -9,7 +9,7 @@ page_keywords: grafana, installation, docker, container, guide ...@@ -9,7 +9,7 @@ page_keywords: grafana, installation, docker, container, guide
> **2.0.2 -> 2.1.0 Upgrade NOTICE!** > **2.0.2 -> 2.1.0 Upgrade NOTICE!**
> The data and log paths were not correct in the previous image. The grafana database was placed by default in /usr/share/grafana/data instead of the correct path /var/lib/grafana. This means it was not in a dir that was marked as a volume. So if you remove the container it will remove the grafana database. So before updating make sure you copy the /usr/share/grafana/data path from inside the container to the host. > The data and log paths were not correct in the previous image. The grafana database was placed by default in /usr/share/grafana/data instead of the correct path /var/lib/grafana. This means it was not in a dir that was marked as a volume. So if you remove the container it will remove the grafana database. So before updating make sure you copy the /usr/share/grafana/data path from inside the container to the host.
## Install from offical docker image ## Install from official docker image
Grafana has an official Docker container. Grafana has an official Docker container.
......
--- ---
page_title: LDAP Integration page_title: LDAP Integration
page_description: LDAP Integrtaion guide for Grafana. page_description: LDAP Integration guide for Grafana.
page_keywords: grafana, ldap, configuration, documentation, integration page_keywords: grafana, ldap, configuration, documentation, integration
--- ---
...@@ -85,12 +85,12 @@ bind_dn = "cn=%s,o=users,dc=grafana,dc=org" ...@@ -85,12 +85,12 @@ bind_dn = "cn=%s,o=users,dc=grafana,dc=org"
``` ```
In this case you skip providing a `bind_password` and instead provide a `bind_dn` value with a `%s` somewhere. This will be replaced with the username entered in on the Grafana login page. In this case you skip providing a `bind_password` and instead provide a `bind_dn` value with a `%s` somewhere. This will be replaced with the username entered in on the Grafana login page.
The search filter and search bases settings are still needed to perform the LDAP search to retreive the other LDAP information (like LDAP groups and email). The search filter and search bases settings are still needed to perform the LDAP search to retrieve the other LDAP information (like LDAP groups and email).
## LDAP to Grafana Org Role Sync ## LDAP to Grafana Org Role Sync
## Group Mappings ## Group Mappings
In `[[servers.group_mappings]]` you can map an LDAP group to a Grafana organization and role. These will be synced every time the user logs in, with LDAP being the authoratative source. In `[[servers.group_mappings]]` you can map an LDAP group to a Grafana organization and role. These will be synced every time the user logs in, with LDAP being the authoritative source.
So, if you change a user's role in the Grafana Org. Users page, this change will be reset the next time the user logs in. If you change the LDAP groups of a user, the change will take effect the next time the user logs in. So, if you change a user's role in the Grafana Org. Users page, this change will be reset the next time the user logs in. If you change the LDAP groups of a user, the change will take effect the next time the user logs in.
### Priority between Multiple Mappings ### Priority between Multiple Mappings
......
--- ---
page_title: Installing on RPM-based Linux page_title: Installing on RPM-based Linux
page_description: Grafana Installation guide for Centos, Fedora, Redhat. page_description: Grafana Installation guide for Centos, Fedora, OpenSuse, Redhat.
page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
--- ---
...@@ -10,7 +10,7 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide ...@@ -10,7 +10,7 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download Description | Download
------------ | ------------- ------------ | -------------
.RPM for Fedora / RHEL / CentOS Linux | [grafana-2.1.3-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.1.3-1.x86_64.rpm) .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.1.3-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.1.3-1.x86_64.rpm)
## Install from package file ## Install from package file
...@@ -20,9 +20,15 @@ You can install Grafana using Yum directly. ...@@ -20,9 +20,15 @@ You can install Grafana using Yum directly.
Or install manually using `rpm`. Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig $ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-2.1.3-1.x86_64.rpm $ sudo rpm -Uvh grafana-2.1.3-1.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-2.1.3-1.x86_64.rpm
## Install via YUM Repository ## Install via YUM Repository
Add the following to a new file at `/etc/yum.repos.d/grafana.repo` Add the following to a new file at `/etc/yum.repos.d/grafana.repo`
......
...@@ -25,7 +25,7 @@ go get github.com/grafana/grafana ...@@ -25,7 +25,7 @@ go get github.com/grafana/grafana
``` ```
cd $GOPATH/src/github.com/grafana/grafana cd $GOPATH/src/github.com/grafana/grafana
go run build.go setup # (only needed once to install godep) go run build.go setup # (only needed once to install godep)
$GOPATH/bin/godep restore # (will pull down all golang lib dependecies in your current GOPATH) $GOPATH/bin/godep restore # (will pull down all golang lib dependencies in your current GOPATH)
go run build.go build # (or 'go build .') go run build.go build # (or 'go build .')
``` ```
......
...@@ -24,7 +24,7 @@ modify Organization details and options. ...@@ -24,7 +24,7 @@ modify Organization details and options.
<img src="/img/v2/admin_sidenav.png" class="right" style="margin-left: 15px"> <img src="/img/v2/admin_sidenav.png" class="right" style="margin-left: 15px">
As a Grafana Administrator, you have complete access to any Organization or User in that instance of Grafana. As a Grafana Administrator, you have complete access to any Organization or User in that instance of Grafana.
When performing actions as a Grafana admin, the sidebar will change it's apperance as below to indicate you are performing global server administration. When performing actions as a Grafana admin, the sidebar will change it's appearance as below to indicate you are performing global server administration.
From the Grafana Server Admin page, you can access the System Info page which summarizes all of the backend configuration settings of the Grafana server. From the Grafana Server Admin page, you can access the System Info page which summarizes all of the backend configuration settings of the Grafana server.
......
...@@ -8,7 +8,7 @@ page_keywords: grafana, dashlist, panel, documentation ...@@ -8,7 +8,7 @@ page_keywords: grafana, dashlist, panel, documentation
## Overview ## Overview
The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashbaords, a search query and/or dashboard tags. The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, a search query and/or dashboard tags.
<img class="no-shadow" src="/img/v2/dashboard_list_panels.png"> <img class="no-shadow" src="/img/v2/dashboard_list_panels.png">
......
...@@ -30,7 +30,7 @@ The drilldown section allows adding dynamic links to the panel that can link to ...@@ -30,7 +30,7 @@ The drilldown section allows adding dynamic links to the panel that can link to
or URLs or URLs
Each link has a title, a type and params. A link can be either a ``dashboard`` or ``absolute`` links. Each link has a title, a type and params. A link can be either a ``dashboard`` or ``absolute`` links.
If it is a dashboard links, the `dashboard` value must be the name of a dashbaord. If it's an If it is a dashboard links, the `dashboard` value must be the name of a dashboard. If it's an
`absolute` link, the URL is the URL to link. `absolute` link, the URL is the URL to link.
``params`` allows adding additional URL params to the links. The format is the ``name=value`` with ``params`` allows adding additional URL params to the links. The format is the ``name=value`` with
...@@ -127,7 +127,7 @@ If you have stack enabled you can select what the mouse hover feature should sho ...@@ -127,7 +127,7 @@ If you have stack enabled you can select what the mouse hover feature should sho
### Rendering ### Rendering
- ``Flot`` - Render the graphs in the browser using Flot (default) - ``Flot`` - Render the graphs in the browser using Flot (default)
- ``Graphite PNG`` - Render the graph on the server using graphites render API. - ``Graphite PNG`` - Render the graph on the server using graphite's render API.
### Tooltip ### Tooltip
......
page_title: Kayboard Shortcuts page_title: Keyboard Shortcuts
page_description: Kayboard Shortcuts for Grafana page_description: Keyboard Shortcuts for Grafana
page_keywords: grafana, export, import, documentation page_keywords: grafana, export, import, documentation
--- ---
......
...@@ -11,7 +11,7 @@ Dashboards can be searched by the dashboard name, filtered by one (or many) tags ...@@ -11,7 +11,7 @@ Dashboards can be searched by the dashboard name, filtered by one (or many) tags
<img class="no-shadow" src="/img/v2/dashboard_search.png"> <img class="no-shadow" src="/img/v2/dashboard_search.png">
1. `Dashboard Picker`: The Dashboard Picker is your primary navigation tool to move between dashboards. It is present on all dashboards, and open the Dashboard Search. The dashboard picker also doubles as the title of the current dashboard. 1. `Dashboard Picker`: The Dashboard Picker is your primary navigation tool to move between dashboards. It is present on all dashboards, and open the Dashboard Search. The dashboard picker also doubles as the title of the current dashboard.
2. `Search Bar`: The search bar allows you to enter any string and search both database and file based dashbaords in real-time. 2. `Search Bar`: The search bar allows you to enter any string and search both database and file based dashboards in real-time.
3. `Starred`: The starred link allows you to filter the list to display only starred dashboards. 3. `Starred`: The starred link allows you to filter the list to display only starred dashboards.
4. `Tags`: The tags filter allows you to filter the list by dashboard tags. 4. `Tags`: The tags filter allows you to filter the list by dashboard tags.
...@@ -25,14 +25,14 @@ To search and load dashboards click the open folder icon in the header or use th ...@@ -25,14 +25,14 @@ To search and load dashboards click the open folder icon in the header or use th
Dashboard search is: Dashboard search is:
- Real-time - Real-time
- *Not* case senstitive - *Not* case sensitive
- Functional across stored *and* file based dashboards. - Functional across stored *and* file based dashboards.
## Filter by Tag(s) ## Filter by Tag(s)
Tags are a great way to organize your dashboards, especially as the number of dashbaords grow. Tags can be added and managed in the dashboard `Settings`. Tags are a great way to organize your dashboards, especially as the number of dashboards grow. Tags can be added and managed in the dashboard `Settings`.
To filter the dashboard list by tag, click on any tag appearing in the right column. The list may be further filtered by cliking on additional tags: To filter the dashboard list by tag, click on any tag appearing in the right column. The list may be further filtered by clicking on additional tags:
<img class="no-shadow" src="/img/v2/dashboard_search_tag_filtering.gif"> <img class="no-shadow" src="/img/v2/dashboard_search_tag_filtering.gif">
...@@ -40,7 +40,7 @@ Alternately, to see a list of all available tags, click the tags link in the sea ...@@ -40,7 +40,7 @@ Alternately, to see a list of all available tags, click the tags link in the sea
<img class="no-shadow" src="/img/v2/dashboard_search_tags_all_filtering.gif"> <img class="no-shadow" src="/img/v2/dashboard_search_tags_all_filtering.gif">
When using only a keybaord: `tab` to focus on the *tags* link, `▼` down arrow key to find a tag and select with the `Enter` key. When using only a keyboard: `tab` to focus on the *tags* link, `▼` down arrow key to find a tag and select with the `Enter` key.
**Note**: When multiple tags are selected, Grafana will show dashboards that include **all**. **Note**: When multiple tags are selected, Grafana will show dashboards that include **all**.
...@@ -51,4 +51,4 @@ Starring is a great way to organize and find commonly used dashboards. To show o ...@@ -51,4 +51,4 @@ Starring is a great way to organize and find commonly used dashboards. To show o
<img class="no-shadow" src="/img/v2/dashboard_search_starred_filtering.gif"> <img class="no-shadow" src="/img/v2/dashboard_search_starred_filtering.gif">
When using only a keybaord: `tab` to focus on the *stars* link, `▼` down arrow key to find a tag and select with the `Enter` key. When using only a keyboard: `tab` to focus on the *stars* link, `▼` down arrow key to find a tag and select with the `Enter` key.
\ No newline at end of file \ No newline at end of file
...@@ -5,7 +5,7 @@ page_keywords: grafana, sharing, guide, documentation ...@@ -5,7 +5,7 @@ page_keywords: grafana, sharing, guide, documentation
--- ---
# Sharing features # Sharing features
Grafana provides a number of ways to share a dashboard or a specfic panel to other users within your Grafana provides a number of ways to share a dashboard or a specific panel to other users within your
organization. It also provides ways to publish interactive snapshots that can be accessed by external partners. organization. It also provides ways to publish interactive snapshots that can be accessed by external partners.
## Share dashboard ## Share dashboard
......
...@@ -17,11 +17,11 @@ The singlestat panel has a normal query editor to allow you define your exact me ...@@ -17,11 +17,11 @@ The singlestat panel has a normal query editor to allow you define your exact me
<img class="no-shadow" src="/img/v1/Singlestat-BaseSettings.png"> <img class="no-shadow" src="/img/v1/Singlestat-BaseSettings.png">
1. `Big Value`: Big Value refers to how we display the main stat for the Singlestat Panel. This is always a single value that is displayed in the Panel in between two strings, `Prefix` and `Suffix`. The single number is calculated by choosing a function (min,max,average,current,total) of your metric query. This functions reduces your query into a single numeric value. 1. `Big Value`: Big Value refers to how we display the main stat for the Singlestat Panel. This is always a single value that is displayed in the Panel in between two strings, `Prefix` and `Suffix`. The single number is calculated by choosing a function (min,max,average,current,total) of your metric query. This functions reduces your query into a single numeric value.
2. `Font Size`: You can use this section 2. `Font Size`: You can use this section to select the font size of the different texts in the Singlestat Panel, i.e. prefix, value and postfix.
3. `Values`: The Value fields let you set the function (min, max, average, current, total) that your entire query is reduced into a single value with. You can also set the font size of theand font-size (as a %) of the metric query that the Panel is configured with. This reduces the entire query into a single summary value that is displayed. 3. `Values`: The Value fields let you set the function (min, max, average, current, total) that your entire query is reduced into a single value with. You can also set the font size of the Value field and font-size (as a %) of the metric query that the Panel is configured with. This reduces the entire query into a single summary value that is displayed.
4. `Postfixes`: The Postfix fields let you define a custom label and font-size (as a %) to appear *after* the value 4. `Postfixes`: The Postfix fields let you define a custom label and font-size (as a %) to appear *after* the value
5. `Units`: Units are appended to the the Singlestat within the panel, and will respect the color and threshold settings for the value. 5. `Units`: Units are appended to the the Singlestat within the panel, and will respect the color and threshold settings for the value.
6. `Decimals`: The Decimal field allows you to override the automatic decimal precision, and set it explicitely. 6. `Decimals`: The Decimal field allows you to override the automatic decimal precision, and set it explicitly.
### Coloring ### Coloring
...@@ -29,9 +29,9 @@ The coloring options of the Singlestat Panel config allow you to dynamically cha ...@@ -29,9 +29,9 @@ The coloring options of the Singlestat Panel config allow you to dynamically cha
<img class="no-shadow" src="/img/v1/Singlestat-Coloring.png"> <img class="no-shadow" src="/img/v1/Singlestat-Coloring.png">
1. `Background`: This checkbox applies the configured thresholds and colors to the entirity of the Singlestat Panel background. 1. `Background`: This checkbox applies the configured thresholds and colors to the entirety of the Singlestat Panel background.
2. `Value`: This checkbox applies the configured thresholds and colors to the summary stat. 2. `Value`: This checkbox applies the configured thresholds and colors to the summary stat.
3. `Thresholds`: Change the background and value colors dyanmically within the panel, depending on the Singlestat value. The threshold field accepts **3 comma-separated** values, corresponding to the three colors directly to the right. 3. `Thresholds`: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **3 comma-separated** values, corresponding to the three colors directly to the right.
4. `Colors`: Select a color and opacity 4. `Colors`: Select a color and opacity
5. `Invert order`: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/v1/ryg.png">). 5. `Invert order`: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/v1/ryg.png">).
......
...@@ -63,7 +63,7 @@ Once configured, Multi-Select Tagging provides a convenient way to group and you ...@@ -63,7 +63,7 @@ Once configured, Multi-Select Tagging provides a convenient way to group and you
### Interval ### Interval
Use the `Interval` type to create Template variables aroundr time ranges (eg. `1m`,`1h`, `1d`). There is also a special `auto` option that will change depending on the current time range, you can specify how many times the current time range should be divided to calculate the current `auto` range. Use the `Interval` type to create Template variables around time ranges (eg. `1m`,`1h`, `1d`). There is also a special `auto` option that will change depending on the current time range, you can specify how many times the current time range should be divided to calculate the current `auto` range.
![](/img/v2/templated_variable_parameter.png) ![](/img/v2/templated_variable_parameter.png)
...@@ -75,7 +75,7 @@ Use the `Custom` type to manually create Template variables around explicit valu ...@@ -75,7 +75,7 @@ Use the `Custom` type to manually create Template variables around explicit valu
Template Variables can be very useful to dynamically change what you're visualizing on a given panel. Sometimes, you might want to create entire new Panels (or Rows) based on what Template Variables have been selected. This is now possible in Grafana 2.1. Template Variables can be very useful to dynamically change what you're visualizing on a given panel. Sometimes, you might want to create entire new Panels (or Rows) based on what Template Variables have been selected. This is now possible in Grafana 2.1.
Once you've got your Template variables (of any type) configured the way you'd like, check out the Repeating Panels and Repeating Row documentatione Once you've got your Template variables (of any type) configured the way you'd like, check out the Repeating Panels and Repeating Row documentation
## Screencast - Templated Graphite Queries ## Screencast - Templated Graphite Queries
......
...@@ -64,13 +64,13 @@ The `hubot-grafana` plugin requires a number of environment variables to be set ...@@ -64,13 +64,13 @@ The `hubot-grafana` plugin requires a number of environment variables to be set
The hubot plugin will take advantage of the Grafana server side rendering feature that can The hubot plugin will take advantage of the Grafana server side rendering feature that can
render any panel on the server using phantomjs. Grafana ships with a phantomjs binary (linux only). render any panel on the server using phantomjs. Grafana ships with a phantomjs binary (linux only).
To verify that this freature works try the `Direct link to rendered image` link in the panel share dialog. To verify that this feature works try the `Direct link to rendered image` link in the panel share dialog.
If you do not get an image when opening this link verify that the required font packages are installed for phantomjs to work. If you do not get an image when opening this link verify that the required font packages are installed for phantomjs to work.
### Grafana API Key ### Grafana API Key
<img src="/img/v2/orgdropdown_api_keys.png" style="width: 150px" class="right"></img> <img src="/img/v2/orgdropdown_api_keys.png" style="width: 150px" class="right"></img>
You need to set the environment variable `HUBOT_GRAFANA_API_KEY` to a Grafana API Key. You need to set the environment variable `HUBOT_GRAFANA_API_KEY` to a Grafana API Key.
You can add these from the API Keys page wich you find in the Organization dropdown. You can add these from the API Keys page which you find in the Organization dropdown.
### Amazon S3 ### Amazon S3
The `S3` options are optional but for the images to work properly in services like Slack and Hipchat they need The `S3` options are optional but for the images to work properly in services like Slack and Hipchat they need
...@@ -118,7 +118,7 @@ Now you can add an alias like this: ...@@ -118,7 +118,7 @@ Now you can add an alias like this:
## Summary ## Summary
Grafana is going to ship with integrated Slack and Hiptchat features some day but you do Grafana is going to ship with integrated Slack and Hipchat features some day but you do
not have to wait for that. Grafana 2 shipped with a very clever server side rendering feature not have to wait for that. Grafana 2 shipped with a very clever server side rendering feature
that can render any panel to a png using phantomjs. The hubot plugin for Grafana is something that can render any panel to a png using phantomjs. The hubot plugin for Grafana is something
you can install and use today! you can install and use today!
......
...@@ -14,7 +14,7 @@ ExecStart=/usr/sbin/grafana-server \ ...@@ -14,7 +14,7 @@ ExecStart=/usr/sbin/grafana-server \
--config=${CONF_FILE} \ --config=${CONF_FILE} \
--pidfile=${PID_FILE} \ --pidfile=${PID_FILE} \
cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.logs=${LOG_DIR} \
cfg:default.paths.data=${DATA_DIR} \ cfg:default.paths.data=${DATA_DIR}
LimitNOFILE=10000 LimitNOFILE=10000
TimeoutStopSec=20 TimeoutStopSec=20
UMask=0027 UMask=0027
......
...@@ -14,7 +14,7 @@ ExecStart=/usr/sbin/grafana-server \ ...@@ -14,7 +14,7 @@ ExecStart=/usr/sbin/grafana-server \
--config=${CONF_FILE} \ --config=${CONF_FILE} \
--pidfile=${PID_FILE} \ --pidfile=${PID_FILE} \
cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.logs=${LOG_DIR} \
cfg:default.paths.data=${DATA_DIR} \ cfg:default.paths.data=${DATA_DIR}
LimitNOFILE=10000 LimitNOFILE=10000
TimeoutStopSec=20 TimeoutStopSec=20
......
...@@ -40,7 +40,9 @@ func Register(r *macaron.Macaron) { ...@@ -40,7 +40,9 @@ func Register(r *macaron.Macaron) {
r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index) r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index)
r.Get("/admin/orgs", reqGrafanaAdmin, Index) r.Get("/admin/orgs", reqGrafanaAdmin, Index)
r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index) r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index)
r.Get("/dashboard/*", reqSignedIn, Index) r.Get("/dashboard/*", reqSignedIn, Index)
r.Get("/dashboard-solo/*", reqSignedIn, Index)
// sign up // sign up
r.Get("/signup", Index) r.Get("/signup", Index)
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/Unknwon/macaron" "github.com/Unknwon/macaron"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
) )
func Logger() macaron.Handler { func Logger() macaron.Handler {
...@@ -36,7 +37,9 @@ func Logger() macaron.Handler { ...@@ -36,7 +37,9 @@ func Logger() macaron.Handler {
switch rw.Status() { switch rw.Status() {
case 200, 304: case 200, 304:
content = fmt.Sprintf("%s", content) content = fmt.Sprintf("%s", content)
if !setting.RouterLogging {
return return
}
case 404: case 404:
content = fmt.Sprintf("%s", content) content = fmt.Sprintf("%s", content)
case 500: case 500:
......
...@@ -91,7 +91,6 @@ func (scanner *PluginScanner) loadPluginJson(path string) error { ...@@ -91,7 +91,6 @@ func (scanner *PluginScanner) loadPluginJson(path string) error {
if !exists { if !exists {
return errors.New("Did not find type property in plugin.json") return errors.New("Did not find type property in plugin.json")
} }
DataSources[datasourceType.(string)] = pluginJson DataSources[datasourceType.(string)] = pluginJson
} }
......
...@@ -100,13 +100,8 @@ function (angular, $, _, appLevelRequire) { ...@@ -100,13 +100,8 @@ function (angular, $, _, appLevelRequire) {
var $scope = this; var $scope = this;
$scope.requireContext(deps, function () { $scope.requireContext(deps, function () {
var deps = _.toArray(arguments); var deps = _.toArray(arguments);
// Check that this is a valid scope.
if($scope.$id) {
$scope.$apply(function () {
fn.apply($scope, deps); fn.apply($scope, deps);
}); });
}
});
}; };
}]); }]);
}); });
......
...@@ -328,11 +328,22 @@ function (angular, $, kbn, _, moment) { ...@@ -328,11 +328,22 @@ function (angular, $, kbn, _, moment) {
} }
} }
if (oldVersion < 7 && old.nav && old.nav.length) { if (oldVersion < 7) {
if (old.nav && old.nav.length) {
this.timepicker = old.nav[0]; this.timepicker = old.nav[0];
delete this.nav; delete this.nav;
} }
// ensure query refIds
panelUpgrades.push(function(panel) {
_.each(panel.targets, function(target) {
if (!target.refId) {
target.refId = this.getNextQueryLetter(panel);
}
}, this);
});
}
if (panelUpgrades.length === 0) { if (panelUpgrades.length === 0) {
return; return;
} }
...@@ -341,7 +352,7 @@ function (angular, $, kbn, _, moment) { ...@@ -341,7 +352,7 @@ function (angular, $, kbn, _, moment) {
var row = this.rows[i]; var row = this.rows[i];
for (j = 0; j < row.panels.length; j++) { for (j = 0; j < row.panels.length; j++) {
for (k = 0; k < panelUpgrades.length; k++) { for (k = 0; k < panelUpgrades.length; k++) {
panelUpgrades[k](row.panels[j]); panelUpgrades[k].call(this, row.panels[j]);
} }
} }
} }
......
...@@ -75,7 +75,7 @@ function (angular, _, require, config) { ...@@ -75,7 +75,7 @@ function (angular, _, require, config) {
$scope.iframeHtml = '<iframe src="' + soloUrl + '" width="450" height="200" frameborder="0"></iframe>'; $scope.iframeHtml = '<iframe src="' + soloUrl + '" width="450" height="200" frameborder="0"></iframe>';
$scope.imageUrl = soloUrl.replace('/dashboard', '/render/dashboard'); $scope.imageUrl = soloUrl.replace('/dashboard-solo/', '/render/dashboard-solo/');
$scope.imageUrl += '&width=1000'; $scope.imageUrl += '&width=1000';
$scope.imageUrl += '&height=500'; $scope.imageUrl += '&height=500';
}; };
......
...@@ -26,7 +26,7 @@ function (angular, $) { ...@@ -26,7 +26,7 @@ function (angular, $) {
$scope.initPanelScope = function() { $scope.initPanelScope = function() {
$scope.row = { $scope.row = {
height: ($(window).height() - 10) + 'px', height: $(window).height() + 'px',
}; };
$scope.test = "Hej"; $scope.test = "Hej";
......
...@@ -48,8 +48,22 @@ function (angular, kbn, _) { ...@@ -48,8 +48,22 @@ function (angular, kbn, _) {
return url; return url;
} }
url += (url.indexOf('?') !== -1 ? '&' : '?'); return this.appendToQueryString(url, paramsArray.join('&'));
return url + paramsArray.join('&'); };
this.appendToQueryString = function(url, stringToAppend) {
if (!_.isUndefined(stringToAppend) && stringToAppend !== null && stringToAppend !== '') {
var pos = url.indexOf('?');
if (pos !== -1) {
if (url.length - pos > 1) {
url += '&';
}
} else {
url += '?';
}
url += stringToAppend;
}
return url;
}; };
this.getAnchorInfo = function(link) { this.getAnchorInfo = function(link) {
...@@ -65,7 +79,6 @@ function (angular, kbn, _) { ...@@ -65,7 +79,6 @@ function (angular, kbn, _) {
info.target = link.targetBlank ? '_blank' : '_self'; info.target = link.targetBlank ? '_blank' : '_self';
info.href = templateSrv.replace(link.url || '', scopedVars); info.href = templateSrv.replace(link.url || '', scopedVars);
info.title = templateSrv.replace(link.title || '', scopedVars); info.title = templateSrv.replace(link.title || '', scopedVars);
info.href += '?';
} }
else if (link.dashUri) { else if (link.dashUri) {
info.href = 'dashboard/' + link.dashUri + '?'; info.href = 'dashboard/' + link.dashUri + '?';
...@@ -91,8 +104,9 @@ function (angular, kbn, _) { ...@@ -91,8 +104,9 @@ function (angular, kbn, _) {
} }
info.href = this.addParamsToUrl(info.href, params); info.href = this.addParamsToUrl(info.href, params);
if (link.params) { if (link.params) {
info.href += "&" + templateSrv.replace(link.params, scopedVars); info.href = this.appendToQueryString(info.href, templateSrv.replace(link.params, scopedVars));
} }
return info; return info;
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
<div class="row-text pointer" ng-click="toggle_row(row)" ng-if="row.showTitle" ng-bind="row.title | interpolateTemplateVars:this"> <div class="row-text pointer" ng-click="toggle_row(row)" ng-if="row.showTitle" ng-bind="row.title | interpolateTemplateVars:this">
</div> </div>
<div ng-repeat="(name, panel) in row.panels track by panel.id" class="panel" ui-draggable="{{!dashboardViewState.fullscreen}}" drag="panel.id" <div ng-repeat="(name, panel) in row.panels track by panel.id" class="panel" ui-draggable="!dashboardViewState.fullscreen" drag="panel.id"
ui-on-Drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width> ui-on-Drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
<panel-loader type="panel.type" class="panel-margin"></panel-loader> <panel-loader type="panel.type" class="panel-margin"></panel-loader>
</div> </div>
......
...@@ -159,7 +159,10 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -159,7 +159,10 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
if (target.hide) {return;} if (target.hide) {return;}
var esQuery = angular.toJson(this.queryBuilder.build(target)); var esQuery = angular.toJson(this.queryBuilder.build(target));
esQuery = esQuery.replace("$lucene_query", target.query || '*'); var luceneQuery = angular.toJson(target.query || '*');
// remove inner quotes
luceneQuery = luceneQuery.substr(1, luceneQuery.length - 2);
esQuery = esQuery.replace("$lucene_query", luceneQuery);
payload += header + '\n' + esQuery + '\n'; payload += header + '\n' + esQuery + '\n';
sentTargets.push(target); sentTargets.push(target);
......
...@@ -20,7 +20,7 @@ function (_, moment) { ...@@ -20,7 +20,7 @@ function (_, moment) {
IndexPattern.prototype.getIndexForToday = function() { IndexPattern.prototype.getIndexForToday = function() {
if (this.interval) { if (this.interval) {
return moment().format(this.pattern); return moment.utc().format(this.pattern);
} else { } else {
return this.pattern; return this.pattern;
} }
......
<section class="grafana-metric-options"> <section class="grafana-metric-options">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item tight-form-item-icon">
<i class="fa fa-wrench"></i>
</li>
<li class="tight-form-item">
Group by time interval
</li>
<li>
<input type="text" class="input-medium tight-form-input" ng-model="panel.interval" ng-blur="get_data();"
spellcheck='false' placeholder="example: >10s">
</li>
<li class="tight-form-item">
<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >60s'" data-placement="right"></i>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form last"> <div class="tight-form last">
<ul class="tight-form-list"> <ul class="tight-form-list">
<li class="tight-form-item tight-form-item-icon"> <li class="tight-form-item tight-form-item-icon">
......
...@@ -15,15 +15,14 @@ function (angular) { ...@@ -15,15 +15,14 @@ function (angular) {
}; };
ElasticQueryBuilder.prototype.buildTermsAgg = function(aggDef, queryNode, target) { ElasticQueryBuilder.prototype.buildTermsAgg = function(aggDef, queryNode, target) {
var metricRef, metric, size, y; var metricRef, metric, y;
queryNode.terms = { "field": aggDef.field }; queryNode.terms = { "field": aggDef.field };
if (!aggDef.settings) { if (!aggDef.settings) {
return queryNode; return queryNode;
} }
size = parseInt(aggDef.settings.size, 10); queryNode.terms.size = parseInt(aggDef.settings.size, 10);
if (size > 0) { queryNode.terms.size = size; }
if (aggDef.settings.orderBy !== void 0) { if (aggDef.settings.orderBy !== void 0) {
queryNode.terms.order = {}; queryNode.terms.order = {};
queryNode.terms.order[aggDef.settings.orderBy] = aggDef.settings.order; queryNode.terms.order[aggDef.settings.orderBy] = aggDef.settings.order;
......
...@@ -36,22 +36,21 @@ describe('ElasticDatasource', function() { ...@@ -36,22 +36,21 @@ describe('ElasticDatasource', function() {
ctx.ds.testDatasource(); ctx.ds.testDatasource();
ctx.$rootScope.$apply(); ctx.$rootScope.$apply();
var today = moment().format("YYYY.MM.DD"); var today = moment.utc().format("YYYY.MM.DD");
expect(requestOptions.url).to.be("http://es.com/asd-" + today + '/_stats'); expect(requestOptions.url).to.be("http://es.com/asd-" + today + '/_stats');
}); });
}); });
describe('When issueing metric query with interval pattern', function() { describe('When issueing metric query with interval pattern', function() {
var requestOptions, parts, header;
beforeEach(function() { beforeEach(function() {
ctx.ds = new ctx.service({ ctx.ds = new ctx.service({
url: 'http://es.com', url: 'http://es.com',
index: '[asd-]YYYY.MM.DD', index: '[asd-]YYYY.MM.DD',
jsonData: { interval: 'Daily' } jsonData: { interval: 'Daily' }
}); });
});
it('should translate index pattern to current day', function() {
var requestOptions;
ctx.backendSrv.datasourceRequest = function(options) { ctx.backendSrv.datasourceRequest = function(options) {
requestOptions = options; requestOptions = options;
return ctx.$q.when({data: {responses: []}}); return ctx.$q.when({data: {responses: []}});
...@@ -62,13 +61,22 @@ describe('ElasticDatasource', function() { ...@@ -62,13 +61,22 @@ describe('ElasticDatasource', function() {
from: moment([2015, 4, 30, 10]), from: moment([2015, 4, 30, 10]),
to: moment([2015, 5, 1, 10]) to: moment([2015, 5, 1, 10])
}, },
targets: [{ bucketAggs: [], metrics: [] }] targets: [{ bucketAggs: [], metrics: [], query: 'escape\\:test' }]
}); });
ctx.$rootScope.$apply(); ctx.$rootScope.$apply();
var parts = requestOptions.data.split('\n');
var header = angular.fromJson(parts[0]); parts = requestOptions.data.split('\n');
header = angular.fromJson(parts[0]);
});
it('should translate index pattern to current day', function() {
expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']); expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']);
}); });
it('should json escape lucene query', function() {
var body = angular.fromJson(parts[1]);
expect(body.query.filtered.query.query_string.query).to.be('escape\\:test');
});
}); });
}); });
...@@ -11,7 +11,7 @@ describe('IndexPattern', function() { ...@@ -11,7 +11,7 @@ describe('IndexPattern', function() {
describe('when getting index for today', function() { describe('when getting index for today', function() {
it('should return correct index name', function() { it('should return correct index name', function() {
var pattern = new IndexPattern('[asd-]YYYY.MM.DD', 'Daily'); var pattern = new IndexPattern('[asd-]YYYY.MM.DD', 'Daily');
var expected = 'asd-' + moment().format('YYYY.MM.DD'); var expected = 'asd-' + moment.utc().format('YYYY.MM.DD');
expect(pattern.getIndexForToday()).to.be(expected); expect(pattern.getIndexForToday()).to.be(expected);
}); });
......
...@@ -75,7 +75,7 @@ function (angular, _, $, config, dateMath) { ...@@ -75,7 +75,7 @@ function (angular, _, $, config, dateMath) {
if (annotation.target) { if (annotation.target) {
var target = templateSrv.replace(annotation.target); var target = templateSrv.replace(annotation.target);
var graphiteQuery = { var graphiteQuery = {
range: rangeUnparsed, rangeRaw: rangeUnparsed,
targets: [{ target: target }], targets: [{ target: target }],
format: 'json', format: 'json',
maxDataPoints: 100 maxDataPoints: 100
......
...@@ -12,7 +12,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { ...@@ -12,7 +12,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
var module = angular.module('grafana.services'); var module = angular.module('grafana.services');
module.factory('InfluxDatasource', function($q, $http, templateSrv) { module.factory('InfluxDatasource', function($q, backendSrv, templateSrv) {
function InfluxDatasource(datasource) { function InfluxDatasource(datasource) {
this.type = 'influxdb'; this.type = 'influxdb';
...@@ -78,7 +78,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { ...@@ -78,7 +78,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
}; };
InfluxDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) { InfluxDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) {
var timeFilter = getTimeFilter({ range: rangeUnparsed }); var timeFilter = getTimeFilter({ rangeRaw: rangeUnparsed });
var query = annotation.query.replace('$timeFilter', timeFilter); var query = annotation.query.replace('$timeFilter', timeFilter);
query = templateSrv.replace(query); query = templateSrv.replace(query);
...@@ -161,7 +161,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { ...@@ -161,7 +161,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
options.headers.Authorization = self.basicAuth; options.headers.Authorization = self.basicAuth;
} }
return $http(options).then(function(result) { return backendSrv.datasourceRequest(options).then(function(result) {
return result.data; return result.data;
}, function(err) { }, function(err) {
if (err.status !== 0 || err.status >= 300) { if (err.status !== 0 || err.status >= 300) {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div class="editor-row"> <div class="editor-row">
<div class="section"> <div class="section">
<h5>Column mappings <tip>If your influxdb query returns more than one column you need to specify the column names bellow. An annotation event is composed of a title, tags, and an additional text field.</tip></h5> <h5>Column mappings <tip>If your influxdb query returns more than one column you need to specify the column names below. An annotation event is composed of a title, tags, and an additional text field.</tip></h5>
<div class="editor-option"> <div class="editor-option">
<label class="small">Title</label> <label class="small">Title</label>
<input type="text" class="input-small" ng-model='currentAnnotation.titleColumn' placeholder=""></input> <input type="text" class="input-small" ng-model='currentAnnotation.titleColumn' placeholder=""></input>
......
...@@ -2,11 +2,11 @@ define([ ...@@ -2,11 +2,11 @@ define([
'angular', 'angular',
'lodash', 'lodash',
'app/core/utils/datemath', 'app/core/utils/datemath',
'./influxSeries', './influx_series',
'./queryBuilder', './query_builder',
'./directives', './directives',
'./queryCtrl', './query_ctrl',
'./funcEditor', './func_editor',
], ],
function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
'use strict'; 'use strict';
...@@ -266,11 +266,11 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { ...@@ -266,11 +266,11 @@ function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
} }
function getInfluxTime(date, roundUp) { function getInfluxTime(date, roundUp) {
if (_.isString(date) && date.indexOf('/') === -1) { if (_.isString(date)) {
if (date === 'now') { if (date === 'now') {
return 'now()'; return 'now()';
} }
if (date.indexOf('now-') >= 0) { if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) {
return date.replace('now', 'now()'); return date.replace('now', 'now()');
} }
date = dateMath.parse(date, roundUp); date = dateMath.parse(date, roundUp);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div class="editor-row"> <div class="editor-row">
<div class="section"> <div class="section">
<h5>Column mappings <tip>If your influxdb query returns more than one column you need to specify the column names bellow. An annotation event is composed of a title, tags, and an additional text field.</tip></h5> <h5>Column mappings <tip>If your influxdb query returns more than one column you need to specify the column names below. An annotation event is composed of a title, tags, and an additional text field.</tip></h5>
<div class="editor-option"> <div class="editor-option">
<label class="small">Title</label> <label class="small">Title</label>
<input type="text" class="input-small" ng-model='currentAnnotation.titleColumn' placeholder=""></input> <input type="text" class="input-small" ng-model='currentAnnotation.titleColumn' placeholder=""></input>
......
...@@ -89,6 +89,7 @@ function (angular) { ...@@ -89,6 +89,7 @@ function (angular) {
} }
}; };
}); $scope.init();
});
}); });
define([ ///<amd-dependency path="app/plugins/datasource/influxdb_08/datasource"/>
'./helpers', ///<amd-dependency path="app/services/backendSrv"/>
'app/plugins/datasource/influxdb_08/datasource', ///<amd-dependency path="app/services/alertSrv"/>
'app/services/backendSrv', ///<amd-dependency path="test/specs/helpers" name="helpers" />
'app/services/alertSrv'
], function(helpers) { import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
'use strict';
declare var helpers: any;
describe('InfluxDatasource', function() {
describe('InfluxDatasource', function() {
var ctx = new helpers.ServiceTestContext(); var ctx = new helpers.ServiceTestContext();
beforeEach(module('grafana.services')); beforeEach(angularMocks.module('grafana.services'));
beforeEach(ctx.providePhase(['templateSrv'])); beforeEach(ctx.providePhase(['templateSrv']));
beforeEach(ctx.createService('InfluxDatasource_08')); beforeEach(ctx.createService('InfluxDatasource_08'));
beforeEach(function() { beforeEach(function() {
...@@ -18,8 +19,7 @@ define([ ...@@ -18,8 +19,7 @@ define([
describe('When querying influxdb with one target using query editor target spec', function() { describe('When querying influxdb with one target using query editor target spec', function() {
var results; var results;
var urlExpected = "/series?p=mupp&q=select+mean(value)+from+%22test%22"+ var urlExpected = "/series?p=mupp&q=select+mean(value)+from+%22test%22+where+time+%3E+now()-1h+group+by+time(1s)+order+asc";
"+where+time+%3E+now()-1h+group+by+time(1s)+order+asc";
var query = { var query = {
rangeRaw: { from: 'now-1h', to: 'now' }, rangeRaw: { from: 'now-1h', to: 'now' },
targets: [{ series: 'test', column: 'value', function: 'mean' }], targets: [{ series: 'test', column: 'value', function: 'mean' }],
...@@ -51,8 +51,7 @@ define([ ...@@ -51,8 +51,7 @@ define([
describe('When querying influxdb with one raw query', function() { describe('When querying influxdb with one raw query', function() {
var results; var results;
var urlExpected = "/series?p=mupp&q=select+value+from+series"+ var urlExpected = "/series?p=mupp&q=select+value+from+series+where+time+%3E+now()-1h";
"+where+time+%3E+now()-1h";
var query = { var query = {
rangeRaw: { from: 'now-1h', to: 'now' }, rangeRaw: { from: 'now-1h', to: 'now' },
targets: [{ query: "select value from series where $timeFilter", rawQuery: true }] targets: [{ query: "select value from series where $timeFilter", rawQuery: true }]
...@@ -74,8 +73,7 @@ define([ ...@@ -74,8 +73,7 @@ define([
describe('When issuing annotation query', function() { describe('When issuing annotation query', function() {
var results; var results;
var urlExpected = "/series?p=mupp&q=select+title+from+events.backend_01"+ var urlExpected = "/series?p=mupp&q=select+title+from+events.backend_01+where+time+%3E+now()-1h";
"+where+time+%3E+now()-1h";
var range = { from: 'now-1h', to: 'now' }; var range = { from: 'now-1h', to: 'now' };
var annotation = { query: 'select title from events.$server where $timeFilter' }; var annotation = { query: 'select title from events.$server where $timeFilter' };
...@@ -96,6 +94,4 @@ define([ ...@@ -96,6 +94,4 @@ define([
}); });
});
}); });
define([ ///<amd-dependency path="app/plugins/datasource/influxdb_08/influx_series" name="InfluxSeries"/>
'app/plugins/datasource/influxdb_08/influxSeries'
], function(InfluxSeries) {
'use strict';
describe('when generating timeseries from influxdb response', function() { import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
declare var InfluxSeries: any;
describe('when generating timeseries from influxdb response', function() {
describe('given two series', function() { describe('given two series', function() {
var series = new InfluxSeries({ var series = new InfluxSeries({
...@@ -158,9 +159,9 @@ define([ ...@@ -158,9 +159,9 @@ define([
}); });
}); });
describe("when creating annotations from influxdb response", function() { describe("when creating annotations from influxdb response", function() {
describe('given column mapping for all columns', function() { describe('given column mapping for all columns', function() {
var series = new InfluxSeries({ var series = new InfluxSeries({
seriesList: [ seriesList: [
...@@ -215,6 +216,5 @@ define([ ...@@ -215,6 +216,5 @@ define([
}); });
});
}); });
define([ ///<amd-dependency path="app/plugins/datasource/influxdb_08/query_builder" name="InfluxQueryBuilder"/>
'app/plugins/datasource/influxdb_08/queryBuilder'
], function(InfluxQueryBuilder) {
'use strict';
describe('InfluxQueryBuilder', function() { import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
declare var InfluxQueryBuilder: any;
describe('InfluxQueryBuilder', function() {
describe('series with conditon and group by', function() { describe('series with conditon and group by', function() {
var builder = new InfluxQueryBuilder({ var builder = new InfluxQueryBuilder({
...@@ -73,6 +74,5 @@ define([ ...@@ -73,6 +74,5 @@ define([
}); });
});
}); });
...@@ -152,7 +152,10 @@ define([ ...@@ -152,7 +152,10 @@ define([
rows: [ rows: [
{ {
panels: [ panels: [
{type: 'graphite', legend: true, aliasYAxis: { test: 2 }, grid: { min: 1, max: 10 }} {
type: 'graphite', legend: true, aliasYAxis: { test: 2 }, grid: { min: 1, max: 10 },
targets: [{refId: 'A'}, {}],
}
] ]
} }
] ]
...@@ -178,6 +181,10 @@ define([ ...@@ -178,6 +181,10 @@ define([
expect(graph.type).to.be('graph'); expect(graph.type).to.be('graph');
}); });
it('queries without refId should get it', function() {
expect(graph.targets[1].refId).to.be('B');
});
it('update legend setting', function() { it('update legend setting', function() {
expect(graph.legend.show).to.be(true); expect(graph.legend.show).to.be(true);
}); });
......
...@@ -38,9 +38,10 @@ define([ ...@@ -38,9 +38,10 @@ define([
}; };
this.createControllerPhase = function(controllerName) { this.createControllerPhase = function(controllerName) {
return inject(function($controller, $rootScope, $q, $location) { return inject(function($controller, $rootScope, $q, $location, $browser) {
self.scope = $rootScope.$new(); self.scope = $rootScope.$new();
self.$location = $location; self.$location = $location;
self.$browser = $browser;
self.scope.contextSrv = {}; self.scope.contextSrv = {};
self.scope.panel = {}; self.scope.panel = {};
self.scope.row = { panels:[] }; self.scope.row = { panels:[] };
......
...@@ -4,6 +4,25 @@ define([ ...@@ -4,6 +4,25 @@ define([
], function(kbn, dateMath) { ], function(kbn, dateMath) {
'use strict'; 'use strict';
describe('unit format menu', function() {
var menu = kbn.getUnitFormats();
menu.map(function(submenu) {
describe('submenu ' + submenu.text, function() {
it('should have a title', function() { expect(submenu.text).to.be.a('string'); });
it('should have a submenu', function() { expect(submenu.submenu).to.be.an('array'); });
submenu.submenu.map(function(entry) {
describe('entry ' + entry.text, function() {
it('should have a title', function() { expect(entry.text).to.be.a('string'); });
it('should have a format', function() { expect(entry.value).to.be.a('string'); });
it('should have a valid format', function() {
expect(kbn.valueFormats[entry.value]).to.be.a('function');
});
});
});
});
});
});
function describeValueFormat(desc, value, tickSize, tickDecimals, result) { function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
describe('value format: ' + desc, function() { describe('value format: ' + desc, function() {
...@@ -26,6 +45,18 @@ define([ ...@@ -26,6 +45,18 @@ define([
describeValueFormat('none', 2.75e-10, 0, 10, '3e-10'); describeValueFormat('none', 2.75e-10, 0, 10, '3e-10');
describeValueFormat('none', 0, 0, 2, '0'); describeValueFormat('none', 0, 0, 2, '0');
describeValueFormat('dB', 10, 1000, 2, '10.00 dB');
describeValueFormat('percent', 0, 0, 0, '0%');
describeValueFormat('percent', 53, 0, 1, '53.0%');
describeValueFormat('percentunit', 0.0, 0, 0, '0%');
describeValueFormat('percentunit', 0.278, 0, 1, '27.8%');
describeValueFormat('percentunit', 1.0, 0, 0, '100%');
describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42');
describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K');
describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M');
describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA'); describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA');
describeValueFormat('ns', 25, 1, 0, '25 ns'); describeValueFormat('ns', 25, 1, 0, '25 ns');
......
define([
'lodash',
'app/features/panellinks/linkSrv'
], function(_) {
'use strict';
describe('linkSrv', function() {
var _linkSrv;
beforeEach(module('grafana.services'));
beforeEach(inject(function(linkSrv) {
_linkSrv = linkSrv;
}));
describe('when appending query strings', function() {
it('add ? to URL if not present', function() {
var url = _linkSrv.appendToQueryString('http://example.com', 'foo=bar');
expect(url).to.be('http://example.com?foo=bar');
});
it('do not add & to URL if ? is present but query string is empty', function() {
var url = _linkSrv.appendToQueryString('http://example.com?', 'foo=bar');
expect(url).to.be('http://example.com?foo=bar');
});
it('add & to URL if query string is present', function() {
var url = _linkSrv.appendToQueryString('http://example.com?foo=bar', 'hello=world');
expect(url).to.be('http://example.com?foo=bar&hello=world');
});
it('do not change the URL if there is nothing to append', function() {
_.each(['', undefined, null], function(toAppend) {
var url1 = _linkSrv.appendToQueryString('http://example.com', toAppend);
expect(url1).to.be('http://example.com');
var url2 = _linkSrv.appendToQueryString('http://example.com?', toAppend);
expect(url2).to.be('http://example.com?');
var url3 = _linkSrv.appendToQueryString('http://example.com?foo=bar', toAppend);
expect(url3).to.be('http://example.com?foo=bar');
});
});
});
});
});
...@@ -31,6 +31,17 @@ define([ ...@@ -31,6 +31,17 @@ define([
expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=1000&to=2000&panelId=22&fullscreen'); expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=1000&to=2000&panelId=22&fullscreen');
}); });
it('should generate render url', function() {
ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/db/my-dash';
ctx.scope.panel = { id: 22 };
ctx.scope.init();
var base = 'http://dashboards.grafana.com/render/dashboard-solo/db/my-dash';
var params = '?from=1000&to=2000&panelId=22&fullscreen&width=1000&height=500';
expect(ctx.scope.imageUrl).to.be(base + params);
});
it('should remove panel id when no panel in scope', function() { it('should remove panel id when no panel in scope', function() {
ctx.$location.path('/test'); ctx.$location.path('/test');
ctx.scope.options.forCurrent = true; ctx.scope.options.forCurrent = true;
......
...@@ -23,7 +23,6 @@ module.exports = function(grunt) { ...@@ -23,7 +23,6 @@ module.exports = function(grunt) {
'filerev', 'filerev',
'remapFilerev', 'remapFilerev',
'usemin', 'usemin',
'clean:temp',
'uglify:genDir' 'uglify:genDir'
]); ]);
......
...@@ -24,7 +24,6 @@ module.exports = function(config) { ...@@ -24,7 +24,6 @@ module.exports = function(config) {
], ],
dest: '<%= genDir %>/css/grafana.light.min.css' dest: '<%= genDir %>/css/grafana.light.min.css'
}, },
js: { js: {
src: [ src: [
'<%= tempDir %>/vendor/requirejs/require.js', '<%= tempDir %>/vendor/requirejs/require.js',
......
...@@ -6,7 +6,7 @@ module.exports = function(config) { ...@@ -6,7 +6,7 @@ module.exports = function(config) {
dest: '<%= genDir %>/app/components/partials.js', dest: '<%= genDir %>/app/components/partials.js',
options: { options: {
bootstrap: function(module, script) { bootstrap: function(module, script) {
return "define('components/partials', ['angular'], function(angular) { \n" + return "define('app/components/partials', ['angular'], function(angular) { \n" +
"angular.module('grafana').run(['$templateCache', function($templateCache) { \n" + "angular.module('grafana').run(['$templateCache', function($templateCache) { \n" +
script + script +
'\n}]);' + '\n}]);' +
......
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