Merge branch 'master' into apps

Conflicts:
	public/app/partials/sidemenu.html
This commit is contained in:
Torkel Ödegaard 2016-01-08 18:10:53 +01:00
commit 60e7c6d150
13 changed files with 225 additions and 155 deletions

View File

@ -44,7 +44,7 @@ docs-test: docs-build
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh
docs-build:
git fetch https://github.com/grafana/grafana.git docs-2.1 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
git fetch https://github.com/grafana/grafana.git docs-2.5 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
echo "$(GIT_BRANCH)" > GIT_BRANCH
echo "$(GITCOMMIT)" > GITCOMMIT
docker build -t "$(DOCKER_DOCS_IMAGE)" .

View File

@ -62,6 +62,7 @@ pages:
- ['reference/templating.md', 'Reference', 'Templating']
- ['reference/scripting.md', 'Reference', 'Scripting']
- ['reference/playlist.md', 'Reference', 'Playlist']
- ['reference/plugins.md', 'Reference', 'Plugins']
- ['reference/export_import.md', 'Reference', 'Import & Export']
- ['reference/admin.md', 'Reference', 'Administration']
- ['reference/http_api.md', 'Reference', 'HTTP API']

View File

@ -11,7 +11,7 @@ dev environment. Grafana ships with its own required backend server; also comple
## Dependencies
- [Go 1.4](https://golang.org/dl/)
- [Go 1.5](https://golang.org/dl/)
- [NodeJS](https://nodejs.org/download/)
## Get Code
@ -54,7 +54,7 @@ bra run
## Running Grafana Locally
You can run a local instance of Grafana by running:
```
./bin/grafana-server
./bin/grafana-server
```
If you built the binary with `go run build.go build`, run `./bin/grafana-server`
@ -63,7 +63,7 @@ If you built it with `go build .`, run `./grafana`
Open grafana in your browser (default [http://localhost:3000](http://localhost:3000)) and login with admin user (default user/pass = admin/admin).
## Developing for Grafana
To add features, customize your config, etc, you'll need to rebuild on source change (requires that you executed [godep restore](#build-the-backend), as outlined above).
To add features, customize your config, etc, you'll need to rebuild on source change (requires that you executed [godep restore](#build-the-backend), as outlined above).
```
go get github.com/Unknwon/bra
bra run
@ -88,7 +88,7 @@ You only need to add the options you want to override. Config files are applied
Learn more about Grafana config options in the [Configuration section](/installation/configuration/)
## Create a pull requests
Please contribute to the Grafana project and submit a pull request! Build new features, write or update documentation, fix bugs and generally make Grafana even more awesome.
Please contribute to the Grafana project and submit a pull request! Build new features, write or update documentation, fix bugs and generally make Grafana even more awesome.
Before or after you create a pull request, sign the [contributor license agreement](/project/cla.html).
Together we can build amazing software faster.
Together we can build amazing software faster.

View File

@ -0,0 +1,50 @@
---
page_title: Plugin guide
page_description: Plugin guide for Grafana
page_keywords: grafana, plugins, documentation
---
# Plugins
!Plugin support for panels is only available in nightly!
Adding support for all datasources and suggested panels would bloat grafana and make it impossible to maintain. That's why we implemented a plugin system that makes it possible for anyone to develop support for a datasource or custom panel without adding it to Grafana itself.
## Installing plugins
Installing a plugin is very simple. Just download it and place it in the Grafana plugins folder and restart grafana.
The default plugin folder is configurable under paths.plugins
It's also possible to add one specific plugin by linking to its folder.
```
[plugin.mirror]
path = /home/evil-queen/datasource-plugin-mirror
```
## Plugin implementation ##
Each plugin is defined in plugin.json file in the plugin folder.
Instead of massive documentation about how it works we created a reference implementation of a plugin.
You can find each reference implementation further down on this page.
## Datasource plugins
Datasource have three responsibilities.
* UI for configuring its settings
* Datasource object that can send queries, metricqueries and healthcheck the datasource
* Query editor within panels
https://github.com/grafana/datasource-plugin-genericdatasource
## Panel plugins
Panel plugins are responsible for
* UI for Panel options.
* Creating a directive that can render something based on datasource data.
We currently dont have a reference implementation for panel plugins but you can checkout https://github.com/grafana/panel-plugin-piechart

View File

@ -43,18 +43,29 @@ func init() {
}
}
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
var awsCredentials map[string]*credentials.Credentials = make(map[string]*credentials.Credentials)
func getCredentials(profile string) *credentials.Credentials {
if _, ok := awsCredentials[profile]; ok {
return awsCredentials[profile]
}
sess := session.New()
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
&credentials.SharedCredentialsProvider{Filename: "", Profile: profile},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
})
awsCredentials[profile] = creds
return creds
}
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{
Region: aws.String(req.Region),
Credentials: creds,
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg)
@ -92,17 +103,9 @@ func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
}
func handleListMetrics(req *cwRequest, c *middleware.Context) {
sess := session.New()
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
})
cfg := &aws.Config{
Region: aws.String(req.Region),
Credentials: creds,
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg)
@ -140,17 +143,9 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
}
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
sess := session.New()
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
})
cfg := &aws.Config{
Region: aws.String(req.Region),
Credentials: creds,
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg)
@ -188,17 +183,9 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
}
func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
sess := session.New()
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
})
cfg := &aws.Config{
Region: aws.String(req.Region),
Credentials: creds,
Credentials: getCredentials(req.DataSource.Database),
}
svc := cloudwatch.New(session.New(cfg), cfg)
@ -232,17 +219,9 @@ func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
}
func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
sess := session.New()
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
})
cfg := &aws.Config{
Region: aws.String(req.Region),
Credentials: creds,
Credentials: getCredentials(req.DataSource.Database),
}
svc := ec2.New(session.New(cfg), cfg)

View File

@ -24,26 +24,32 @@ function (angular, _, $, coreModule, config) {
});
};
$scope.loadOrgs = function() {
$scope.orgMenu = [];
$scope.openUserDropdown = function() {
$scope.orgMenu = [
{section: 'You', cssClass: 'dropdown-menu-title'},
{text: 'Profile', url: $scope.getUrl('/profile')},
];
if (contextSrv.hasRole('Admin')) {
$scope.orgMenu.push({section: contextSrv.user.orgName, cssClass: 'dropdown-menu-title'});
$scope.orgMenu.push({
text: "Organization settings",
href: $scope.getUrl("/org"),
text: "Settings",
url: $scope.getUrl("/org"),
});
$scope.orgMenu.push({
text: "Users",
href: $scope.getUrl("/org/users"),
url: $scope.getUrl("/org/users"),
});
$scope.orgMenu.push({
text: "API Keys",
href: $scope.getUrl("/org/apikeys"),
url: $scope.getUrl("/org/apikeys"),
});
}
if ($scope.orgMenu.length > 0) {
$scope.orgMenu.push({ cssClass: 'divider' });
$scope.orgMenu.push({cssClass: "divider"});
if (config.allowOrgCreate) {
$scope.orgMenu.push({text: "New organization", icon: "fa fa-fw fa-plus", url: $scope.getUrl('/org/new')});
}
backendSrv.get('/api/user/orgs').then(function(orgs) {
@ -61,12 +67,12 @@ function (angular, _, $, coreModule, config) {
});
});
if (config.allowOrgCreate) {
$scope.orgMenu.push({
text: "New Organization",
icon: "fa fa-fw fa-plus",
href: $scope.getUrl('/org/new')
});
$scope.orgMenu.push({cssClass: "divider"});
if (contextSrv.isGrafanaAdmin) {
$scope.orgMenu.push({text: "Server admin", url: $scope.getUrl("/admin/settings")});
}
if (contextSrv.isSignedIn) {
$scope.orgMenu.push({text: "Sign out", url: $scope.getUrl("/logout"), target: "_self"});
}
});
};

View File

@ -44,7 +44,7 @@
<table class="grafana-options-table">
<tr ng-repeat="annotation in annotations">
<td style="width:90%">
<i class="fa fa-bolt"></i> &nbsp;
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i> &nbsp;
{{annotation.name}}
</td>
<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>

View File

@ -6,8 +6,4 @@ define([
'./userInviteCtrl',
'./orgApiKeysCtrl',
'./orgDetailsCtrl',
'./app_list_ctrl',
'./app_edit_ctrl',
'./app_srv',
'./app_directive',
], function () {});

View File

@ -8,6 +8,32 @@
</a>
</li>
<li class="sidemenu-org-section dropdown" ng-if="contextSrv.isSignedIn">
<div class="sidemenu-org" data-toggle="dropdown" ng-click="openUserDropdown()">
<div class="sidemenu-org-avatar">
<img ng-src="{{contextSrv.user.gravatarUrl}}">
</div>
<div class="sidemenu-org-details">
<span class="sidemenu-org-user sidemenu-item-text">{{contextSrv.user.name}}</span>
<span class="sidemenu-org-name sidemenu-item-text">{{contextSrv.user.orgName}}</span>
</div>
<i class="fa fa-caret-down small"></i>
</div>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="menuItem in orgMenu" ng-class="menuItem.cssClass">
<span ng-if="menuItem.section">{{menuItem.section}}</span>
<a href="{{menuItem.url}}" ng-if="menuItem.url" target="{{menuItem.target}}">
<i class="{{menuItem.icon}}" ng-if="menuItem.icon"></i>
{{menuItem.text}}
</a>
<a ng-click="menuItem.click()" ng-if="menuItem.click">
<i class="{{menuItem.icon}}"></i>
{{menuItem.text}}
</a>
</li>
</ul>
</li>
<li class="sidemenu-system-section" ng-if="systemSection">
<div class="sidemenu-system-section-inner">
<i class="fa fa-fw fa-cubes"></i>
@ -19,75 +45,20 @@
</li>
<li ng-repeat="item in mainLinks">
<a href="{{item.url}}" class="sidemenu-item" target="{{item.target}}">
<a href="{{item.url}}" class="sidemenu-item sidemenu-main-link" target="{{item.target}}">
<span class="icon-circle sidemenu-icon"><i class="{{item.icon}}"></i></span>
<span class="sidemenu-item-text">{{item.text}}</span>
</a>
</li>
</ul>
<ul class="sidemenu sidemenu-small" style="margin-top:50px" ng-if="!systemSection">
<li ng-if="contextSrv.user.isSignedIn">
<a href="profile" class="sidemenu-item">
<img ng-src="{{contextSrv.user.gravatarUrl}}">
<span class="sidemenu-item-text">{{contextSrv.user.name}}</span>
</a>
</a>
</li>
<li class="dropdown">
<a class="sidemenu-item pointer" data-toggle="dropdown" ng-click="loadOrgs()" tabindex="0">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-users"></i></span>
<span class="sidemenu-item-text">{{contextSrv.user.orgName}}</span><i class="fa fa-caret-down small"></i>
</a>
<ul class="dropdown-menu" role="menu" style="left: 65px">
<li ng-repeat="menuItem in orgMenu" ng-class="menuItem.cssClass">
<a href="{{menuItem.href}}" ng-if="menuItem.href">
<i class="{{menuItem.icon}}" ng-if="menuItem.icon"></i>
{{menuItem.text}}
</a>
<a ng-click="menuItem.click()" ng-if="menuItem.click">
<i class="{{menuItem.icon}}"></i>
{{menuItem.text}}
</a>
</li>
</ul>
</li>
<li ng-if="contextSrv.isGrafanaAdmin">
<a href="admin/settings" class="sidemenu-item">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-cog"></i></span>
<span class="sidemenu-item-text">Grafana admin</span>
</a>
</li>
<li ng-if="showSignout">
<a href="logout" class="sidemenu-item" target="_self">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-out"></i></span>
<span class="sidemenu-item-text">Sign out</span>
</a>
</li>
<li ng-if="!contextSrv.isSignedIn">
<a href="login" class="sidemenu-item" target="_self">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-in"></i></span>
<span class="sidemenu-item-text">Sign in</span>
</a>
</li>
</ul>
<ul class="sidemenu sidemenu-small" style="margin-top:50px" ng-if="systemSection">
<li>
<a href="{{appSubUrl}}/" class="sidemenu-item">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-backward"></i></span>
<span class="sidemenu-item-text">Exit admin</span>
</a>
</li>
<li ng-if="showSignout">
<a href="logout" class="sidemenu-item" target="_self">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-out"></i></span>
<span class="sidemenu-item-text">Sign out</span>
</a>
</li>
<ul class="sidemenu sidemenu-small" style="margin-top:50px" ng-if="systemSection">
<li>
<a href="{{appSubUrl}}/" class="sidemenu-item">
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-backward"></i></span>
<span class="sidemenu-item-text">Exit admin</span>
</a>
</li>
</ul>
</ul>
</div>

View File

@ -13,7 +13,7 @@
<ul class="tight-form-list" ng-if="dashboard.annotations.list.length > 0">
<li ng-repeat="annotation in dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
<a ng-click="disableAnnotation(annotation)">
<i class="fa fa-bolt"></i>
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
{{annotation.name}}
<input class="cr1" id="hideYAxis" type="checkbox" ng-model="annotation.enable" ng-checked="annotation.enable">
<label for="hideYAxis" class="cr1"></label>

View File

@ -33,7 +33,6 @@
}
.sidemenu {
font-size: 16px;
font-weight: @baseFontWeight;
list-style: none;
margin: 0;
@ -48,32 +47,11 @@
top: 2px;
font-size: 90%;
}
&.sidemenu-small {
font-size: 14px;
.icon-circle {
border-radius: 50%;
background: @iconContainerBackground;
box-shadow: @iconContainerShadow;
border: @iconContainerBorder;
width: 28px;
height: 28px;
i {
top: 1px;
left: 4px;
font-size: 110%;
}
}
.sidemenu-item {
// color: @textColor;
line-height: 28px;
padding-left: 25px;
}
}
}
.sidemenu-main-link {
font-size: 16px;
}
.sidemenu-item-text {
width: 110px;
@ -162,6 +140,7 @@
padding: 0 15px;
}
}
.sidemenu-section-tagline {
font-style: italic;
font-size: 75%;
@ -171,3 +150,80 @@
.sidemenu-section-text-wrapper {
padding-top: 4px;
}
.sidemenu-org-section .dropdown-menu {
top: 51%;
left: 100px;
}
.sidemenu-org-section .dropdown-menu-title {
margin: 0 10px 0 6px;
padding: 10px 0 0;
overflow: hidden;
color: @dropdownTitle;
font-weight: bold;
}
.sidemenu-org-section .dropdown-menu-title > span {
display: inline-block;
position: relative;
&:after {
display: block;
position: absolute;
top: 50%;
right: 0;
left: 100%;
width: 200px;
height: 1px;
margin-left: 5px;
background: @dropdownDivider;
content: '';
}
}
.sidemenu-org {
display: table;
position: relative;
width: 159px;
padding: 2px 10px 20px 21px;
border-bottom: 1px solid @sideMenuOrgBorder;
cursor: pointer;
}
.sidemenu-org .fa-caret-down {
position: absolute;
top: 33px;
right: 2px;
}
.sidemenu-org-avatar,
.sidemenu-org-details {
display: table-cell;
vertical-align: top;
}
.sidemenu-org-avatar {
width: 44px;
}
.sidemenu-org-avatar > img {
width: 44px;
height: 44px;
border-radius: 50%;
}
.sidemenu-org-details {
padding-left: 12px;
color: @linkColor;
}
.sidemenu-org-user,
.sidemenu-org-name {
display: block;
}
.sidemenu-org-user {
font-size: 14px;
}

View File

@ -157,6 +157,9 @@
@formActionsBackground: transparent;
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
// Sidemenu
// -------------------------
@sideMenuOrgBorder: rgb(37,37,37);
// Dropdowns
// -------------------------
@ -164,6 +167,8 @@
@dropdownBorder: rgba(0,0,0,.2);
@dropdownDividerTop: transparent;
@dropdownDividerBottom: #444;
@dropdownDivider: @dropdownDividerBottom;
@dropdownTitle: @white;
@dropdownLinkColor: @textColor;
@dropdownLinkColorHover: @white;

View File

@ -171,12 +171,18 @@
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
@inputText: #020202;
// Sidemenu
// -------------------------
@sideMenuOrgBorder: #555;
// Dropdowns
// -------------------------
@dropdownBackground: @white;
@dropdownBorder: rgba(0,0,0,.2);
@dropdownDividerTop: #e5e5e5;
@dropdownDividerBottom: @white;
@dropdownDivider: @dropdownDividerTop;
@dropdownTitle: #333;
@dropdownLinkColor: @grayDark;
@dropdownLinkColorHover: @white;