Browse Source

Merge branch 'master' into apps

Conflicts:
	public/app/partials/sidemenu.html
Torkel Ödegaard 10 years ago
parent
commit
60e7c6d150

+ 1 - 1
docs/Makefile

@@ -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)" .

+ 1 - 0
docs/mkdocs.yml

@@ -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']

+ 5 - 5
docs/sources/project/building_from_source.md

@@ -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.

+ 50 - 0
docs/sources/reference/plugins.md

@@ -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

+ 18 - 39
pkg/api/cloudwatch/cloudwatch.go

@@ -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)

+ 20 - 14
public/app/core/controllers/sidemenu_ctrl.js

@@ -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"});
         }
       });
     };

+ 1 - 1
public/app/features/annotations/partials/editor.html

@@ -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>

+ 0 - 4
public/app/features/org/all.js

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

+ 33 - 62
public/app/partials/sidemenu.html

@@ -8,41 +8,21 @@
 			</a>
 		</li>
 
-		<li class="sidemenu-system-section" ng-if="systemSection">
-			<div class="sidemenu-system-section-inner">
-				<i class="fa fa-fw fa-cubes"></i>
-				<div class="sidemenu-section-text-wrapper">
-					<div class="sidemenu-section-heading">Grafana Admin</div>
-					<div class="sidemenu-section-tagline">v {{grafanaVersion}}</div>
+		<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>
-		</li>
-
-		<li ng-repeat="item in mainLinks">
-			<a href="{{item.url}}" class="sidemenu-item" 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>
-		</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">
+			<ul class="dropdown-menu" role="menu">
 				<li ng-repeat="menuItem in orgMenu" ng-class="menuItem.cssClass">
-					<a href="{{menuItem.href}}" ng-if="menuItem.href">
+					<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>
@@ -54,40 +34,31 @@
 			</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 class="sidemenu-system-section" ng-if="systemSection">
+			<div class="sidemenu-system-section-inner">
+				<i class="fa fa-fw fa-cubes"></i>
+				<div class="sidemenu-section-text-wrapper">
+					<div class="sidemenu-section-heading">Grafana Admin</div>
+					<div class="sidemenu-section-tagline">v {{grafanaVersion}}</div>
+				</div>
+			</div>
 		</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 ng-repeat="item in mainLinks">
+			<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>
-			<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>

+ 1 - 1
public/app/partials/submenu.html

@@ -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>

+ 81 - 25
public/less/sidemenu.less

@@ -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;
+}
+

+ 5 - 0
public/less/variables.dark.less

@@ -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;

+ 6 - 0
public/less/variables.light.less

@@ -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;