Browse Source

search and save are 'working', barely

Torkel Ödegaard 11 years ago
commit
8154b4f60d
51 changed files with 3809 additions and 0 deletions
  1. 15 0
      .gitignore
  2. 3 0
      .gitmodules
  3. 35 0
      .jshintrc
  4. 5 0
      .travis.yml
  5. 38 0
      Gruntfile.js
  6. 11 0
      backend/configuration/configuration.go
  7. 106 0
      backend/httpApi/api.go
  8. 1 0
      backend/httpApi/api_test.go
  9. 7 0
      backend/httpApi/commands.go
  10. 53 0
      backend/models/dashboards.go
  11. 27 0
      backend/server/server.go
  12. 155 0
      backend/stores/file_store.go
  13. 112 0
      backend/stores/file_store_test.go
  14. 16 0
      backend/stores/store.go
  15. 269 0
      data/dashboards/annotations.json
  16. 409 0
      data/dashboards/default.json
  17. 796 0
      data/dashboards/graph-styles.json
  18. 288 0
      data/dashboards/templated-graphs-nested.json
  19. 272 0
      data/dashboards/templated-graphs.json
  20. 0 0
      data/dashboards/testing-save.json
  21. 519 0
      data/dashboards/white-theme.json
  22. 1 0
      grafana
  23. BIN
      grafana-pro
  24. 18 0
      grafana-pro.sublime-project
  25. 31 0
      grafana.go
  26. 68 0
      package.json
  27. 37 0
      tasks/build_task.js
  28. 5 0
      tasks/default_task.js
  29. 31 0
      tasks/distribute_task.js
  30. 7 0
      tasks/options/clean.js
  31. 76 0
      tasks/options/compress.js
  32. 22 0
      tasks/options/concat.js
  33. 20 0
      tasks/options/connect.js
  34. 11 0
      tasks/options/copy.js
  35. 10 0
      tasks/options/cssmin.js
  36. 17 0
      tasks/options/filerev.js
  37. 7 0
      tasks/options/git-describe.js
  38. 18 0
      tasks/options/htmlmin.js
  39. 22 0
      tasks/options/jscs.js
  40. 26 0
      tasks/options/jshint.js
  41. 27 0
      tasks/options/karma.js
  42. 25 0
      tasks/options/less.js
  43. 9 0
      tasks/options/meta.js
  44. 19 0
      tasks/options/ngmin.js
  45. 18 0
      tasks/options/ngtemplates.js
  46. 83 0
      tasks/options/requirejs.js
  47. 16 0
      tasks/options/uglify.js
  48. 5 0
      tasks/options/usemin.js
  49. 5 0
      tasks/options/useminPrepare.js
  50. 3 0
      tasks/server_task.js
  51. 35 0
      views/index.html

+ 15 - 0
.gitignore

@@ -0,0 +1,15 @@
+node_modules
+coverage/
+.aws-config.json
+dist
+
+public
+gin-bin
+# locally required config files
+web.config
+config.js
+
+# Editor junk
+*.sublime-workspace
+*.swp
+.idea/

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "grafana"]
+	path = grafana
+	url = git@github.com:torkelo/grafana-private.git

+ 35 - 0
.jshintrc

@@ -0,0 +1,35 @@
+{
+  "browser": true,
+
+  "bitwise":false,
+  "curly": true,
+  "eqnull": true,
+  "globalstrict": true,
+  "devel": true,
+  "eqeqeq": true,
+  "forin": false,
+  "immed": true,
+  "supernew": true,
+  "expr": true,
+  "indent": 2,
+  "latedef": true,
+  "newcap": true,
+  "noarg": true,
+  "noempty": true,
+  "undef": true,
+  "boss": true,
+  "trailing": true,
+  "laxbreak": true,
+  "laxcomma": true,
+  "sub": true,
+  "unused": true,
+  "maxdepth": 5,
+  "maxlen": 140,
+
+  "globals": {
+    "define": true,
+    "require": true,
+    "Chromath": false,
+    "setImmediate": true
+  }
+}

+ 5 - 0
.travis.yml

@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+  - "0.10"
+before_script:
+  - npm install -g grunt-cli

+ 38 - 0
Gruntfile.js

@@ -0,0 +1,38 @@
+/* jshint node:true */
+'use strict';
+module.exports = function (grunt) {
+
+  var config = {
+    pkg: grunt.file.readJSON('package.json'),
+    baseDir: '.',
+    srcDir: 'public',
+    destDir: 'dist',
+    tempDir: 'tmp',
+    docsDir: 'docs/'
+  };
+
+  // load plugins
+  require('load-grunt-tasks')(grunt);
+
+  // load task definitions
+  grunt.loadTasks('tasks');
+
+  // Utility function to load plugin settings into config
+  function loadConfig(config,path) {
+    require('glob').sync('*', {cwd: path}).forEach(function(option) {
+      var key = option.replace(/\.js$/,'');
+      // If key already exists, extend it. It is your responsibility to avoid naming collisions
+      config[key] = config[key] || {};
+      grunt.util._.extend(config[key], require(path + option)(config,grunt));
+    });
+    // technically not required
+    return config;
+  }
+
+  // Merge that object with what with whatever we have here
+  loadConfig(config,'./tasks/options/');
+
+  // pass the config to grunt
+  grunt.initConfig(config);
+
+};

+ 11 - 0
backend/configuration/configuration.go

@@ -0,0 +1,11 @@
+package configuration
+
+type Cfg struct {
+	httpPort        string
+	DashboardSource DashboardSourceCfg
+}
+
+type DashboardSourceCfg struct {
+	sourceType string
+	path       string
+}

+ 106 - 0
backend/httpApi/api.go

@@ -0,0 +1,106 @@
+package httpApi
+
+import (
+	"html/template"
+
+	log "github.com/alecthomas/log4go"
+	"github.com/gin-gonic/gin"
+	"github.com/torkelo/grafana-pro/backend/models"
+	"github.com/torkelo/grafana-pro/backend/stores"
+)
+
+type HttpServer struct {
+	port     string
+	shutdown chan bool
+	store    stores.Store
+}
+
+func NewHttpServer(port string, store stores.Store) *HttpServer {
+	self := &HttpServer{}
+	self.port = port
+	self.store = store
+
+	return self
+}
+
+func CacheHeadersMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		c.Writer.Header().Add("Cache-Control", "max-age=0, public, must-revalidate, proxy-revalidate")
+	}
+}
+
+func (self *HttpServer) ListenAndServe() {
+	log.Info("Starting Http Listener on port %v", self.port)
+
+	defer func() { self.shutdown <- true }()
+
+	r := gin.Default()
+	r.Use(CacheHeadersMiddleware())
+
+	templates := template.New("templates")
+	templates.Delims("[[", "]]")
+	templates.ParseFiles("./views/index.html")
+
+	r.SetHTMLTemplate(templates)
+
+	r.GET("/", self.index)
+	r.GET("/api/dashboards/:id", self.getDashboard)
+	r.GET("/api/search/", self.search)
+	r.POST("/api/dashboard", self.postDashboard)
+
+	r.Static("/public", "./public")
+	r.Static("/app", "./public/app")
+	r.Static("/img", "./public/img")
+
+	r.Run(":" + self.port)
+}
+
+type IndexViewModel struct {
+	Title string
+}
+
+func (self *HttpServer) index(c *gin.Context) {
+	c.HTML(200, "index.html", &IndexViewModel{Title: "hello from go"})
+}
+
+type ErrorRsp struct {
+	Message string `json:"message"`
+}
+
+func (self *HttpServer) getDashboard(c *gin.Context) {
+	id := c.Params.ByName("id")
+
+	dash, err := self.store.GetById(id)
+	if err != nil {
+		c.JSON(404, &ErrorRsp{Message: "Dashboard not found"})
+		return
+	}
+
+	c.JSON(200, dash.Data)
+}
+
+func (self *HttpServer) search(c *gin.Context) {
+	query := c.Params.ByName("q")
+
+	results, err := self.store.Query(query)
+	if err != nil {
+		c.JSON(500, &ErrorRsp{Message: "Search error"})
+		return
+	}
+
+	c.JSON(200, results)
+}
+
+func (self *HttpServer) postDashboard(c *gin.Context) {
+	var command saveDashboardCommand
+
+	if c.EnsureBody(&command) {
+		err := self.store.Save(&models.Dashboard{Data: command.Dashboard})
+		if err == nil {
+			c.JSON(200, gin.H{"status": "saved"})
+			return
+		}
+	}
+
+	c.JSON(500, gin.H{"error": "bad request"})
+}

