diff --git a/server/graph.go b/server/graph.go
index ed5c7fd8338cedb694944674e0cdfe1c440cdca0..ddc8c4d17e5f6aec59531fbcf2a5e4f33b50c852 100644 (file)
--- a/server/graph.go
+++ b/server/graph.go
import (
"bytes"
- "errors"
"fmt"
"io"
"net/http"
"time"
- "github.com/gonum/plot"
- "github.com/gonum/plot/plotter"
- "github.com/gonum/plot/plotutil"
"github.com/gonum/plot/vg"
- "github.com/sysdb/go/client"
"github.com/sysdb/go/sysdb"
+ "github.com/sysdb/webui/graph"
)
var urldate = "20060102150405"
return
}
}
- if start.Equal(end) || start.After(end) {
- s.badrequest(w, fmt.Errorf("START(%v) is greater than or equal to END(%v)", start, end))
- return
- }
- q, err := client.QueryString("TIMESERIES %s.%s START %s END %s", req.args[0], req.args[1], start, end)
- if err != nil {
- s.internal(w, fmt.Errorf("Failed to retrieve graph data: %v", err))
- return
- }
- res, err := s.c.Query(q)
- if err != nil {
- s.internal(w, fmt.Errorf("Failed to retrieve graph data: %v", err))
- return
+ g := &graph.Graph{
+ Start: start,
+ End: end,
}
-
- ts, ok := res.(*sysdb.Timeseries)
- if !ok {
- s.internal(w, errors.New("TIMESERIES did not return a time-series"))
- return
+ if req.args[0] == "q" {
+ if g.Metrics, err = s.queryMetrics(req.args[1]); err != nil {
+ s.badrequest(w, fmt.Errorf("Failed to query metrics: %v", err))
+ return
+ }
+ } else {
+ g.Metrics = []graph.Metric{{Hostname: req.args[0], Identifier: req.args[1]}}
}
- p, err := plot.New()
+ p, err := g.Plot(s.c)
if err != nil {
- s.internal(w, fmt.Errorf("Failed to create plot: %v", err))
+ s.internal(w, err)
return
}
- p.Add(plotter.NewGrid())
- p.X.Tick.Marker = dateTicks{}
-
- var i int
- for name, data := range ts.Data {
- pts := make(plotter.XYs, len(data))
- for i, p := range data {
- pts[i].X = float64(time.Time(p.Timestamp).UnixNano())
- pts[i].Y = p.Value
- }
- l, err := plotter.NewLine(pts)
- if err != nil {
- s.internal(w, fmt.Errorf("Failed to create line plotter: %v", err))
- return
- }
- l.LineStyle.Color = plotutil.DarkColors[i%len(plotutil.DarkColors)]
- p.Add(l)
- p.Legend.Add(name, l)
- i++
- }
pw, err := p.WriterTo(vg.Length(500), vg.Length(200), "svg")
if err != nil {
io.Copy(w, &buf)
}
-type dateTicks struct{}
+func (s *Server) queryMetrics(q string) ([]graph.Metric, error) {
+ raw, err := parseQuery(q)
+ if err != nil {
+ return nil, err
+ }
+ if raw.typ != "" && raw.typ != "metrics" {
+ return nil, fmt.Errorf("Invalid object type %q for graphs", raw.typ)
+ }
+
+ var args string
+ for name, value := range raw.args {
+ if len(args) > 0 {
+ args += " AND"
+ }
+
+ if name == "name" {
+ args += fmt.Sprintf(" name =~ %s", value)
+ } else {
+ args += fmt.Sprintf(" %s = %s", name, value)
+ }
+ }
-func (dateTicks) Ticks(min, max float64) []plot.Tick {
- // TODO: this is surely not the best we can do
- // but it'll distribute ticks evenly.
- ticks := plot.DefaultTicks{}.Ticks(min, max)
- for i, t := range ticks {
- if t.Label == "" {
- // Skip minor ticks.
- continue
+ res, err := s.c.Query("LOOKUP metrics MATCHING" + args)
+ if err != nil {
+ return nil, err
+ }
+ hosts, ok := res.([]sysdb.Host)
+ if !ok {
+ return nil, fmt.Errorf("LOOKUP did not return a list of hosts but %T", res)
+ }
+ var metrics []graph.Metric
+ for _, h := range hosts {
+ for _, m := range h.Metrics {
+ metrics = append(metrics, graph.Metric{Hostname: h.Name, Identifier: m.Name})
}
- ticks[i].Label = time.Unix(0, int64(t.Value)).Format(time.RFC822)
}
- return ticks
+ return metrics, nil
}
// vim: set tw=78 sw=4 sw=4 noexpandtab :