Example #1
0
func addGettingStartedPanelToHomeDashboard(dash *simplejson.Json) {
	rows := dash.Get("rows").MustArray()
	row := simplejson.NewFromAny(rows[0])

	newpanel := simplejson.NewFromAny(map[string]interface{}{
		"type": "gettingstarted",
		"id":   123123,
		"span": 12,
	})

	panels := row.Get("panels").MustArray()
	panels = append(panels, newpanel)
	row.Set("panels", panels)
}
func TestDashboardSnapshotDBAccess(t *testing.T) {

	Convey("Testing DashboardSnapshot data access", t, func() {
		InitTestDB(t)

		Convey("Given saved snaphot", func() {
			cmd := m.CreateDashboardSnapshotCommand{
				Key: "hej",
				Dashboard: simplejson.NewFromAny(map[string]interface{}{
					"hello": "mupp",
				}),
			}
			err := CreateDashboardSnapshot(&cmd)
			So(err, ShouldBeNil)

			Convey("Should be able to get snaphot by key", func() {
				query := m.GetDashboardSnapshotQuery{Key: "hej"}
				err = GetDashboardSnapshot(&query)
				So(err, ShouldBeNil)

				So(query.Result, ShouldNotBeNil)
				So(query.Result.Dashboard.Get("hello").MustString(), ShouldEqual, "mupp")
			})

		})
	})
}
Example #3
0
func (this *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {

	sourceValue := source.Interface()

	switch v := sourceValue.(type) {
	case string:
		interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
			if replacement, exists := this.variables[match]; exists {
				return replacement
			} else {
				return match
			}
		})
		return interpolated
	case bool:
		return v
	case json.Number:
		return v
	case map[string]interface{}:
		return this.evalObject(source)
	case []interface{}:
		array := make([]interface{}, 0)
		for _, item := range v {
			array = append(array, this.evalValue(simplejson.NewFromAny(item)))
		}
		return array
	}

	return nil
}
Example #4
0
func (*InfluxdbQueryParser) parseTags(model *simplejson.Json) ([]*Tag, error) {
	var result []*Tag
	for _, t := range model.Get("tags").MustArray() {
		tagJson := simplejson.NewFromAny(t)
		tag := &Tag{}
		var err error

		tag.Key, err = tagJson.Get("key").String()
		if err != nil {
			return nil, err
		}

		tag.Value, err = tagJson.Get("value").String()
		if err != nil {
			return nil, err
		}

		operator, err := tagJson.Get("operator").String()
		if err == nil {
			tag.Operator = operator
		}

		condition, err := tagJson.Get("condition").String()
		if err == nil {
			tag.Condition = condition
		}

		result = append(result, tag)
	}

	return result, nil
}
Example #5
0
func (*InfluxdbQueryParser) parseQueryPart(model *simplejson.Json) (*QueryPart, error) {
	typ, err := model.Get("type").String()
	if err != nil {
		return nil, err
	}

	var params []string
	for _, paramObj := range model.Get("params").MustArray() {
		param := simplejson.NewFromAny(paramObj)

		stringParam, err := param.String()
		if err == nil {
			params = append(params, stringParam)
			continue
		}

		intParam, err := param.Int()
		if err == nil {
			params = append(params, strconv.Itoa(intParam))
			continue
		}

		return nil, err

	}

	qp, err := NewQueryPart(typ, params)
	if err != nil {
		return nil, err
	}

	return qp, nil
}
Example #6
0
func (qp *QueryParser) Parse(model *simplejson.Json, dsInfo *models.DataSource, queryContext *tsdb.QueryContext) (*Query, error) {
	query := &Query{TimeRange: queryContext.TimeRange}
	query.AddClusterToAlias = model.Get("addClusterToAlias").MustBool(false)
	query.AddHostToAlias = model.Get("addHostToAlias").MustBool(false)
	query.UseRawQuery = model.Get("rawQuery").MustBool(false)
	query.RawQuery = model.Get("query").MustString("")

	query.Cluster = model.Get("cluster").MustStringArray([]string{})
	query.Hosts = model.Get("hosts").MustStringArray([]string{})

	var metrics []Metric
	var err error
	for _, metricsObj := range model.Get("metrics").MustArray() {
		metricJson := simplejson.NewFromAny(metricsObj)
		var m Metric

		m.Alias = metricJson.Get("alias").MustString("")
		m.Metric, err = metricJson.Get("metric").String()
		if err != nil {
			return nil, err
		}

		metrics = append(metrics, m)
	}

	query.Metrics = metrics

	var functions []Function
	for _, functionListObj := range model.Get("functionList").MustArray() {
		functionListJson := simplejson.NewFromAny(functionListObj)
		var f Function

		f.Func = functionListJson.Get("func").MustString("")
		if err != nil {
			return nil, err
		}

		if f.Func != "" {
			functions = append(functions, f)
		}
	}

	query.FunctionList = functions

	return query, nil
}
Example #7
0
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
	executionError := ""
	annotationData := simplejson.New()

	evalContext.Rule.State = handler.GetStateFromEvaluation(evalContext)

	if evalContext.Error != nil {
		executionError = evalContext.Error.Error()
		annotationData.Set("errorMessage", executionError)
	}

	if evalContext.Firing {
		annotationData = simplejson.NewFromAny(evalContext.EvalMatches)
	}

	countStateResult(evalContext.Rule.State)
	if evalContext.ShouldUpdateAlertState() {
		handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "prev state", evalContext.PrevAlertState)

		cmd := &m.SetAlertStateCommand{
			AlertId:  evalContext.Rule.Id,
			OrgId:    evalContext.Rule.OrgId,
			State:    evalContext.Rule.State,
			Error:    executionError,
			EvalData: annotationData,
		}

		if err := bus.Dispatch(cmd); err != nil {
			handler.log.Error("Failed to save state", "error", err)
		}

		// save annotation
		item := annotations.Item{
			OrgId:       evalContext.Rule.OrgId,
			DashboardId: evalContext.Rule.DashboardId,
			PanelId:     evalContext.Rule.PanelId,
			Type:        annotations.AlertType,
			AlertId:     evalContext.Rule.Id,
			Title:       evalContext.Rule.Name,
			Text:        evalContext.GetStateModel().Text,
			NewState:    string(evalContext.Rule.State),
			PrevState:   string(evalContext.PrevAlertState),
			Epoch:       time.Now().Unix(),
			Data:        annotationData,
		}

		annotationRepo := annotations.GetRepository()
		if err := annotationRepo.Save(&item); err != nil {
			handler.log.Error("Failed to save annotation for new alert state", "error", err)
		}

		if evalContext.ShouldSendNotification() {
			handler.notifier.Notify(evalContext)
		}
	}

	return nil
}
Example #8
0
func (h *hub) run() {
	for {
		select {
		case c := <-h.register:
			h.connections[c] = true
			log.Info("Live: New connection (Total count: %v)", len(h.connections))

		case c := <-h.unregister:
			if _, ok := h.connections[c]; ok {
				log.Info("Live: Closing Connection (Total count: %v)", len(h.connections))
				delete(h.connections, c)
				close(c.send)
			}
		// hand stream subscriptions
		case sub := <-h.subChannel:
			log.Info("Live: Subscribing to: %v, remove: %v", sub.name, sub.remove)
			subscribers, exists := h.streams[sub.name]

			// handle unsubscribe
			if exists && sub.remove {
				delete(subscribers, sub.conn)
				continue
			}

			if !exists {
				subscribers = make(map[*connection]bool)
				h.streams[sub.name] = subscribers
			}
			subscribers[sub.conn] = true

			// handle stream messages
		case message := <-h.streamChannel:
			subscribers, exists := h.streams[message.Stream]
			if !exists || len(subscribers) == 0 {
				log.Info("Live: Message to stream without subscribers: %v", message.Stream)
				continue
			}

			messageBytes, _ := simplejson.NewFromAny(message).Encode()
			for sub := range subscribers {
				// check if channel is open
				if _, ok := h.connections[sub]; !ok {
					delete(subscribers, sub)
					continue
				}

				select {
				case sub.send <- messageBytes:
				default:
					close(sub.send)
					delete(h.connections, sub)
					delete(subscribers, sub)
				}
			}
		}
	}
}
Example #9
0
func findPanelQueryByRefId(panel *simplejson.Json, refId string) *simplejson.Json {
	for _, targetsObj := range panel.Get("targets").MustArray() {
		target := simplejson.NewFromAny(targetsObj)

		if target.Get("refId").MustString() == refId {
			return target
		}
	}
	return nil
}
Example #10
0
func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
	model := &Rule{}
	model.Id = ruleDef.Id
	model.OrgId = ruleDef.OrgId
	model.DashboardId = ruleDef.DashboardId
	model.PanelId = ruleDef.PanelId
	model.Name = ruleDef.Name
	model.Message = ruleDef.Message
	model.Frequency = ruleDef.Frequency
	model.State = ruleDef.State
	model.NoDataState = m.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
	model.ExecutionErrorState = m.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting"))

	for _, v := range ruleDef.Settings.Get("notifications").MustArray() {
		jsonModel := simplejson.NewFromAny(v)
		if id, err := jsonModel.Get("id").Int64(); err != nil {
			return nil, ValidationError{Reason: "Invalid notification schema"}
		} else {
			model.Notifications = append(model.Notifications, id)
		}
	}

	for index, condition := range ruleDef.Settings.Get("conditions").MustArray() {
		conditionModel := simplejson.NewFromAny(condition)
		conditionType := conditionModel.Get("type").MustString()
		if factory, exist := conditionFactories[conditionType]; !exist {
			return nil, ValidationError{Reason: "Unknown alert condition: " + conditionType}
		} else {
			if queryCondition, err := factory(conditionModel, index); err != nil {
				return nil, err
			} else {
				model.Conditions = append(model.Conditions, queryCondition)
			}
		}
	}

	if len(model.Conditions) == 0 {
		return nil, fmt.Errorf("Alert is missing conditions")
	}

	return model, nil
}
Example #11
0
func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
	this.result = simplejson.New()
	this.variables = make(map[string]string)
	this.varRegex, _ = regexp.Compile(`(\$\{\w+\})`)

	// check that we have all inputs we need
	for _, inputDef := range this.template.Get("__inputs").MustArray() {
		inputDefJson := simplejson.NewFromAny(inputDef)
		inputName := inputDefJson.Get("name").MustString()
		inputType := inputDefJson.Get("type").MustString()
		input := this.findInput(inputName, inputType)

		if input == nil {
			return nil, &DashboardInputMissingError{VariableName: inputName}
		}

		this.variables["${"+inputName+"}"] = input.Value
	}

	return simplejson.NewFromAny(this.evalObject(this.template)), nil
}
Example #12
0
func (this *DashTemplateEvaluator) evalObject(source *simplejson.Json) interface{} {
	result := make(map[string]interface{})

	for key, value := range source.MustMap() {
		if key == "__inputs" {
			continue
		}
		result[key] = this.evalValue(simplejson.NewFromAny(value))
	}

	return result
}
Example #13
0
func (qp *InfluxdbQueryParser) parseSelects(model *simplejson.Json) ([]*Select, error) {
	var result []*Select

	for _, selectObj := range model.Get("select").MustArray() {
		selectJson := simplejson.NewFromAny(selectObj)
		var parts Select

		for _, partObj := range selectJson.MustArray() {
			part := simplejson.NewFromAny(partObj)
			queryPart, err := qp.parseQueryPart(part)
			if err != nil {
				return nil, err
			}

			parts = append(parts, *queryPart)
		}

		result = append(result, &parts)
	}

	return result, nil
}
Example #14
0
func (qp *InfluxdbQueryParser) parseGroupBy(model *simplejson.Json) ([]*QueryPart, error) {
	var result []*QueryPart

	for _, groupObj := range model.Get("groupBy").MustArray() {
		groupJson := simplejson.NewFromAny(groupObj)
		queryPart, err := qp.parseQueryPart(groupJson)

		if err != nil {
			return nil, err
		}
		result = append(result, queryPart)
	}

	return result, nil
}
Example #15
0
func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
	cmd := m.SaveDashboardCommand{
		OrgId: orgId,
		Dashboard: simplejson.NewFromAny(map[string]interface{}{
			"id":    nil,
			"title": title,
			"tags":  tags,
		}),
	}

	err := SaveDashboard(&cmd)
	So(err, ShouldBeNil)

	return cmd.Result
}
Example #16
0
func TestAlertRuleExtraction(t *testing.T) {

	Convey("Parsing alert rules  from dashboard json", t, func() {

		RegisterCondition("query", func(model *simplejson.Json, index int) (Condition, error) {
			return &FakeCondition{}, nil
		})

		Convey("Parsing and validating alerts from dashboards", func() {
			json := `{
        "id": 57,
        "title": "Graphite 4",
        "originalTitle": "Graphite 4",
        "tags": ["graphite"],
        "rows": [
        {
          "panels": [
          {
            "title": "Active desktop users",
            "editable": true,
            "type": "graph",
            "id": 3,
            "targets": [
            {
              "refId": "A",
              "target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
            }
            ],
            "datasource": null,
            "alert": {
              "name": "name1",
              "message": "desc1",
              "handler": 1,
              "frequency": "60s",
              "conditions": [
              {
                "type": "query",
                "query": {"params": ["A", "5m", "now"]},
                "reducer": {"type": "avg", "params": []},
                "evaluator": {"type": ">", "params": [100]}
              }
              ]
            }
          },
          {
            "title": "Active mobile users",
            "id": 4,
            "targets": [
              {"refId": "A", "target": ""},
              {"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
            ],
            "datasource": "graphite2",
            "alert": {
              "name": "name2",
              "message": "desc2",
              "handler": 0,
              "frequency": "60s",
              "severity": "warning",
              "conditions": [
              {
                "type": "query",
                "query":  {"params": ["B", "5m", "now"]},
                "reducer": {"type": "avg", "params": []},
                "evaluator": {"type": ">", "params": [100]}
              }
              ]
            }
          }
          ]
        }
      ]
    }`
			dashJson, err := simplejson.NewJson([]byte(json))
			So(err, ShouldBeNil)

			dash := m.NewDashboardFromJson(dashJson)
			extractor := NewDashAlertExtractor(dash, 1)

			// mock data
			defaultDs := &m.DataSource{Id: 12, OrgId: 2, Name: "I am default", IsDefault: true}
			graphite2Ds := &m.DataSource{Id: 15, OrgId: 2, Name: "graphite2"}

			bus.AddHandler("test", func(query *m.GetDataSourcesQuery) error {
				query.Result = []*m.DataSource{defaultDs, graphite2Ds}
				return nil
			})

			bus.AddHandler("test", func(query *m.GetDataSourceByNameQuery) error {
				if query.Name == defaultDs.Name {
					query.Result = defaultDs
				}
				if query.Name == graphite2Ds.Name {
					query.Result = graphite2Ds
				}
				return nil
			})

			alerts, err := extractor.GetAlerts()

			Convey("Get rules without error", func() {
				So(err, ShouldBeNil)
			})

			Convey("all properties have been set", func() {
				So(len(alerts), ShouldEqual, 2)

				for _, v := range alerts {
					So(v.DashboardId, ShouldEqual, 57)
					So(v.Name, ShouldNotBeEmpty)
					So(v.Message, ShouldNotBeEmpty)
				}

				Convey("should extract handler property", func() {
					So(alerts[0].Handler, ShouldEqual, 1)
					So(alerts[1].Handler, ShouldEqual, 0)
				})

				Convey("should extract frequency in seconds", func() {
					So(alerts[0].Frequency, ShouldEqual, 60)
					So(alerts[1].Frequency, ShouldEqual, 60)
				})

				Convey("should extract panel idc", func() {
					So(alerts[0].PanelId, ShouldEqual, 3)
					So(alerts[1].PanelId, ShouldEqual, 4)
				})

				Convey("should extract name and desc", func() {
					So(alerts[0].Name, ShouldEqual, "name1")
					So(alerts[0].Message, ShouldEqual, "desc1")
					So(alerts[1].Name, ShouldEqual, "name2")
					So(alerts[1].Message, ShouldEqual, "desc2")
				})

				Convey("should set datasourceId", func() {
					condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
					query := condition.Get("query")
					So(query.Get("datasourceId").MustInt64(), ShouldEqual, 12)
				})

				Convey("should copy query model to condition", func() {
					condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
					model := condition.Get("query").Get("model")
					So(model.Get("target").MustString(), ShouldEqual, "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)")
				})
			})
		})
	})
}
Example #17
0
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
	oldState := evalContext.Rule.State

	executionError := ""
	annotationData := simplejson.New()
	if evalContext.Error != nil {
		handler.log.Error("Alert Rule Result Error", "ruleId", evalContext.Rule.Id, "error", evalContext.Error)
		evalContext.Rule.State = m.AlertStateExecError
		executionError = evalContext.Error.Error()
		annotationData.Set("errorMessage", executionError)
	} else if evalContext.Firing {
		evalContext.Rule.State = m.AlertStateAlerting
		annotationData = simplejson.NewFromAny(evalContext.EvalMatches)
	} else {
		if evalContext.NoDataFound {
			if evalContext.Rule.NoDataState != m.NoDataKeepState {
				evalContext.Rule.State = evalContext.Rule.NoDataState.ToAlertState()
			}
		} else {
			evalContext.Rule.State = m.AlertStateOK
		}
	}

	countStateResult(evalContext.Rule.State)
	if handler.shouldUpdateAlertState(evalContext, oldState) {
		handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "oldState", oldState)

		cmd := &m.SetAlertStateCommand{
			AlertId:  evalContext.Rule.Id,
			OrgId:    evalContext.Rule.OrgId,
			State:    evalContext.Rule.State,
			Error:    executionError,
			EvalData: annotationData,
		}

		if err := bus.Dispatch(cmd); err != nil {
			handler.log.Error("Failed to save state", "error", err)
		}

		// save annotation
		item := annotations.Item{
			OrgId:       evalContext.Rule.OrgId,
			DashboardId: evalContext.Rule.DashboardId,
			PanelId:     evalContext.Rule.PanelId,
			Type:        annotations.AlertType,
			AlertId:     evalContext.Rule.Id,
			Title:       evalContext.Rule.Name,
			Text:        evalContext.GetStateModel().Text,
			NewState:    string(evalContext.Rule.State),
			PrevState:   string(oldState),
			Epoch:       time.Now().Unix(),
			Data:        annotationData,
		}

		annotationRepo := annotations.GetRepository()
		if err := annotationRepo.Save(&item); err != nil {
			handler.log.Error("Failed to save annotation for new alert state", "error", err)
		}

		if (oldState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) {
			handler.log.Info("Notfication not sent", "oldState", oldState, "newState", evalContext.Rule.State)
		} else {
			handler.notifier.Notify(evalContext)
		}

	}

	return nil
}
Example #18
0
func TestDashboardDataAccess(t *testing.T) {

	Convey("Testing DB", t, func() {
		InitTestDB(t)

		Convey("Given saved dashboard", func() {
			savedDash := insertTestDashboard("test dash 23", 1, "prod", "webapp")
			insertTestDashboard("test dash 45", 1, "prod")
			insertTestDashboard("test dash 67", 1, "prod", "webapp")

			Convey("Should return dashboard model", func() {
				So(savedDash.Title, ShouldEqual, "test dash 23")
				So(savedDash.Slug, ShouldEqual, "test-dash-23")
				So(savedDash.Id, ShouldNotEqual, 0)
			})

			Convey("Should be able to get dashboard", func() {
				query := m.GetDashboardQuery{
					Slug:  "test-dash-23",
					OrgId: 1,
				}

				err := GetDashboard(&query)
				So(err, ShouldBeNil)

				So(query.Result.Title, ShouldEqual, "test dash 23")
				So(query.Result.Slug, ShouldEqual, "test-dash-23")
			})

			Convey("Should be able to delete dashboard", func() {
				insertTestDashboard("delete me", 1, "delete this")

				dashboardSlug := slug.Make("delete me")

				err := DeleteDashboard(&m.DeleteDashboardCommand{
					Slug:  dashboardSlug,
					OrgId: 1,
				})

				So(err, ShouldBeNil)
			})

			Convey("Should return error if no dashboard is updated", func() {
				cmd := m.SaveDashboardCommand{
					OrgId:     1,
					Overwrite: true,
					Dashboard: simplejson.NewFromAny(map[string]interface{}{
						"id":    float64(123412321),
						"title": "Expect error",
						"tags":  []interface{}{},
					}),
				}

				err := SaveDashboard(&cmd)
				So(err, ShouldNotBeNil)
			})

			Convey("Should not be able to overwrite dashboard in another org", func() {
				query := m.GetDashboardQuery{Slug: "test-dash-23", OrgId: 1}
				GetDashboard(&query)

				cmd := m.SaveDashboardCommand{
					OrgId:     2,
					Overwrite: true,
					Dashboard: simplejson.NewFromAny(map[string]interface{}{
						"id":    float64(query.Result.Id),
						"title": "Expect error",
						"tags":  []interface{}{},
					}),
				}

				err := SaveDashboard(&cmd)
				So(err, ShouldNotBeNil)
			})

			Convey("Should be able to search for dashboard", func() {
				query := search.FindPersistedDashboardsQuery{
					Title: "test dash 23",
					OrgId: 1,
				}

				err := SearchDashboards(&query)
				So(err, ShouldBeNil)

				So(len(query.Result), ShouldEqual, 1)
				hit := query.Result[0]
				So(len(hit.Tags), ShouldEqual, 2)
			})

			Convey("Should be able to search for dashboard by dashboard ids", func() {
				Convey("should be able to find two dashboards by id", func() {
					query := search.FindPersistedDashboardsQuery{
						DashboardIds: []int{1, 2},
						OrgId:        1,
					}

					err := SearchDashboards(&query)
					So(err, ShouldBeNil)

					So(len(query.Result), ShouldEqual, 2)

					hit := query.Result[0]
					So(len(hit.Tags), ShouldEqual, 2)

					hit2 := query.Result[1]
					So(len(hit2.Tags), ShouldEqual, 1)
				})

				Convey("DashboardIds that does not exists should not cause errors", func() {
					query := search.FindPersistedDashboardsQuery{
						DashboardIds: []int{1000},
						OrgId:        1,
					}

					err := SearchDashboards(&query)
					So(err, ShouldBeNil)
					So(len(query.Result), ShouldEqual, 0)
				})
			})

			Convey("Should not be able to save dashboard with same name", func() {
				cmd := m.SaveDashboardCommand{
					OrgId: 1,
					Dashboard: simplejson.NewFromAny(map[string]interface{}{
						"id":    nil,
						"title": "test dash 23",
						"tags":  []interface{}{},
					}),
				}

				err := SaveDashboard(&cmd)
				So(err, ShouldNotBeNil)
			})

			Convey("Should be able to get dashboard tags", func() {
				query := m.GetDashboardTagsQuery{OrgId: 1}

				err := GetDashboardTags(&query)
				So(err, ShouldBeNil)

				So(len(query.Result), ShouldEqual, 2)
			})

			Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() {
				starredDash := insertTestDashboard("starred dash", 1)
				StarDashboard(&m.StarDashboardCommand{
					DashboardId: starredDash.Id,
					UserId:      10,
				})

				StarDashboard(&m.StarDashboardCommand{
					DashboardId: savedDash.Id,
					UserId:      1,
				})

				Convey("Should be able to search for starred dashboards", func() {
					query := search.FindPersistedDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true}
					err := SearchDashboards(&query)

					So(err, ShouldBeNil)
					So(len(query.Result), ShouldEqual, 1)
					So(query.Result[0].Title, ShouldEqual, "starred dash")
				})
			})
		})
	})
}
Example #19
0
func TestAlertRuleExtraction(t *testing.T) {

	Convey("Parsing alert rules  from dashboard json", t, func() {

		RegisterCondition("query", func(model *simplejson.Json, index int) (Condition, error) {
			return &FakeCondition{}, nil
		})

		setting.NewConfigContext(&setting.CommandLineArgs{
			HomePath: "../../../",
		})

		// mock data
		defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
		graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
		influxDBDs := &m.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}

		bus.AddHandler("test", func(query *m.GetDataSourcesQuery) error {
			query.Result = []*m.DataSource{defaultDs, graphite2Ds}
			return nil
		})

		bus.AddHandler("test", func(query *m.GetDataSourceByNameQuery) error {
			if query.Name == defaultDs.Name {
				query.Result = defaultDs
			}
			if query.Name == graphite2Ds.Name {
				query.Result = graphite2Ds
			}
			if query.Name == influxDBDs.Name {
				query.Result = influxDBDs
			}
			return nil
		})

		json := `
      {
        "id": 57,
        "title": "Graphite 4",
        "originalTitle": "Graphite 4",
        "tags": ["graphite"],
        "rows": [
        {
          "panels": [
          {
            "title": "Active desktop users",
            "editable": true,
            "type": "graph",
            "id": 3,
            "targets": [
            {
              "refId": "A",
              "target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
            }
            ],
            "datasource": null,
            "alert": {
              "name": "name1",
              "message": "desc1",
              "handler": 1,
              "frequency": "60s",
              "conditions": [
              {
                "type": "query",
                "query": {"params": ["A", "5m", "now"]},
                "reducer": {"type": "avg", "params": []},
                "evaluator": {"type": ">", "params": [100]}
              }
              ]
            }
          },
          {
            "title": "Active mobile users",
            "id": 4,
            "targets": [
              {"refId": "A", "target": ""},
              {"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
            ],
            "datasource": "graphite2",
            "alert": {
              "name": "name2",
              "message": "desc2",
              "handler": 0,
              "frequency": "60s",
              "severity": "warning",
              "conditions": [
              {
                "type": "query",
                "query":  {"params": ["B", "5m", "now"]},
                "reducer": {"type": "avg", "params": []},
                "evaluator": {"type": ">", "params": [100]}
              }
              ]
            }
          }
          ]
        }
      ]
      }`

		Convey("Parsing and validating dashboard containing graphite alerts", func() {

			dashJson, err := simplejson.NewJson([]byte(json))
			So(err, ShouldBeNil)

			dash := m.NewDashboardFromJson(dashJson)
			extractor := NewDashAlertExtractor(dash, 1)

			alerts, err := extractor.GetAlerts()

			Convey("Get rules without error", func() {
				So(err, ShouldBeNil)
			})

			Convey("all properties have been set", func() {
				So(len(alerts), ShouldEqual, 2)

				for _, v := range alerts {
					So(v.DashboardId, ShouldEqual, 57)
					So(v.Name, ShouldNotBeEmpty)
					So(v.Message, ShouldNotBeEmpty)

					settings := simplejson.NewFromAny(v.Settings)
					So(settings.Get("interval").MustString(""), ShouldEqual, "")
				}

				Convey("should extract handler property", func() {
					So(alerts[0].Handler, ShouldEqual, 1)
					So(alerts[1].Handler, ShouldEqual, 0)
				})

				Convey("should extract frequency in seconds", func() {
					So(alerts[0].Frequency, ShouldEqual, 60)
					So(alerts[1].Frequency, ShouldEqual, 60)
				})

				Convey("should extract panel idc", func() {
					So(alerts[0].PanelId, ShouldEqual, 3)
					So(alerts[1].PanelId, ShouldEqual, 4)
				})

				Convey("should extract name and desc", func() {
					So(alerts[0].Name, ShouldEqual, "name1")
					So(alerts[0].Message, ShouldEqual, "desc1")
					So(alerts[1].Name, ShouldEqual, "name2")
					So(alerts[1].Message, ShouldEqual, "desc2")
				})

				Convey("should set datasourceId", func() {
					condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
					query := condition.Get("query")
					So(query.Get("datasourceId").MustInt64(), ShouldEqual, 12)
				})

				Convey("should copy query model to condition", func() {
					condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
					model := condition.Get("query").Get("model")
					So(model.Get("target").MustString(), ShouldEqual, "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)")
				})
			})
		})

		Convey("Parse and validate dashboard containing influxdb alert", func() {

			json2 := `{
				  "id": 4,
				  "title": "Influxdb",
				  "tags": [
				    "apa"
				  ],
				  "style": "dark",
				  "timezone": "browser",
				  "editable": true,
				  "hideControls": false,
				  "sharedCrosshair": false,
				  "rows": [
				    {
				      "collapse": false,
				      "editable": true,
				      "height": "450px",
				      "panels": [
				        {
				          "alert": {
				            "conditions": [
				              {
				                "evaluator": {
				                  "params": [
				                    10
				                  ],
				                  "type": "gt"
				                },
				                "query": {
				                  "params": [
				                    "B",
				                    "5m",
				                    "now"
				                  ]
				                },
				                "reducer": {
				                  "params": [],
				                  "type": "avg"
				                },
				                "type": "query"
				              }
				            ],
				            "frequency": "3s",
				            "handler": 1,
				            "name": "Influxdb",
				            "noDataState": "no_data",
				            "notifications": [
				              {
				                "id": 6
				              }
				            ]
				          },
				          "alerting": {},
				          "aliasColors": {
				            "logins.count.count": "#890F02"
				          },
				          "bars": false,
				          "datasource": "InfluxDB",
				          "editable": true,
				          "error": false,
				          "fill": 1,
				          "grid": {},
				          "id": 1,
				          "interval": ">10s",
				          "isNew": true,
				          "legend": {
				            "avg": false,
				            "current": false,
				            "max": false,
				            "min": false,
				            "show": true,
				            "total": false,
				            "values": false
				          },
				          "lines": true,
				          "linewidth": 2,
				          "links": [],
				          "nullPointMode": "connected",
				          "percentage": false,
				          "pointradius": 5,
				          "points": false,
				          "renderer": "flot",
				          "seriesOverrides": [],
				          "span": 10,
				          "stack": false,
				          "steppedLine": false,
				          "targets": [
				            {
				              "dsType": "influxdb",
				              "groupBy": [
				                {
				                  "params": [
				                    "$interval"
				                  ],
				                  "type": "time"
				                },
				                {
				                  "params": [
				                    "datacenter"
				                  ],
				                  "type": "tag"
				                },
				                {
				                  "params": [
				                    "none"
				                  ],
				                  "type": "fill"
				                }
				              ],
				              "hide": false,
				              "measurement": "logins.count",
				              "policy": "default",
				              "query": "SELECT 8 * count(\"value\") FROM \"logins.count\" WHERE $timeFilter GROUP BY time($interval), \"datacenter\" fill(none)",
				              "rawQuery": true,
				              "refId": "B",
				              "resultFormat": "time_series",
				              "select": [
				                [
				                  {
				                    "params": [
				                      "value"
				                    ],
				                    "type": "field"
				                  },
				                  {
				                    "params": [],
				                    "type": "count"
				                  }
				                ]
				              ],
				              "tags": []
				            },
				            {
				              "dsType": "influxdb",
				              "groupBy": [
				                {
				                  "params": [
				                    "$interval"
				                  ],
				                  "type": "time"
				                },
				                {
				                  "params": [
				                    "null"
				                  ],
				                  "type": "fill"
				                }
				              ],
				              "hide": true,
				              "measurement": "cpu",
				              "policy": "default",
				              "refId": "A",
				              "resultFormat": "time_series",
				              "select": [
				                [
				                  {
				                    "params": [
				                      "value"
				                    ],
				                    "type": "field"
				                  },
				                  {
				                    "params": [],
				                    "type": "mean"
				                  }
				                ],
				                [
				                  {
				                    "params": [
				                      "value"
				                    ],
				                    "type": "field"
				                  },
				                  {
				                    "params": [],
				                    "type": "sum"
				                  }
				                ]
				              ],
				              "tags": []
				            }
				          ],
				          "thresholds": [
				            {
				              "colorMode": "critical",
				              "fill": true,
				              "line": true,
				              "op": "gt",
				              "value": 10
				            }
				          ],
				          "timeFrom": null,
				          "timeShift": null,
				          "title": "Panel Title",
				          "tooltip": {
				            "msResolution": false,
				            "ordering": "alphabetical",
				            "shared": true,
				            "sort": 0,
				            "value_type": "cumulative"
				          },
				          "type": "graph",
				          "xaxis": {
				            "mode": "time",
				            "name": null,
				            "show": true,
				            "values": []
				          },
				          "yaxes": [
				            {
				              "format": "short",
				              "logBase": 1,
				              "max": null,
				              "min": null,
				              "show": true
				            },
				            {
				              "format": "short",
				              "logBase": 1,
				              "max": null,
				              "min": null,
				              "show": true
				            }
				          ]
				        },
				        {
				          "editable": true,
				          "error": false,
				          "id": 2,
				          "isNew": true,
				          "limit": 10,
				          "links": [],
				          "show": "current",
				          "span": 2,
				          "stateFilter": [
				            "alerting"
				          ],
				          "title": "Alert status",
				          "type": "alertlist"
				        }
				      ],
				      "title": "Row"
				    }
				  ],
				  "time": {
				    "from": "now-5m",
				    "to": "now"
				  },
				  "timepicker": {
				    "now": true,
				    "refresh_intervals": [
				      "5s",
				      "10s",
				      "30s",
				      "1m",
				      "5m",
				      "15m",
				      "30m",
				      "1h",
				      "2h",
				      "1d"
				    ],
				    "time_options": [
				      "5m",
				      "15m",
				      "1h",
				      "6h",
				      "12h",
				      "24h",
				      "2d",
				      "7d",
				      "30d"
				    ]
				  },
				  "templating": {
				    "list": []
				  },
				  "annotations": {
				    "list": []
				  },
				  "schemaVersion": 13,
				  "version": 120,
				  "links": [],
				  "gnetId": null
				}`

			dashJson, err := simplejson.NewJson([]byte(json2))
			So(err, ShouldBeNil)
			dash := m.NewDashboardFromJson(dashJson)
			extractor := NewDashAlertExtractor(dash, 1)

			alerts, err := extractor.GetAlerts()

			Convey("Get rules without error", func() {
				So(err, ShouldBeNil)
			})

			Convey("should be able to read interval", func() {
				So(len(alerts), ShouldEqual, 1)

				for _, alert := range alerts {
					So(alert.DashboardId, ShouldEqual, 4)

					conditions := alert.Settings.Get("conditions").MustArray()
					cond := simplejson.NewFromAny(conditions[0])

					So(cond.Get("query").Get("model").Get("interval").MustString(), ShouldEqual, ">10s")
				}
			})
		})
	})
}
Example #20
0
func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
	e.log.Debug("GetAlerts")

	alerts := make([]*m.Alert, 0)

	for _, rowObj := range e.Dash.Data.Get("rows").MustArray() {
		row := simplejson.NewFromAny(rowObj)

		for _, panelObj := range row.Get("panels").MustArray() {
			panel := simplejson.NewFromAny(panelObj)
			jsonAlert, hasAlert := panel.CheckGet("alert")

			if !hasAlert {
				continue
			}

			// backward compatability check, can be removed later
			enabled, hasEnabled := jsonAlert.CheckGet("enabled")
			if hasEnabled && enabled.MustBool() == false {
				continue
			}

			alert := &m.Alert{
				DashboardId: e.Dash.Id,
				OrgId:       e.OrgId,
				PanelId:     panel.Get("id").MustInt64(),
				Id:          jsonAlert.Get("id").MustInt64(),
				Name:        jsonAlert.Get("name").MustString(),
				Handler:     jsonAlert.Get("handler").MustInt64(),
				Message:     jsonAlert.Get("message").MustString(),
				Frequency:   getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString()),
			}

			for _, condition := range jsonAlert.Get("conditions").MustArray() {
				jsonCondition := simplejson.NewFromAny(condition)

				jsonQuery := jsonCondition.Get("query")
				queryRefId := jsonQuery.Get("params").MustArray()[0].(string)
				panelQuery := findPanelQueryByRefId(panel, queryRefId)

				if panelQuery == nil {
					return nil, ValidationError{Reason: "Alert refes to query that cannot be found"}
				}

				dsName := ""
				if panelQuery.Get("datasource").MustString() != "" {
					dsName = panelQuery.Get("datasource").MustString()
				} else if panel.Get("datasource").MustString() != "" {
					dsName = panel.Get("datasource").MustString()
				}

				if datasource, err := e.lookupDatasourceId(dsName); err != nil {
					return nil, err
				} else {
					jsonQuery.SetPath([]string{"datasourceId"}, datasource.Id)
				}

				jsonQuery.Set("model", panelQuery.Interface())
			}

			alert.Settings = jsonAlert

			// validate
			_, err := NewRuleFromDBAlert(alert)
			if err == nil && alert.ValidToSave() {
				alerts = append(alerts, alert)
			} else {
				e.log.Error("Failed to extract alerts from dashboard", "error", err)
				return nil, err
			}
		}
	}

	e.log.Debug("Extracted alerts from dashboard", "alertCount", len(alerts))
	return alerts, nil
}