+ 1 - 0
backend/httpApi/api_test.go

@@ -0,0 +1 @@
+package httpApi

+ 7 - 0
backend/httpApi/commands.go

@@ -0,0 +1,7 @@
+package httpApi
+
+type saveDashboardCommand struct {
+	Id        string `json:"id"`
+	Title     string `json:"title"`
+	Dashboard map[string]interface{}
+}

+ 53 - 0
backend/models/dashboards.go

@@ -0,0 +1,53 @@
+package models
+
+import (
+	"encoding/json"
+	"io"
+)
+
+type Dashboard struct {
+	Data map[string]interface{}
+}
+
+type SearchResult struct {
+	Type  string `json:"title"`
+	Id    string `json:"id"`
+	Title string `json:"title"`
+}
+
+func NewDashboard(title string) *Dashboard {
+	dash := &Dashboard{}
+	dash.Data = make(map[string]interface{})
+	dash.Data["title"] = title
+
+	return dash
+}
+
+func NewFromJson(reader io.Reader) (*Dashboard, error) {
+	dash := NewDashboard("temp")
+	jsonParser := json.NewDecoder(reader)
+
+	if err := jsonParser.Decode(&dash.Data); err != nil {
+		return nil, err
+	}
+
+	return dash, nil
+}
+
+/*type DashboardServices struct {
+}
+
+type DashboardServicesFilter struct {
+}
+
+type DashboardServicesFilterTime struct {
+	From string 	To	string
+}*/
+
+func (dash *Dashboard) GetString(prop string) string {
+	return dash.Data[prop].(string)
+}
+
+func (dash *Dashboard) Title() string {
+	return dash.GetString("title")
+}

+ 27 - 0
backend/server/server.go

@@ -0,0 +1,27 @@
+package server
+
+import (
+	"github.com/torkelo/grafana-pro/backend/httpApi"
+	"github.com/torkelo/grafana-pro/backend/stores"
+)
+
+type Server struct {
+	HttpServer *httpApi.HttpServer
+	Store      stores.Store
+}
+
+func NewServer(port string) (*Server, error) {
+	store := stores.New()
+	httpServer := httpApi.NewHttpServer(port, store)
+
+	return &Server{
+		HttpServer: httpServer,
+		Store:      store,
+	}, nil
+}
+
+func (self *Server) ListenAndServe() error {
+	self.HttpServer.ListenAndServe()
+
+	return nil
+}

+ 155 - 0
backend/stores/file_store.go

