// eval takes an expression or string (which it turns into an expression), executes it and returns the result. // It can also takes a ResultSlice so callers can transparantly handle different inputs. // The filter argument constrains the result to matching tags in the current context. // The series argument asserts that the result is a time series. func (c *Context) eval(v interface{}, filter bool, series bool, autods int) (res expr.ResultSlice, title string, err error) { switch v := v.(type) { case string: var e *expr.Expr e, err = expr.New(v, c.schedule.RuleConf.GetFuncs(c.schedule.SystemConf.EnabledBackends())) if err != nil { return nil, "", fmt.Errorf("%s: %v", v, err) } res, title, err = c.evalExpr(e, filter, series, autods) if err != nil { return } case *expr.Expr: res, title, err = c.evalExpr(v, filter, series, autods) if err != nil { return } case expr.ResultSlice: res = v default: return nil, "", fmt.Errorf("expected string, expression or resultslice, got %T (%v)", v, v) } if filter { res = res.Filter(c.AlertKey.Group()) } if series { for _, k := range res { if k.Type() != models.TypeSeriesSet { return nil, "", fmt.Errorf("need a series, got %v (%v)", k.Type(), k) } } } return res, title, err }
// eval takes an expression or string (which it turns into an expression), executes it and returns the result. // It can also takes a ResultSlice so callers can transparantly handle different inputs. // The filter argument constrains the result to matching tags in the current context. // The series argument asserts that the result is a time series. func (c *Context) eval(v interface{}, filter bool, series bool, autods int) (res expr.ResultSlice, title string, err error) { switch v := v.(type) { case string: e, err := expr.New(v, c.schedule.Conf.Funcs()) if err != nil { return nil, "", fmt.Errorf("%s: %v", v, err) } res, title, err = c.evalExpr(e, filter, series, autods) case *expr.Expr: res, title, err = c.evalExpr(v, filter, series, autods) case expr.ResultSlice: res = v default: return nil, "", fmt.Errorf("expected string, expression or resultslice, got %T (%v)", v, v) } if filter { res = res.Filter(c.State.Group) } if series { for _, k := range res { if k.Type() != parse.TypeSeriesSet { return nil, "", fmt.Errorf("need a series, got %v (%v)", k.Type(), k) } } } return }
func (c *Conf) NewExpr(s string) *expr.Expr { exp, err := expr.New(s, c.Funcs()) if err != nil { c.error(err) } switch exp.Root.Return() { case eparse.TypeNumberSet, eparse.TypeScalar: break default: c.errorf("expression must return a number") } return exp }
func NewGraphiteCheckEvaluator(c graphite.Context, check CheckDef) (*GraphiteCheckEvaluator, error) { var warnExpr *expr.Expr var critExpr *expr.Expr var err error if check.WarnExpr != "" { warnExpr, err = expr.New(check.WarnExpr, expr.Graphite) if err != nil { return nil, err } } if check.CritExpr != "" { critExpr, err = expr.New(check.CritExpr, expr.Graphite) if err != nil { return nil, err } } return &GraphiteCheckEvaluator{ Context: c, Check: check, warnExpr: warnExpr, critExpr: critExpr, }, nil }
// ExprGraph returns an svg graph. // The basename of the requested svg file should be a base64 encoded expression. func ExprGraph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) { vars := mux.Vars(r) bs := vars["bs"] b, err := base64.StdEncoding.DecodeString(bs) if err != nil { return nil, err } q := string(b) if len(q) == 0 { return nil, fmt.Errorf("missing expression") } autods := 1000 if a := r.FormValue("autods"); a != "" { i, err := strconv.Atoi(a) if err != nil { return nil, err } autods = i } now := time.Now().UTC() if n := r.FormValue("now"); n != "" { i, err := strconv.ParseInt(n, 10, 64) if err != nil { return nil, err } now = time.Unix(i, 0).UTC() } e, err := expr.New(q, schedule.Conf.Funcs()) if err != nil { return nil, err } else if e.Root.Return() != parse.TypeSeriesSet { return nil, fmt.Errorf("egraph: requires an expression that returns a series") } // it may not strictly be necessary to recreate the contexts each time, but we do to be safe tsdbContext := schedule.Conf.TSDBContext() graphiteContext := schedule.Conf.GraphiteContext() ls := schedule.Conf.LogstashElasticHosts influx := schedule.Conf.InfluxConfig es := schedule.Conf.ElasticHosts res, _, err := e.Execute(tsdbContext, graphiteContext, ls, es, influx, cacheObj, t, now, autods, false, schedule.Search, nil, nil) if err != nil { return nil, err } if err := schedule.ExprSVG(t, w, 800, 600, "", res.Results); err != nil { return nil, err } return nil, nil }
func (c *Context) evalExpr(e *expr.Expr, filter bool, series bool, autods int) (expr.ResultSlice, string, error) { var err error if filter { e, err = expr.New(opentsdb.ReplaceTags(e.Text, c.State.Group), c.schedule.Conf.Funcs()) if err != nil { return nil, "", err } } if series && e.Root.Return() != parse.TypeSeriesSet { return nil, "", fmt.Errorf("need a series, got %T (%v)", e, e) } res, _, err := e.Execute(c.runHistory.Context, c.runHistory.GraphiteContext, c.runHistory.Logstash, c.runHistory.Cache, nil, c.runHistory.Start, autods, c.Alert.UnjoinedOK, c.schedule.Search, c.schedule.Conf.AlertSquelched(c.Alert), c.runHistory) if err != nil { return nil, "", fmt.Errorf("%s: %v", e, err) } return res.Results, e.String(), nil }
func Expr(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) { e, err := expr.New(r.FormValue("q"), schedule.Conf.Funcs()) if err != nil { return nil, err } now, err := getTime(r) if err != nil { return nil, err } // it may not strictly be necessary to recreate the contexts each time, but we do to be safe tsdbContext := schedule.Conf.TSDBContext() graphiteContext := schedule.Conf.GraphiteContext() ls := schedule.Conf.LogstashElasticHosts res, queries, err := e.Execute(tsdbContext, graphiteContext, ls, cacheObj, t, now, 0, false, schedule.Search, nil, nil) if err != nil { return nil, err } for _, r := range res.Results { if r.Computations == nil { r.Computations = make(expr.Computations, 0) } } ret := struct { Type string Results []*expr.Result Queries map[string]opentsdb.Request }{ e.Tree.Root.Return().String(), res.Results, make(map[string]opentsdb.Request), } for _, q := range queries { if e, err := url.QueryUnescape(q.String()); err == nil { ret.Queries[e] = q } } return ret, nil }
func (c *Context) evalExpr(e *expr.Expr, filter bool, series bool, autods int) (expr.ResultSlice, string, error) { var err error if filter { e, err = expr.New(opentsdb.ReplaceTags(e.Text, c.AlertKey.Group()), c.schedule.RuleConf.GetFuncs(c.schedule.SystemConf.EnabledBackends())) if err != nil { return nil, "", err } } if series && e.Root.Return() != models.TypeSeriesSet { return nil, "", fmt.Errorf("need a series, got %T (%v)", e, e) } providers := &expr.BosunProviders{ Cache: c.runHistory.Cache, Search: c.schedule.Search, Squelched: c.schedule.RuleConf.AlertSquelched(c.Alert), History: c.schedule, } res, _, err := e.Execute(c.runHistory.Backends, providers, nil, c.runHistory.Start, autods, c.Alert.UnjoinedOK) if err != nil { return nil, "", fmt.Errorf("%s: %v", e, err) } return res.Results, e.String(), nil }
func Expr(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (v interface{}, err error) { defer func() { if pan := recover(); pan != nil { v = nil err = fmt.Errorf("%v", pan) } }() text, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } lines := strings.Split(strings.TrimSpace(string(text)), "\n") var expression string vars := map[string]string{} varRegex := regexp.MustCompile(`(\$\w+)\s*=(.*)`) for i, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } // last line is expression we care about if i == len(lines)-1 { expression = schedule.Conf.Expand(line, vars, false) } else { // must be a variable declatation matches := varRegex.FindStringSubmatch(line) if len(matches) == 0 { return nil, fmt.Errorf("Expect all lines before final expression to be variable declarations of form `$foo = something`") } name := strings.TrimSpace(matches[1]) value := strings.TrimSpace(matches[2]) vars[name] = schedule.Conf.Expand(value, vars, false) } } e, err := expr.New(expression, schedule.Conf.Funcs()) if err != nil { return nil, err } now, err := getTime(r) if err != nil { return nil, err } // it may not strictly be necessary to recreate the contexts each time, but we do to be safe tsdbContext := schedule.Conf.TSDBContext() graphiteContext := schedule.Conf.GraphiteContext() ls := schedule.Conf.LogstashElasticHosts influx := schedule.Conf.InfluxConfig es := schedule.Conf.ElasticHosts res, queries, err := e.Execute(tsdbContext, graphiteContext, ls, es, influx, cacheObj, t, now, 0, false, schedule.Search, nil, nil) if err != nil { return nil, err } for _, r := range res.Results { if r.Computations == nil { r.Computations = make(models.Computations, 0) } } ret := struct { Type string Results []*expr.Result Queries map[string]opentsdb.Request }{ e.Tree.Root.Return().String(), res.Results, make(map[string]opentsdb.Request), } for _, q := range queries { if e, err := url.QueryUnescape(q.String()); err == nil { ret.Queries[e] = q } } return ret, nil }
// ExprGraph returns an svg graph. // The basename of the requested svg file should be a base64 encoded expression. func ExprGraph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) { vars := mux.Vars(r) bs := vars["bs"] format := vars["format"] b, err := base64.StdEncoding.DecodeString(bs) if err != nil { return nil, err } q := string(b) if len(q) == 0 { return nil, fmt.Errorf("missing expression") } autods := 1000 if a := r.FormValue("autods"); a != "" { i, err := strconv.Atoi(a) if err != nil { return nil, err } autods = i } now := time.Now().UTC() if n := r.FormValue("now"); n != "" { i, err := strconv.ParseInt(n, 10, 64) if err != nil { return nil, err } now = time.Unix(i, 0).UTC() } e, err := expr.New(q, schedule.RuleConf.GetFuncs(schedule.SystemConf.EnabledBackends())) if err != nil { return nil, err } else if e.Root.Return() != models.TypeSeriesSet { return nil, fmt.Errorf("egraph: requires an expression that returns a series") } // it may not strictly be necessary to recreate the contexts each time, but we do to be safe backends := &expr.Backends{ TSDBContext: schedule.SystemConf.GetTSDBContext(), GraphiteContext: schedule.SystemConf.GetGraphiteContext(), InfluxConfig: schedule.SystemConf.GetInfluxContext(), LogstashHosts: schedule.SystemConf.GetLogstashContext(), ElasticHosts: schedule.SystemConf.GetElasticContext(), AnnotateContext: schedule.SystemConf.GetAnnotateContext(), } providers := &expr.BosunProviders{ Cache: cacheObj, Search: schedule.Search, Squelched: nil, History: nil, } res, _, err := e.Execute(backends, providers, t, now, autods, false) if err != nil { return nil, err } switch format { case "svg": if err := schedule.ExprSVG(t, w, 800, 600, "", res.Results); err != nil { return nil, err } case "png": if err := schedule.ExprPNG(t, w, 800, 600, "", res.Results); err != nil { return nil, err } } return nil, nil }