Skip to content

Commit

Permalink
Added dimensions specs for alert triggers (#15)
Browse files Browse the repository at this point in the history
* Added dimensions specs for alert triggers
  • Loading branch information
cristianciutea authored and fryckbos committed Jun 11, 2018
1 parent 66cf741 commit 4e907f8
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 21 deletions.
119 changes: 101 additions & 18 deletions src/coscale/api/alert.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
)

Expand Down Expand Up @@ -46,17 +48,18 @@ func (a AlertType) GetId() int64 {

// AlertTrigger defines what Triggers an Alert of AlertType
type AlertTrigger struct {
ID int64
Name string // Unique
Description string
AutoResolve int64 `json:"autoresolveSeconds"` // Optional
Metric int64
Config string
OnApp bool
GroupID int64 // Optional
ServerID int64 // Optional
Source string
Version int64
ID int64
Name string // Unique
Description string
AutoResolve int64 `json:"autoresolveSeconds"` // Optional
DimensionSpecs string // The dimension specs for the selected metric.
Metric int64
Config string
OnApp bool
GroupID int64 // Optional
ServerID int64 // Optional
Source string
Version int64
}

// GetId returns the id of the AlertTrigger.
Expand Down Expand Up @@ -161,7 +164,7 @@ func (api *Api) GetTriggers(alertTypeID int64) (string, error) {
}

// CreateTrigger is used to add a new Trigger for alerts.
func (api *Api) CreateTrigger(name, description, config string, alertTypeID, autoResolve, metricID, serverID, serverGroupID int64, onApp bool) (string, error) {
func (api *Api) CreateTrigger(name, description, config, dimensionSpecs string, alertTypeID, autoResolve, metricID, serverID, serverGroupID int64, onApp bool) (string, error) {

data := map[string][]string{
"name": {name},
Expand All @@ -172,6 +175,13 @@ func (api *Api) CreateTrigger(name, description, config string, alertTypeID, aut
"source": {GetSource()},
}

parsedDimensionSpecs, err := ParseDimensionSpecs(dimensionSpecs)
if err != nil {
return "", err
}

data["dimensionSpecs"] = []string{parsedDimensionSpecs}

// Set the option values if they have value.
if serverID != -1 {
data["server"] = []string{fmt.Sprintf("%d", serverID)}
Expand All @@ -196,12 +206,13 @@ func (api *Api) CreateTrigger(name, description, config string, alertTypeID, aut
func (api *Api) UpdateTrigger(typeID int64, trigger *AlertTrigger) (string, error) {

data := map[string][]string{
"name": {trigger.Name},
"description": {trigger.Description},
"config": {trigger.Config},
"onApp": {fmt.Sprintf("%t", trigger.OnApp)},
"source": {trigger.Source},
"version": {fmt.Sprintf("%d", trigger.Version)},
"name": {trigger.Name},
"description": {trigger.Description},
"dimensionSpecs": {trigger.DimensionSpecs},
"config": {trigger.Config},
"onApp": {fmt.Sprintf("%t", trigger.OnApp)},
"source": {trigger.Source},
"version": {fmt.Sprintf("%d", trigger.Version)},
}

// Set the option values if they have value.
Expand Down Expand Up @@ -267,3 +278,75 @@ func ParseHandle(handle string) (string, error) {
jsonHandle, err := json.Marshal(result)
return string(jsonHandle), err
}

// ParseDimensionSpecs is used to parse the dimensions specs for a metric.
func ParseDimensionSpecs(format string) (string, error) {
var js interface{}
// Check if the specs are already provided in JSON format.
if err := json.Unmarshal([]byte(format), &js); err == nil {
return format, nil
}
var buffer bytes.Buffer

formatParts := strings.Split(format, ";")

buffer.WriteString("[")
for i, formatPart := range formatParts {
if i > 0 {
buffer.WriteString(",")
}
buffer.WriteString("[")

elements := strings.Split(formatPart, ":")

var dimPart, dimValsPart, aggregatorPart string
if len(elements) == 2 {
dimPart = elements[0]
dimValsPart = elements[1]
} else if len(elements) == 3 {
dimPart = elements[0]
aggregatorPart = strings.ToUpper(elements[1])
dimValsPart = elements[2]
} else {
return "", fmt.Errorf("Failed to parse dimension specs format: %s", formatPart)
}

if len(dimPart) == 0 || len(dimValsPart) == 0 {
return "", fmt.Errorf("Failed to parse dimension specs format: %s", formatPart)
}

dimID, err := strconv.ParseInt(dimPart, 10, 64)
if err != nil {
return "", fmt.Errorf("Failed to parse dimension specs format: %s", formatPart)
}
buffer.WriteString(fmt.Sprintf(`%d,"`, dimID))

// Just check if is valid format.
if dimValsPart != "*" {
for _, dimVal := range strings.Split(dimValsPart, ",") {
_, err := strconv.ParseInt(dimVal, 10, 64)
if err != nil {
return "", fmt.Errorf("Failed to parse dimension values ids: %s", formatPart)
}
}
}

if len(aggregatorPart) > 0 {
buffer.WriteString(aggregatorPart)
buffer.WriteString("(")
buffer.WriteString(dimValsPart)
buffer.WriteString(")")
} else {
buffer.WriteString(dimValsPart)
}
buffer.WriteString(`"]`)
}
buffer.WriteString("]")

// Check the format is valid.
if err := json.Unmarshal([]byte(buffer.Bytes()), &js); err != nil {
return "", err
}

return buffer.String(), nil
}
32 changes: 29 additions & 3 deletions src/coscale/command/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,15 @@ Optional:
--servergroupid
The servergroup id for which the alert will be triggered.
Note: if no server or servergroup is provided the trigger will be set for the entire application.
--dimensionsSpecs
The dimensions specifications for the metric monitored in the following format:
[[<dimension_id1>,"[agregator]<dimension_value_id1,dimension_value_id2...>",...]]
e.g.: --dimensionsSpecs='[[1,"AVG(*)"]]'
--dimensionsSpecs='[[2,"*"]]'
--dimensionsSpecs='[[3,"11,12,13"],[4,"21,22,23"]]'
`,
Run: func(cmd *Command, args []string) {
var name, config, metric, description, server, serverGroup, source, typeName string
var name, config, metric, description, server, serverGroup, source, typeName, dimSpecs string
var metricID, autoResolve, serverID, serverGroupID, typeID int64
var onApp bool

Expand All @@ -432,6 +438,7 @@ Optional:
cmd.Flag.StringVar(&source, "source", "cli", "Deprecated.")
cmd.Flag.StringVar(&typeName, "typename", "Default alerts", "Specify the name of the alert type for triggers.")
cmd.Flag.Int64Var(&typeID, "typeid", -1, "Specify the alert type id for triggers.")
cmd.Flag.StringVar(&dimSpecs, "dimensionsSpecs", "[]", "The dimensions specifications.")

cmd.ParseArgs(args)

Expand Down Expand Up @@ -493,7 +500,7 @@ Optional:
typeID = alertTypeObj.ID
}

cmd.PrintResult(cmd.Capi.CreateTrigger(name, description, config, typeID, autoResolve, metricID, serverID, serverGroupID, onApp))
cmd.PrintResult(cmd.Capi.CreateTrigger(name, description, config, dimSpecs, typeID, autoResolve, metricID, serverID, serverGroupID, onApp))
},
},
{
Expand Down Expand Up @@ -541,9 +548,15 @@ Optional:
--servergroupid
The servergroup id for which the alert will be triggered.
Note: if no server or servergroup is provided the tigger will be set for entire application.
--dimensionsSpecs
The dimensions specifications for the metric monitored in the following format:
[[<dimension_id1>,"[agregator]<dimension_value_id1,dimension_value_id2...>",...]]
e.g.: --dimensionsSpecs='[[1,"AVG(*)"]]'
--dimensionsSpecs='[[2,"*"]]'
--dimensionsSpecs='[[3,"11,12,13"],[4,"21,22,23"]]'
`,
Run: func(cmd *Command, args []string) {
var name, config, metric, description, server, serverGroup, source, typeName string
var name, config, metric, description, server, serverGroup, source, typeName, dimSpecs string
var id, autoResolve, metricID, serverID, serverGroupID, typeID int64
var onApp bool

Expand All @@ -562,6 +575,7 @@ Optional:
cmd.Flag.StringVar(&source, "source", DEFAULT_STRING_FLAG_VALUE, "Deprecated.")
cmd.Flag.StringVar(&typeName, "typename", DEFAULT_STRING_FLAG_VALUE, "Specify the name of the alert type for triggers.")
cmd.Flag.Int64Var(&typeID, "typeid", -1, "Specify the alert type id for triggers.")
cmd.Flag.StringVar(&dimSpecs, "dimensionsSpecs", DEFAULT_STRING_FLAG_VALUE, "The dimensions specifications.")

cmd.ParseArgs(args)

Expand Down Expand Up @@ -666,6 +680,18 @@ Optional:
alertTriggerObj.OnApp = onApp
}

if dimSpecs != DEFAULT_STRING_FLAG_VALUE {
// Validate the dimensions specifications.
parsedDimensionSpecs, err := api.ParseDimensionSpecs(dimSpecs)
if err != nil {
cmd.PrintResult("", err)
}

if alertTriggerObj.DimensionSpecs != parsedDimensionSpecs {
alertTriggerObj.DimensionSpecs = parsedDimensionSpecs
}
}

cmd.PrintResult(cmd.Capi.UpdateTrigger(typeID, alertTriggerObj))
},
},
Expand Down

0 comments on commit 4e907f8

Please sign in to comment.