@@ -0,0 +1,155 @@
+package stores
+
+import (
+	"encoding/json"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+
+	log "github.com/alecthomas/log4go"
+	"github.com/torkelo/grafana-pro/backend/models"
+)
+
+type fileStore struct {
+	dataDir string
+	dashDir string
+	cache   map[string]*models.Dashboard
+}
+
+func NewFileStore(dataDir string) *fileStore {
+
+	if dirDoesNotExist(dataDir) {
+		log.Crashf("FileStore failed to initialize, dataDir does not exist %v", dataDir)
+	}
+
+	dashDir := filepath.Join(dataDir, "dashboards")
+
+	if dirDoesNotExist(dashDir) {
+		log.Debug("Did not find dashboard dir, creating...")
+		err := os.Mkdir(dashDir, 0777)
+		if err != nil {
+			log.Crashf("FileStore failed to initialize, could not create directory %v, error: %v", dashDir, err)
+		}
+	}
+
+	store := &fileStore{}
+	store.dataDir = dataDir
+	store.dashDir = dashDir
+	store.cache = make(map[string]*models.Dashboard)
+	go store.scanFiles()
+
+	return store
+}
+
+func (store *fileStore) scanFiles() {
+	visitor := func(path string, f os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		if f.IsDir() {
+			return nil
+		}
+		if strings.HasSuffix(f.Name(), ".json") {
+			err = store.loadDashboardIntoCache(path)
+			if err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+
+	err := filepath.Walk(store.dashDir, visitor)
+	if err != nil {
+		log.Error("FileStore::updateCache failed %v", err)
+	}
+}
+
+func (store fileStore) loadDashboardIntoCache(filename string) error {
+	log.Info("Loading dashboard file %v into cache", filename)
+	dash, err := loadDashboardFromFile(filename)
+	if err != nil {
+		return err
+	}
+
+	store.cache[dash.Title()] = dash
+
+	return nil
+}
+
+func (store *fileStore) Close() {
+
+}
+
+func (store *fileStore) GetById(id string) (*models.Dashboard, error) {
+	log.Debug("FileStore::GetById id = %v", id)
+	filename := store.getFilePathForDashboard(id)
+
+	return loadDashboardFromFile(filename)
+}
+
+func (store *fileStore) Save(dash *models.Dashboard) error {
+	filename := store.getFilePathForDashboard(dash.Title())
+
+	log.Debug("Saving dashboard %v to %v", dash.Title(), filename)
+
+	var err error
+	var data []byte
+	if data, err = json.Marshal(dash.Data); err != nil {
+		return err
+	}
+
+	return writeFile(filename, data)
+}
+
+func (store *fileStore) Query(query string) ([]*models.SearchResult, error) {
+	results := make([]*models.SearchResult, 0, 50)
+
+	for _, dash := range store.cache {
+		item := &models.SearchResult{
+			Id:   dash.Title(),
+			Type: "dashboard",
+		}
+		results = append(results, item)
+	}
+
+	return results, nil
+}
+
+func loadDashboardFromFile(filename string) (*models.Dashboard, error) {
+	log.Debug("FileStore::loading dashboard from file %v", filename)
+
+	configFile, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	return models.NewFromJson(configFile)
+}
+
+func (store *fileStore) getFilePathForDashboard(id string) string {
+	id = strings.ToLower(id)
+	id = strings.Replace(id, " ", "-", -1)
+	return filepath.Join(store.dashDir, id) + ".json"
+}
+
+func dirDoesNotExist(dir string) bool {
+	_, err := os.Stat(dir)
+	return os.IsNotExist(err)
+}
+
+func writeFile(filename string, data []byte) error {
+	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+	if err != nil {
+		return err
+	}
+	n, err := f.Write(data)
+	if err == nil && n < len(data) {
+		err = io.ErrShortWrite
+	}
+	if err1 := f.Close(); err == nil {
+		err = err1
+	}
+
+	return err
+}

+ 112 - 0
backend/stores/file_store_test.go

@@ -0,0 +1,112 @@
+package stores
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+	"github.com/torkelo/grafana-pro/backend/models"
+)
+
+func TestFileStore(t *testing.T) {
+
+	GivenFileStore("When saving a dashboard", t, func(store *fileStore) {
+		dashboard := models.NewDashboard("hello")
+
+		err := store.Save(dashboard)
+
+		Convey("should be saved to disk", func() {
+			So(err, ShouldBeNil)
+
+			_, err = os.Stat(store.getFilePathForDashboard("hello"))
+			So(err, ShouldBeNil)
+		})
+	})
+
+	GivenFileStore("When getting a saved dashboard", t, func(store *fileStore) {
+		copyDashboardToTempData("default.json", "", store.dashDir)
+		dash, err := store.GetById("default")
+
+		Convey("should be read from disk", func() {
+			So(err, ShouldBeNil)
+			So(dash, ShouldNotBeNil)
+
+			So(dash.Title(), ShouldEqual, "Grafana Play Home")
+		})
+	})
+
+	GivenFileStore("when getting dashboard with capital letters", t, func(store *fileStore) {
+		copyDashboardToTempData("annotations.json", "", store.dashDir)
+		dash, err := store.GetById("AnnoTations")
+
+		Convey("should be read from disk", func() {
+			So(err, ShouldBeNil)
+			So(dash, ShouldNotBeNil)
+
+			So(dash.Title(), ShouldEqual, "Annotations")
+		})
+	})
+
+	GivenFileStore("When copying dashboards into data dir", t, func(store *fileStore) {
+		copyDashboardToTempData("annotations.json", "", store.dashDir)
+		copyDashboardToTempData("default.json", "", store.dashDir)
+		copyDashboardToTempData("graph-styles.json", "", store.dashDir)
+		store.scanFiles()
+
+		Convey("scan should generate index of all dashboards", func() {
+
+			result, err := store.Query("*")
+			So(err, ShouldBeNil)
+			So(len(result), ShouldEqual, 3)
+		})
+	})
+}
+
+func copyDashboardToTempData(name string, destName string, dir string) {
+	if destName == "" {
+		destName = name
+	}
+	source, _ := filepath.Abs("../../data/dashboards/" + name)
+	dest := filepath.Join(dir, destName)
+	err := copyFile(dest, source)
+	if err != nil {
+		panic(fmt.Sprintf("failed to copy file %v", name))
+	}
+}
+
+func GivenFileStore(desc string, t *testing.T, f func(store *fileStore)) {
+	Convey(desc, t, func() {
+		tempDir, _ := ioutil.TempDir("", "store")
+
+		store := NewFileStore(tempDir)
+
+		f(store)
+
+		Reset(func() {
+			os.RemoveAll(tempDir)
+		})
+	})
+}
+
+func copyFile(dst, src string) error {
+	in, err := os.Open(src)
+	if err != nil {
+		return err
+	}
+	defer in.Close()
+	out, err := os.Create(dst)
+	if err != nil {
+		return err
+	}
+	defer out.Close()
+	_, err = io.Copy(out, in)
+	cerr := out.Close()
+	if err != nil {
+		return err
+	}
+	return cerr
+}

+ 16 - 0
backend/stores/store.go

@@ -0,0 +1,16 @@
+package stores
+
+import (
+	"github.com/torkelo/grafana-pro/backend/models"
+)
+
+type Store interface {
+	GetById(id string) (*models.Dashboard, error)
+	Save(dash *models.Dashboard) error
+	Query(query string) ([]*models.SearchResult, error)
+	Close()
+}
+
+func New() Store {
+	return NewFileStore("data")
+}

+ 269 - 0
data/dashboards/annotations.json

@@ -0,0 +1,269 @@
+{
+  "title": "Annotations",
+  "services": {
+    "filter": {
+      "list": [],
+      "time": {
+        "from": "now-1h",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "Welcome to Grafana",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 12,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(apps.fakesite.web_server_02.counters.requests.count,2)"
+            },
+            {
+              "target": "aliasByNode(apps.fakesite.web_server_01.counters.requests.count,2)"
+            }
+          ],
+          "aliasColors": {},
+          "aliasYAxis": {},
+          "title": "Amnotations example",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "test",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(apps.fakesite.web_server_02.counters.request_status.code_304.count,5)"
+            }
+          ],
+          "aliasColors": {
+            "web_server_01": "#1F78C1",
+            "web_server_02": "#6ED0E0"
+          },
+          "aliasYAxis": {},
+          "title": "Annotations example",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "### Annotations\n\n- Annotation is a feature that must be enabled in the dashboards settings / Controls tab / Feature toggles\n- Annotation bar is then visible at the top. \n- Click on the cog to open the Annotations dialog \n- In this dialog you can add or edit annotations \n- Currently only Graphite metrics and Graphite events are supported sources of annotations\n- More datasource options for annotations will be added \n- Click on the annotation name in the bar to toggle the annotation on or off\n\n",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "dark",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": false
+    },
+    {
+      "type": "annotations",
+      "enable": true,
+      "annotations": [
+        {
+          "name": "deploys",
+          "type": "graphite metric",
+          "showLine": true,
+          "iconColor": "#C0C6BE",
+          "lineColor": "rgba(253, 54, 54, 0.77)",
+          "iconSize": 13,
+          "enable": true,
+          "target": "alias(apps.fakesite.web_server_01.counters.request_status.code_500.count, 'deployed v1.3')"
+        },
+        {
+          "name": "puppet apply",
+          "type": "graphite metric",
+          "showLine": true,
+          "iconColor": "#C0C6BE",
+          "lineColor": "rgba(255, 96, 96, 0.592157)",
+          "iconSize": 13,
+          "enable": false,
+          "target": "alias(apps.fakesite.web_server_02.counters.request_status.code_403.count,'puppet apply')"
+        }
+      ]
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [
+    "annotations",
+    "graphite",
+    "showcase"
+  ],
+  "timezone": "browser"
+}

+ 409 - 0
data/dashboards/default.json

@@ -0,0 +1,409 @@
+{
+  "title": "Grafana Play Home",
+  "services": {
+    "filter": {
+      "list": [],
+      "time": {
+        "from": "now-15m",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "test",
+      "height": "190px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "error": false,
+          "span": 12,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "html",
+          "content": "<h3 class=\"text-center\">Welcome to grafana demo, playground and interactive tutorial site.</h2>\n\n<div class=\"row-fluid\">\n\t<div class=\"span4\">\n\t\t<h4>Feature showcases</h2>\n\t\t<ul>\n\t\t\t<li>\n\t\t\t\t<a href=\"#/dashboard/file/graph-styles.json\">Graphs styles</a>\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#/dashboard/file/templated-graphs.json\">Templated graphs</a>\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#/dashboard/file/templated-graphs-nested.json\">Templated graphs nested</a>\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#/dashboard/file/annotations.json\">Annotations</a>\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#/dashboard/file/white-theme.json\">White theme</a>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n\t<div class=\"span4\">\n\t\t<h4>Graphite tutorials</h2>\n\t\t<ul>\n\t\t\t<li>\n\t\t\t\tGraphite introduction (TODO)\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\tBasic functions (TODO)\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\tAdvanced functions (TODO)\n\t\t\t</li>\n\t\t\t<li>\n\t\t\t\tTips and tricks (TODO)\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n\t<div class=\"span4\">\n\t\t<h4>InfluxDB examples</h2>\n\t\t<ul>\n\t\t\t<li>\n\t\t\t\tTODO\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</div>\n\n<script type=\"text/javascript\">(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\nm=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n})(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n\nga('create', 'UA-47280256-1', 'grafana.org');\nga('send', 'pageview');</script>",
+          "style": {},
+          "title": "Grafana demo site"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "test",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 3,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(scaleToSeconds(apps.fakesite.*.counters.requests.count,1),2)"
+            }
+          ],
+          "aliasColors": {
+            "web_server_04": "#3F6833",
+            "web_server_03": "#508642",
+            "web_server_02": "#7EB26D",
+            "web_server_01": "#B7DBAB"
+          },
+          "aliasYAxis": {},
+          "title": "server requests",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": true,
+            "max": true,
+            "current": true,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "alias(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count,1),'logins')"
+            },
+            {
+              "target": "alias(timeShift(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count,1),'1h'),'logins (-1 hour)')"
+            }
+          ],
+          "aliasColors": {
+            "logins": "#7EB26D",
+            "logins (-1 day)": "#447EBC"
+          },
+          "aliasYAxis": {},
+          "title": "logins",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "",
+      "height": "300px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 4,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "bytes",
+            "none"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": true,
+            "max": false,
+            "current": true,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "alias(scale(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count,1),1000000),'memory')"
+            },
+            {
+              "target": "alias(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count,1),'cpu')"
+            }
+          ],
+          "aliasColors": {
+            "cpu": "#E24D42"
+          },
+          "aliasYAxis": {
+            "cpu": 2
+          },
+          "title": "Memory / CPU",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "span": 8,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "ms",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": false,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": true,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "client side full page load",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "test",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "dark",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": false
+    },
+    {
+      "type": "annotations",
+      "enable": false
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [
+    "showcase",
+    "startpage",
+    "home",
+    "default"
+  ],
+  "timezone": "browser"
+}

+ 796 - 0
data/dashboards/graph-styles.json

@@ -0,0 +1,796 @@
+{
+  "title": "Graph styles",
+  "services": {
+    "filter": {
+      "list": [],
+      "time": {
+        "from": "now-15m",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "Simple graph",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(scaleToSeconds(apps.backend.*.counters.requests.count,1),2)"
+            }
+          ],
+          "aliasColors": {
+            "web_server_04": "#E24D42",
+            "web_server_03": "#508642",
+            "web_server_02": "#EAB839",
+            "web_server_01": "#EF843C"
+          },
+          "aliasYAxis": {},
+          "title": "Simple graph",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Simple graph\n- Click on the title and select edit to open edit mode\n- The display styles tab allows you change line width, fill, stacking, and more\n- You can change a series color by clicking the colored line in the legend ",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Stacked Graph",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 2,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(scaleToSeconds(apps.fakesite.*.counters.requests.count,1),2)"
+            }
+          ],
+          "aliasColors": {
+            "web_server_04": "#E24D42",
+            "web_server_03": "#EF843C",
+            "web_server_02": "#EAB839",
+            "web_server_01": "#F2C96D"
+          },
+          "aliasYAxis": {},
+          "title": "Stacked lines",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Stacked graph\n- This graph shows stacked series, with area fill and 2px line width\n- We have also added legend values. These can be enabled in the Grid & Axes tab in edit mode. \n- Legend values can be Min, Max, Total, Current and Average",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Staircase line",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "ms",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": true,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(statsd.fakesite.timers.ads_timer.upper_90,4)"
+            },
+            {
+              "target": "aliasByNode(statsd.fakesite.timers.ads_timer.upper_90,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "Staircase line",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Staircase & Y-Axis format\n- In display styles tab you can switch to staircase line \n- In Axes & Grid tab you can change to different Y units & formats.\n",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Right Y-Axis",
+      "height": "300px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "bytes",
+            "none"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": true,
+            "max": false,
+            "current": true,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "alias(scale(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count,1),1000000),'memory')"
+            },
+            {
+              "target": "alias(scaleToSeconds(apps.fakesite.web_server_02.counters.requests.count,1),'cpu')"
+            }
+          ],
+          "aliasColors": {
+            "cpu": "#E24D42"
+          },
+          "aliasYAxis": {
+            "cpu": 2
+          },
+          "title": "Memory / CPU",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Second Y-Axis\n- Click on the series legend color line to open the color selector\n- In the series color selector popup you can also move the series to the Right-Y axis\n- Multiple Y-Axis are great for showing to related series that have different magnitudes ",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "test",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "none",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "null",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(apps.fakesite.web_server_01.counters.request_status.code_404.count,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "Null point mode",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Null point mode\n- This option under Display styles tab controls how null values are handled\n- The graph to left shows how the default \"null\" looks. \n- __null__   null values are left null and this leaves empty spaces in the graph\n- __null as zero__   null values are drawn as zero values\n- __connected__   null values are ignored and the line jumps directly to the next value.",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Thresholds",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "none",
+            "short"
+          ],
+          "grid": {
+            "max": 700,
+            "min": 0,
+            "threshold1": 400,
+            "threshold2": 600,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": true,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(apps.fakesite.web_server_01.counters.requests.count,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9",
+            "requests": "#6ED0E0"
+          },
+          "aliasYAxis": {},
+          "title": "Thresholds",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Thresholds\n- You can define thresholds in the Grid & Axes tab in edit mode \n- You can define one or two thresholds, color is also changeable. \n- You can have lower bound thresholds as well. ",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Bars",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "ms",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": false,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": true,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": true,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "Bars",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Bars\n- In display styles tab you can switch from line to bars\n- The width of the bar is relative to the pixel width between two values\n",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Graphite PNG",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "ms",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": false,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 2,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(apps.backend.*.counters.requests.count,2)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "Graphite PNG",
+          "datasource": null,
+          "renderer": "png",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Graphite PNG support\n- You can switch from client side rendering to graphite's server side PNG rendering\n- You cannot click and drag to zoom in in this render mode.\n",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "dark",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": false
+    },
+    {
+      "type": "annotations",
+      "enable": false
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [
+    "showcase",
+    "annotations"
+  ],
+  "timezone": "browser"
+}

+ 288 - 0
data/dashboards/templated-graphs-nested.json

@@ -0,0 +1,288 @@
+{
+  "title": "Templated Graphs Nested",
+  "services": {
+    "filter": {
+      "list": [
+        {
+          "type": "filter",
+          "name": "app",
+          "query": "apps.*",
+          "includeAll": true,
+          "options": [
+            {
+              "text": "All",
+              "value": "{backend,fakesite}"
+            },
+            {
+              "text": "backend",
+              "value": "backend"
+            },
+            {
+              "text": "fakesite",
+              "value": "fakesite"
+            }
+          ],
+          "current": {
+            "text": "backend",
+            "value": "backend"
+          }
+        },
+        {
+          "type": "filter",
+          "name": "server",
+          "query": "apps.[[app]].*",
+          "includeAll": true,
+          "options": [
+            {
+              "text": "All",
+              "value": "{backend_01,backend_02,backend_03,backend_04}"
+            },
+            {
+              "text": "backend_01",
+              "value": "backend_01"
+            },
+            {
+              "text": "backend_02",
+              "value": "backend_02"
+            },
+            {
+              "text": "backend_03",
+              "value": "backend_03"
+            },
+            {
+              "text": "backend_04",
+              "value": "backend_04"
+            }
+          ],
+          "current": {
+            "text": "All",
+            "value": "{backend_01,backend_02,backend_03,backend_04}"
+          }
+        }
+      ],
+      "time": {
+        "from": "now-6h",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "Row1",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 12,
+          "editable": true,
+          "type": "graphite",
+          "loadingEditor": false,
+          "datasource": null,
+          "renderer": "flot",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "annotate": {
+            "enable": false
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "groupByNode(apps.[[app]].[[server]].counters.requests.count,2,'sum')"
+            }
+          ],
+          "aliasColors": {
+            "highres.test": "#1F78C1",
+            "scale(highres.test,3)": "#6ED0E0",
+            "mobile": "#6ED0E0",
+            "tablet": "#EAB839"
+          },
+          "aliasYAxis": {},
+          "title": "Traffic"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Row1",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 12,
+          "editable": true,
+          "type": "graphite",
+          "loadingEditor": false,
+          "datasource": null,
+          "renderer": "flot",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "annotate": {
+            "enable": false
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(movingAverage(scaleToSeconds(apps.[[app]].[[server]].counters.requests.count,60),10),1,2)"
+            }
+          ],
+          "aliasColors": {
+            "highres.test": "#1F78C1",
+            "scale(highres.test,3)": "#6ED0E0",
+            "mobile": "#6ED0E0",
+            "tablet": "#EAB839"
+          },
+          "aliasYAxis": {},
+          "title": "Sessions / min"
+        }
+      ],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "dark",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": true
+    },
+    {
+      "type": "annotations",
+      "enable": false
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [
+    "showcase",
+    "templated"
+  ],
+  "timezone": "browser"
+}

+ 272 - 0
data/dashboards/templated-graphs.json

@@ -0,0 +1,272 @@
+{
+  "title": "Templated Graphs",
+  "services": {
+    "filter": {
+      "list": [
+        {
+          "type": "filter",
+          "name": "root",
+          "query": "statsd.fakesite.counters.session_start.*",
+          "includeAll": true,
+          "options": [
+            {
+              "text": "All",
+              "value": "{desktop,mobile,tablet}"
+            },
+            {
+              "text": "desktop",
+              "value": "desktop"
+            },
+            {
+              "text": "mobile",
+              "value": "mobile"
+            },
+            {
+              "text": "tablet",
+              "value": "tablet"
+            }
+          ],
+          "current": {
+            "text": "All",
+            "value": "{desktop,mobile,tablet}"
+          }
+        }
+      ],
+      "time": {
+        "from": "now-6h",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "Row1",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 12,
+          "editable": true,
+          "type": "graphite",
+          "loadingEditor": false,
+          "datasource": null,
+          "renderer": "flot",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "annotate": {
+            "enable": false
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 1,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(summarize(statsd.fakesite.counters.session_start.[[root]].count,'1min','sum'),4)"
+            }
+          ],
+          "aliasColors": {
+            "highres.test": "#1F78C1",
+            "scale(highres.test,3)": "#6ED0E0",
+            "mobile": "#6ED0E0",
+            "tablet": "#EAB839"
+          },
+          "aliasYAxis": {},
+          "title": "Device sessions"
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "Row1",
+      "height": "350px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "loadingEditor": false,
+          "datasource": null,
+          "renderer": "flot",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "annotate": {
+            "enable": false
+          },
+          "resolution": 100,
+          "lines": false,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": true,
+          "stack": true,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(summarize(statsd.fakesite.counters.session_start.[[root]].count,'1h','sum'),4)"
+            }
+          ],
+          "aliasColors": {
+            "highres.test": "#1F78C1",
+            "scale(highres.test,3)": "#6ED0E0",
+            "tablet": "#EAB839",
+            "desktop": "#7EB26D",
+            "mobile": "#6ED0E0"
+          },
+          "aliasYAxis": {},
+          "title": "Device sessions (1h)"
+        },
+        {
+          "error": false,
+          "span": 6,
+          "editable": true,
+          "type": "text",
+          "loadingEditor": false,
+          "mode": "markdown",
+          "content": "#### Templated metric queries / graphs \n- In dashboard settings, in the Controls tab / Feature toggles. You can enable 'Filtering' \n- This feature when enabled will show you a bar bellow the menu.\n- In this bar you can add filters, or what should be named templated metric segments. \n- A filter is a query for a specific metric segment\n- Open any graph in this dashboard and edit mode and you can see that the [[device]] filter is used instead of a wildcard.\n- Try clicking the All link in the filter menu at the top, change device and see that all graphs change to only show values for that device. ",
+          "style": {},
+          "title": "Description"
+        }
+      ],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "dark",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": true
+    },
+    {
+      "type": "annotations",
+      "enable": false
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [
+    "showcase",
+    "templated"
+  ],
+  "timezone": "browser"
+}

File diff suppressed because it is too large
+ 0 - 0
data/dashboards/testing-save.json


+ 519 - 0
data/dashboards/white-theme.json

@@ -0,0 +1,519 @@
+{
+  "title": "White theme",
+  "services": {
+    "filter": {
+      "list": [],
+      "time": {
+        "from": "now-1h",
+        "to": "now"
+      }
+    }
+  },
+  "rows": [
+    {
+      "title": "test",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 3,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": false,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(scaleToSeconds(apps.fakesite.*.counters.requests.count,1),2)"
+            }
+          ],
+          "aliasColors": {
+            "web_server_04": "#3F6833",
+            "web_server_03": "#508642",
+            "web_server_02": "#7EB26D",
+            "web_server_01": "#B7DBAB"
+          },
+          "aliasYAxis": {},
+          "title": "server requests",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.request_status.code_302.count,1),4),2)"
+            }
+          ],
+          "aliasColors": {
+            "logins": "#7EB26D",
+            "logins (-1 day)": "#447EBC"
+          },
+          "aliasYAxis": {},
+          "title": "logins",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "",
+      "height": "300px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 4,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "bytes",
+            "none"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 0,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": false,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": true,
+            "max": false,
+            "current": true,
+            "total": false,
+            "avg": false
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "alias(scale(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count,1),1000000),'memory')"
+            },
+            {
+              "target": "alias(scaleToSeconds(apps.fakesite.web_server_02.counters.requests.count,1),'cpu')"
+            }
+          ],
+          "aliasColors": {
+            "cpu": "#E24D42"
+          },
+          "aliasYAxis": {
+            "cpu": 2
+          },
+          "title": "Memory / CPU",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "span": 8,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "ms",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": false,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": true,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"
+            }
+          ],
+          "aliasColors": {
+            "upper_75": "#EAB839",
+            "upper_50": "#7EB26D",
+            "upper_25": "#BA43A9"
+          },
+          "aliasYAxis": {},
+          "title": "client side full page load",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    },
+    {
+      "title": "test",
+      "height": "250px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.request_status.code_302.count,1),4),2)"
+            }
+          ],
+          "aliasColors": {
+            "logins": "#7EB26D",
+            "logins (-1 day)": "#447EBC",
+            "web_server_03": "#1F78C1",
+            "web_server_02": "#6ED0E0",
+            "web_server_01": "#64B0C8"
+          },
+          "aliasYAxis": {},
+          "title": "logins",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        },
+        {
+          "span": 6,
+          "editable": true,
+          "type": "graphite",
+          "x-axis": true,
+          "y-axis": true,
+          "scale": 1,
+          "y_formats": [
+            "short",
+            "short"
+          ],
+          "grid": {
+            "max": null,
+            "min": 0,
+            "threshold1": null,
+            "threshold2": null,
+            "threshold1Color": "rgba(216, 200, 27, 0.27)",
+            "threshold2Color": "rgba(234, 112, 112, 0.22)"
+          },
+          "resolution": 100,
+          "lines": true,
+          "fill": 1,
+          "linewidth": 2,
+          "points": false,
+          "pointradius": 5,
+          "bars": false,
+          "stack": true,
+          "spyable": true,
+          "options": false,
+          "legend": {
+            "show": true,
+            "values": true,
+            "min": false,
+            "max": false,
+            "current": false,
+            "total": false,
+            "avg": true
+          },
+          "interactive": true,
+          "legend_counts": true,
+          "timezone": "browser",
+          "percentage": false,
+          "zerofill": true,
+          "nullPointMode": "connected",
+          "steppedLine": false,
+          "tooltip": {
+            "value_type": "cumulative",
+            "query_as_alias": true
+          },
+          "targets": [
+            {
+              "target": "aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.request_status.code_302.count,1),4),2)"
+            }
+          ],
+          "aliasColors": {
+            "logins": "#7EB26D",
+            "logins (-1 day)": "#447EBC",
+            "web_server_03": "#E24D42",
+            "web_server_02": "#EF843C",
+            "web_server_01": "#EAB839"
+          },
+          "aliasYAxis": {},
+          "title": "logins",
+          "datasource": null,
+          "renderer": "flot",
+          "annotate": {
+            "enable": false
+          }
+        }
+      ],
+      "notice": false
+    }
+  ],
+  "editable": true,
+  "failover": false,
+  "panel_hints": true,
+  "style": "light",
+  "pulldowns": [
+    {
+      "type": "filtering",
+      "collapse": false,
+      "notice": false,
+      "enable": false
+    },
+    {
+      "type": "annotations",
+      "enable": false
+    }
+  ],
+  "nav": [
+    {
+      "type": "timepicker",
+      "collapse": false,
+      "notice": false,
+      "enable": true,
+      "status": "Stable",
+      "time_options": [
+        "5m",
+        "15m",
+        "1h",
+        "6h",
+        "12h",
+        "24h",
+        "2d",
+        "7d",
+        "30d"
+      ],
+      "refresh_intervals": [
+        "5s",
+        "10s",
+        "30s",
+        "1m",
+        "5m",
+        "15m",
+        "30m",
+        "1h",
+        "2h",
+        "1d"
+      ],
+      "now": true
+    }
+  ],
+  "loader": {
+    "save_gist": false,
+    "save_elasticsearch": true,
+    "save_local": true,
+    "save_default": true,
+    "save_temp": true,
+    "save_temp_ttl_enable": true,
+    "save_temp_ttl": "30d",
+    "load_gist": false,
+    "load_elasticsearch": true,
+    "load_elasticsearch_size": 20,
+    "load_local": false,
+    "hide": false
+  },
+  "refresh": false,
+  "tags": [],
+  "timezone": "browser"
+}

+ 1 - 0
grafana

@@ -0,0 +1 @@
+Subproject commit 91a6ae756f30744afe82dabbb5caa7f43d6f7e5a

BIN
grafana-pro


+ 18 - 0
grafana-pro.sublime-project

@@ -0,0 +1,18 @@
+{
+	"folders":
+	[
+		{
+			"follow_symlinks": true,
+			"path": ".",
+			"folder_exclude_patterns": [
+	       "node_modules",
+	       "grafana"
+	     ]
+		}
+	],
+	"settings":
+  {
+    "tab_size": 2,
+    "trim_trailing_white_space_on_save": true
+  }
+}

+ 31 - 0
grafana.go

@@ -0,0 +1,31 @@
+package main
+
+import (
+	"os"
+	"time"
+
+	log "github.com/alecthomas/log4go"
+	"github.com/torkelo/grafana-pro/backend/server"
+)
+
+func main() {
+	port := os.Getenv("PORT")
+	if port == "" {
+		port = "3838"
+	}
+
+	log.Info("Starting Grafana-Pro v.1-alpha")
+
+	server, err := server.NewServer(port)
+	if err != nil {
+		time.Sleep(time.Second)
+		panic(err)
+	}
+
+	err = server.ListenAndServe()
+	if err != nil {
+		log.Error("ListenAndServe failed: ", err)
+	}
+
+	time.Sleep(time.Millisecond * 2000)
+}

+ 68 - 0
package.json

@@ -0,0 +1,68 @@
+{
+  "author": {
+    "name": "Torkel Ödegaard",
+    "company": "Coding Instinct AB"
+  },
+  "name": "grafana",
+  "version": "1.7.0-rc1",
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/torkelo/grafana.git"
+  },
+  "devDependencies": {
+    "expect.js": "~0.2.0",
+    "glob": "~3.2.7",
+    "grunt": "~0.4.0",
+    "grunt-angular-templates": "^0.5.5",
+    "grunt-cli": "~0.1.13",
+    "grunt-contrib-clean": "~0.5.0",
+    "grunt-contrib-compress": "~0.5.2",
+    "grunt-contrib-concat": "^0.4.0",
+    "grunt-contrib-connect": "~0.5.0",
+    "grunt-contrib-copy": "~0.5.0",
+    "grunt-contrib-cssmin": "~0.6.1",
+    "grunt-contrib-htmlmin": "~0.1.3",
+    "grunt-contrib-jshint": "~0.10.0",
+    "grunt-contrib-less": "~0.7.0",
+    "grunt-contrib-requirejs": "~0.4.1",
+    "grunt-contrib-uglify": "~0.2.4",
+    "grunt-filerev": "^0.2.1",
+    "grunt-git-describe": "~2.3.2",
+    "grunt-karma": "~0.8.3",
+    "grunt-ngmin": "0.0.3",
+    "grunt-string-replace": "~0.2.4",
+    "grunt-usemin": "^2.1.1",
+    "jshint-stylish": "~0.1.5",
+    "karma": "~0.12.16",
+    "karma-chrome-launcher": "~0.1.4",
+    "karma-coffee-preprocessor": "~0.1.2",
+    "karma-coverage": "^0.2.5",
+    "karma-coveralls": "^0.1.4",
+    "karma-expect": "~1.1.0",
+    "karma-firefox-launcher": "~0.1.3",
+    "karma-html2js-preprocessor": "~0.1.0",
+    "karma-jasmine": "~0.2.2",
+    "karma-mocha": "~0.1.4",
+    "karma-phantomjs-launcher": "~0.1.1",
+    "karma-requirejs": "~0.2.1",
+    "karma-script-launcher": "~0.1.0",
+    "load-grunt-tasks": "~0.2.0",
+    "mocha": "~1.16.1",
+    "requirejs": "~2.1.14",
+    "rjs-build-analysis": "0.0.3"
+  },
+  "engines": {
+    "node": "0.10.x",
+    "npm": "1.2.x"
+  },
+  "scripts": {
+    "test": "grunt test",
+    "coveralls": "grunt karma:coveralls && rm -rf ./coverage"
+  },
+  "license": "Apache License",
+  "dependencies": {
+    "grunt-jscs-checker": "^0.4.4",
+    "karma-sinon": "^1.0.3",
+    "sinon": "^1.10.3"
+  }
+}

+ 37 - 0
tasks/build_task.js

@@ -0,0 +1,37 @@
+module.exports = function(grunt) {
+
+  // Concat and Minify the src directory into dist
+  grunt.registerTask('build', [
+    'jshint:source',
+    'jshint:tests',
+    'clean:on_start',
+    'less:src',
+    'concat:css',
+    'copy:everything_but_less_to_temp',
+    'htmlmin:build',
+    'ngtemplates',
+    'cssmin:build',
+    'build:grafanaVersion',
+    'ngmin:build',
+    'requirejs:build',
+    'concat:js',
+    'filerev',
+    'usemin',
+    'clean:temp',
+    'uglify:dest'
+  ]);
+
+
+  grunt.registerTask('build:grafanaVersion', function() {
+    grunt.config('string-replace.config', {
+      files: {
+        '<%= tempDir %>/app/app.js': '<%= tempDir %>/app/app.js'
+      },
+      options: {
+        replacements: [{ pattern: /@grafanaVersion@/g,  replacement: '<%= pkg.version %>' }]
+      }
+    });
+    grunt.task.run('string-replace:config');
+  });
+
+};

+ 5 - 0
tasks/default_task.js

@@ -0,0 +1,5 @@
+// Lint and build CSS
+module.exports = function(grunt) {
+  grunt.registerTask('default', ['jscs', 'jshint', 'less:src', 'concat:css']);
+  grunt.registerTask('test', ['default', 'karma:test']);
+};

+ 31 - 0
tasks/distribute_task.js

@@ -0,0 +1,31 @@
+module.exports = function(grunt) {
+
+  // build, then zip and upload to s3
+  grunt.registerTask('distribute', [
+    'distribute:load_s3_config',
+    'build',
+    'compress:zip',
+    'compress:tgz',
+    's3:dist',
+    'clean:temp'
+  ]);
+
+  // build, then zip and upload to s3
+  grunt.registerTask('release', [
+  //  'distribute:load_s3_config',
+    'build',
+    'compress:zip_release',
+    'compress:tgz_release',
+    //'s3:release',
+    //'clean:temp'
+  ]);
+
+  // collect the key and secret from the .aws-config.json file, finish configuring the s3 task
+  grunt.registerTask('distribute:load_s3_config', function () {
+    var config = grunt.file.readJSON('.aws-config.json');
+    grunt.config('s3.options', {
+      key: config.key,
+      secret: config.secret
+    });
+  });
+}

+ 7 - 0
tasks/options/clean.js

@@ -0,0 +1,7 @@
+module.exports = function(config) {
+  return {
+    on_start: ['<%= destDir %>', '<%= tempDir %>'],
+    temp: ['<%= tempDir %>'],
+    docs: ['<%= docsDir %>']
+  };
+};

+ 76 - 0
tasks/options/compress.js

@@ -0,0 +1,76 @@
+module.exports = function(config) {
+  return {
+    zip: {
+      options: {
+        archive: '<%= tempDir %>/<%= pkg.name %>-latest.zip'
+      },
+      files : [
+        {
+          expand: true,
+          cwd: '<%= destDir %>',
+          src: ['**/*'],
+          dest: '<%= pkg.name %>/',
+        },
+        {
+          expand: true,
+          dest: '<%= pkg.name %>/',
+          src: ['LICENSE.md', 'README.md', 'NOTICE.md'],
+        }
+      ]
+    },
+    tgz: {
+      options: {
+        archive: '<%= tempDir %>/<%= pkg.name %>-latest.tar.gz'
+      },
+      files : [
+        {
+          expand: true,
+          cwd: '<%= destDir %>',
+          src: ['**/*'],
+          dest: '<%= pkg.name %>/',
+        },
+        {
+          expand: true,
+          src: ['LICENSE.md', 'README.md', 'NOTICE.md'],
+          dest: '<%= pkg.name %>/',
+        }
+      ]
+    },
+    zip_release: {
+      options: {
+        archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.zip'
+      },
+      files : [
+        {
+          expand: true,
+          cwd: '<%= destDir %>',
+          src: ['**/*'],
+          dest: '<%= pkg.name %>-<%= pkg.version %>/',
+        },
+        {
+          expand: true,
+          src: ['LICENSE.md', 'README.md', 'NOTICE.md'],
+          dest: '<%= pkg.name %>-<%= pkg.version %>/',
+        }
+      ]
+    },
+    tgz_release: {
+      options: {
+        archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz'
+      },
+      files : [
+        {
+          expand: true,
+          cwd: '<%= destDir %>',
+          src: ['**/*'],
+          dest: '<%= pkg.name %>-<%= pkg.version %>/',
+        },
+        {
+          expand: true,
+          src: ['LICENSE.md', 'README.md', 'NOTICE.md'],
+          dest: '<%= pkg.name %>-<%= pkg.version %>/',
+        }
+      ]
+    }
+  };
+};

+ 22 - 0
tasks/options/concat.js

@@ -0,0 +1,22 @@
+module.exports = function(config) {
+  return {
+    css: {
+      src: [
+        '<%= srcDir %>/css/normalize.min.css',
+        '<%= srcDir %>/css/timepicker.css',
+        '<%= srcDir %>/css/spectrum.css',
+        '<%= srcDir %>/css/animate.min.css',
+        '<%= srcDir %>/css/bootstrap.dark.min.css'
+      ],
+      dest: '<%= srcDir %>/css/default.min.css'
+    },
+    js: {
+      src: [
+        '<%= destDir %>/vendor/require/require.js',
+        '<%= destDir %>/app/components/require.config.js',
+        '<%= destDir %>/app/app.js',
+      ],
+      dest: '<%= destDir %>/app/app.js'
+    },
+  };
+};

+ 20 - 0
tasks/options/connect.js

@@ -0,0 +1,20 @@
+module.exports = function(config) {
+  return {
+    dev: {
+      options: {
+        port: 5601,
+        hostname: '*',
+        base: config.srcDir,
+        keepalive: true
+      }
+    },
+    dist: {
+      options: {
+        port: 5605,
+        hostname: '*',
+        base: config.destDir,
+        keepalive: true
+      }
+    },
+  }
+};

+ 11 - 0
tasks/options/copy.js

@@ -0,0 +1,11 @@
+module.exports = function(config) {
+  return {
+    // copy source to temp, we will minify in place for the dist build
+    everything_but_less_to_temp: {
+      cwd: '<%= srcDir %>',
+      expand: true,
+      src: ['**/*', '!**/*.less', '!config.js'],
+      dest: '<%= tempDir %>'
+    }
+  };
+};

+ 10 - 0
tasks/options/cssmin.js

@@ -0,0 +1,10 @@
+module.exports = function(config) {
+  return {
+    build: {
+      expand: true,
+      cwd: '<%= tempDir %>',
+      src: '**/*.css',
+      dest: '<%= tempDir %>'
+    }
+  };
+};

+ 17 - 0
tasks/options/filerev.js

@@ -0,0 +1,17 @@
+module.exports = function(config) {
+  return {
+    options: {
+      encoding: 'utf8',
+      algorithm: 'md5',
+      length: 8,
+    },
+    css: {
+      src: '<%= destDir %>/css/default.min.css',
+      dest: '<%= destDir %>/css'
+    },
+    js: {
+      src: '<%= destDir %>/app/app.js',
+      dest: '<%= destDir %>/app'
+    }
+  };
+};

+ 7 - 0
tasks/options/git-describe.js

@@ -0,0 +1,7 @@
+module.exports = function(config) {
+  return {
+    me: {
+      // Target-specific file lists and/or options go here.
+    }
+  };
+};

+ 18 - 0
tasks/options/htmlmin.js

@@ -0,0 +1,18 @@
+module.exports = function(config) {
+  return {
+    build: {
+      options:{
+        removeComments: true,
+        collapseWhitespace: true
+      },
+      expand: true,
+      cwd: '<%= tempDir %>',
+      src: [
+        //'index.html',
+        'app/panels/**/*.html',
+        'app/partials/**/*.html'
+      ],
+      dest: '<%= tempDir %>'
+    }
+  };
+};

+ 22 - 0
tasks/options/jscs.js

@@ -0,0 +1,22 @@
+module.exports = function(config) {
+  return {
+    src: [
+      'Gruntfile.js',
+      '<%= srcDir %>/app/**/*.js',
+      '!<%= srcDir %>/app/panels/*/{lib,leaflet}/*',
+      '!<%= srcDir %>/app/dashboards/*'
+    ],
+    options: {
+      config: ".jscs.json",
+    },
+  };
+};
+
+/*
+ "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
+    "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
+    "disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
+    "disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
+    "requireRightStickedOperators": ["!"],
+    "requireLeftStickedOperators": [","],
+   */

+ 26 - 0
tasks/options/jshint.js

@@ -0,0 +1,26 @@
+module.exports = function(config) {
+  return {
+    source: {
+      files: {
+        src: ['Gruntfile.js', '<%= srcDir %>/app/**/*.js'],
+      }
+    },
+    tests: {
+      files: {
+        src: ['<%= srcDir %>/test/**/*.js'],
+      }
+    },
+    options: {
+      jshintrc: true,
+      reporter: require('jshint-stylish'),
+      ignores: [
+        'node_modules/*',
+        'dist/*',
+        'sample/*',
+        '<%= srcDir %>/vendor/*',
+        '<%= srcDir %>/app/panels/*/{lib,leaflet}/*',
+        '<%= srcDir %>/app/dashboards/*'
+      ]
+    }
+  };
+};

+ 27 - 0
tasks/options/karma.js

@@ -0,0 +1,27 @@
+module.exports = function(config) {
+  return {
+    dev: {
+      configFile: 'src/test/karma.conf.js',
+      singleRun: false,
+    },
+    debug: {
+      configFile: 'src/test/karma.conf.js',
+      singleRun: false,
+      browsers: ['Chrome']
+    },
+    test: {
+      configFile: 'src/test/karma.conf.js',
+    },
+    coveralls: {
+      configFile: 'src/test/karma.conf.js',
+      reporters: ['dots','coverage','coveralls'],
+      preprocessors: {
+        'src/app/**/*.js': ['coverage']
+      },
+      coverageReporter: {
+        type: 'lcov',
+        dir: 'coverage/'
+      }
+    }
+  };
+};

+ 25 - 0
tasks/options/less.js

@@ -0,0 +1,25 @@
+module.exports = function(config) {
+  return {
+    // this is the only task, other than copy, that runs on the src directory, since we don't really need
+    // the less files in the dist. Everything else runs from on temp, and require copys everything
+    // from temp -> dist
+    dist:{
+      expand: true,
+      cwd:'<%= srcDir %>/vendor/bootstrap/less/',
+      src: ['bootstrap.dark.less', 'bootstrap.light.less'],
+      dest: '<%= tempDir %>/css/',
+    },
+    // Compile in place when not building
+    src:{
+      options: {
+        paths: ["<%= srcDir %>/vendor/bootstrap/less", "<%= srcDir %>/css/less"],
+        yuicompress:true
+      },
+      files: {
+        "<%= srcDir %>/css/bootstrap.dark.min.css": "<%= srcDir %>/css/less/bootstrap.dark.less",
+        "<%= srcDir %>/css/bootstrap.light.min.css": "<%= srcDir %>/css/less/bootstrap.light.less",
+        "<%= srcDir %>/css/bootstrap-responsive.min.css": "<%= srcDir %>/css/less/grafana-responsive.less"
+      }
+    }
+  };
+};

+ 9 - 0
tasks/options/meta.js

@@ -0,0 +1,9 @@
+module.exports = function(config) {
+  return {
+    banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
+    '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+    '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
+    ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
+    ' Licensed <%= pkg.license %> */\n\n'
+  };
+};

+ 19 - 0
tasks/options/ngmin.js

@@ -0,0 +1,19 @@
+module.exports = function(config) {
+  return {
+    build: {
+      expand:true,
+      cwd:'<%= tempDir %>',
+      src: [
+        'app/controllers/**/*.js',
+        'app/directives/**/*.js',
+        'app/services/**/*.js',
+        'app/filters/**/*.js',
+        'app/panels/**/*.js',
+        'app/routes/**/*.js',
+        'app/app.js',
+        'vendor/angular/**/*.js',
+      ],
+      dest: '<%= tempDir %>'
+    }
+  };
+};

+ 18 - 0
tasks/options/ngtemplates.js

@@ -0,0 +1,18 @@
+module.exports = function(config) {
+  return {
+    grafana: {
+      cwd:  '<%= tempDir %>',
+      src:  ['app/**/*.html', '!app/panels/*/module.html'],
+      dest: '<%= tempDir %>/app/components/partials.js',
+      options: {
+        bootstrap: function(module, script) {
+          return "define('components/partials', ['angular'], function(angular) { \n" +
+            "angular.module('grafana').run(['$templateCache', function($templateCache) { \n" +
+                script +
+            '\n}]);' +
+          '\n});';
+        }
+      }
+    }
+  };
+};

+ 83 - 0
tasks/options/requirejs.js

@@ -0,0 +1,83 @@
+module.exports = function(config,grunt) {
+  var _c = {
+    build: {
+      options: {
+        appDir: '<%= tempDir %>',
+        dir: '<%= destDir %>',
+
+        mainConfigFile: '<%= tempDir %>/app/components/require.config.js',
+        modules: [], // populated below
+
+        optimize: 'none',
+        optimizeCss: 'none',
+        optimizeAllPluginResources: false,
+
+        paths: { config: '../config.sample' }, // fix, fallbacks need to be specified
+
+        removeCombined: true,
+        findNestedDependencies: true,
+        normalizeDirDefines: 'all',
+        inlineText: true,
+        skipPragmas: true,
+
+        done: function (done, output) {
+          var duplicates = require('rjs-build-analysis').duplicates(output);
+
+          if (duplicates.length > 0) {
+            grunt.log.subhead('Duplicates found in requirejs build:');
+            grunt.log.warn(duplicates);
+            done(new Error('r.js built duplicate modules, please check the excludes option.'));
+          }
+
+          done();
+        }
+      }
+    }
+  };
+
+  // setup the modules require will build
+  var requireModules = _c.build.options.modules = [
+    {
+      // main/common module
+      name: 'app',
+      include: [
+        'css',
+        'kbn',
+        'text',
+        'jquery',
+        'angular',
+        'settings',
+        'bootstrap',
+        'modernizr',
+        'timepicker',
+        'datepicker',
+        'underscore',
+        'filters/all',
+        'jquery.flot',
+        'services/all',
+        'angular-strap',
+        'directives/all',
+        'jquery.flot.pie',
+        'angular-dragdrop',
+      ]
+    }
+  ];
+
+  var fs = require('fs');
+  var panelPath = config.srcDir+'/app/panels'
+
+  // create a module for each directory in src/app/panels/
+  fs.readdirSync(panelPath).forEach(function (panelName) {
+    requireModules[0].include.push('panels/'+panelName+'/module');
+    requireModules[0].include.push('text!panels/'+panelName+'/module.html');
+  });
+
+  // exclude the literal config definition from all modules
+  requireModules
+    .forEach(function (module) {
+      module.excludeShallow = module.excludeShallow || [];
+      module.excludeShallow.push('config');
+    });
+
+  return _c;
+};

+ 16 - 0
tasks/options/uglify.js

@@ -0,0 +1,16 @@
+module.exports = function(config) {
+  return {
+    dest: {
+      expand: true,
+      src: ['**/*.js', '!config.sample.js', '!app/dashboards/*.js', '!app/dashboards/**/*.js',],
+      dest: '<%= destDir %>',
+      cwd: '<%= destDir %>',
+      options: {
+        quite: true,
+        compress: true,
+        preserveComments: false,
+        banner: '<%= meta.banner %>'
+      }
+    }
+  };
+};

+ 5 - 0
tasks/options/usemin.js

@@ -0,0 +1,5 @@
+module.exports = function(config) {
+  return {
+    html: '<%= destDir %>/index.html',
+  };
+};

+ 5 - 0
tasks/options/useminPrepare.js

@@ -0,0 +1,5 @@
+module.exports = function(config) {
+  return {
+    html: 'tmp/index.html',
+  };
+};

+ 3 - 0
tasks/server_task.js

@@ -0,0 +1,3 @@
+module.exports = function(grunt) {
+  grunt.registerTask('server', ['connect:dev']);
+};

+ 35 - 0
views/index.html

@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+  <!--[if IE 8]>         <html class="no-js lt-ie9" lang="en"> <![endif]-->
+  <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width">
+
+    <title>Grafana</title>
+    <link rel="stylesheet" href="public/css/default.min.css" title="Dark">
+
+    <!-- build:js app/app.js -->
+    <script src="public/vendor/require/require.js"></script>
+    <script src="public/app/components/require.config.js"></script>
+    <!-- endbuild -->
+
+    <script>require(['app'], function (app) { app.boot(); })</script>
+
+  </head>
+
+  <body ng-cloak ng-controller="GrafanaCtrl">
+
+    <link rel="stylesheet" href="public/css/bootstrap.light.min.css" ng-if="grafana.style === 'light'">
+    <link rel="stylesheet" href="public/css/bootstrap-responsive.min.css">
+    <link rel="stylesheet" href="public/css/font-awesome.min.css">
+
+    <div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} dashboard-notice" ng-show="$last">
+      <button type="button" class="close" ng-click="dashAlerts.clear(alert)" style="padding-right:50px">&times;</button>
+      <strong>{{alert.title}}</strong> <span ng-bind-html='alert.text'></span> <div style="padding-right:10px" class='pull-right small'> {{$index + 1}} alert(s) </div>
+    </div>
+
+    <div ng-view ng-class="{'dashboard-fullscreen': fullscreen}"></div>
+
+  </body>
+</html>

Some files were not shown because too many files changed in this diff