summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 1d4e480)
raw | patch | inline | side by side (parent: 1d4e480)
author | Sebastian Harl <sh@tokkee.org> | |
Thu, 14 May 2015 10:42:51 +0000 (12:42 +0200) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Thu, 14 May 2015 10:46:13 +0000 (12:46 +0200) |
A new page now allows to query metrics using the simple query language of the
webUI. All returned metrics will be thrown into a single graph.
webUI. All returned metrics will be thrown into a single graph.
server/graph.go | patch | blob | history | |
server/query.go | patch | blob | history | |
server/server.go | patch | blob | history | |
static/style/main.css | patch | blob | history | |
templates/graphs.tmpl | [new file with mode: 0644] | patch | blob |
templates/main.tmpl | patch | blob | history |
diff --git a/server/graph.go b/server/graph.go
index 7509485930e23f60f1dc140e8776d3d0b473c8b5..ddc8c4d17e5f6aec59531fbcf2a5e4f33b50c852 100644 (file)
--- a/server/graph.go
+++ b/server/graph.go
"time"
"github.com/gonum/plot/vg"
+ "github.com/sysdb/go/sysdb"
"github.com/sysdb/webui/graph"
)
return
}
}
+
g := &graph.Graph{
- Start: start,
- End: end,
- Metrics: []graph.Metric{{Hostname: req.args[0], Identifier: req.args[1]}},
+ Start: start,
+ End: end,
+ }
+ 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 := g.Plot(s.c)
if err != nil {
s.internal(w, err)
io.Copy(w, &buf)
}
+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)
+ }
+ }
+
+ 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})
+ }
+ }
+ return metrics, nil
+}
+
// vim: set tw=78 sw=4 sw=4 noexpandtab :
diff --git a/server/query.go b/server/query.go
index 962e3ef56c704ae3548abf52155da114954f1dcd..bde450fef97dc74911e4aa516b859a3dba85afe7 100644 (file)
--- a/server/query.go
+++ b/server/query.go
return nil, err
}
+ if raw.typ == "" {
+ raw.typ = "hosts"
+ }
var args string
for name, value := range raw.args {
if len(args) > 0 {
return tmpl(s.results[req.cmd], res)
}
+func graphs(req request, s *Server) (*page, error) {
+ p := struct {
+ Query, Metrics string
+ }{
+ Query: req.r.PostForm.Get("metrics-query"),
+ }
+
+ if req.r.Method == "POST" {
+ p.Metrics = p.Query
+ }
+ return tmpl(s.results["graphs"], &p)
+}
+
var datetime = "2006-01-02 15:04:05"
func metric(req request, res interface{}, s *Server) (*page, error) {
return nil, errors.New("Empty query")
}
- q := &query{typ: "hosts", args: make(map[string]string)}
+ q := &query{args: make(map[string]string)}
for i, tok := range tokens {
if fields := strings.SplitN(tok, ":", 2); len(fields) == 2 {
// Query: [<type>:] [<sibling-type>.]<attribute>:<value> ...
diff --git a/server/server.go b/server/server.go
index 8d0d72dc191770f6810896306ea3f0f3beca64b5..1f5ea94a3879f8a26ddb98a27455393e5eeebca6 100644 (file)
--- a/server/server.go
+++ b/server/server.go
if s.main, err = cfg.parse("main.tmpl"); err != nil {
return nil, err
}
- types := []string{"host", "hosts", "service", "services", "metric", "metrics"}
+ types := []string{"graphs", "host", "hosts", "service", "services", "metric", "metrics"}
for _, t := range types {
s.results[t], err = cfg.parse(t + ".tmpl")
if err != nil {
"": index,
// Queries
+ "graphs": graphs,
"host": fetch,
"service": fetch,
"metric": fetch,
diff --git a/static/style/main.css b/static/style/main.css
index 4127602aa6de6c352091f2c43f73abf5964f51e1..1bcae8641f89521ca6ba6c8693e29c0ca17918fc 100644 (file)
--- a/static/style/main.css
+++ b/static/style/main.css
padding: 0px 3px;
}
+input[type=text].query {
+ width: 25em;
+ height: 25px;
+ border: 1px solid #000;
+ padding: 0px 3px;
+}
+
button {
background-color: #000;
color: #fff;
diff --git a/templates/graphs.tmpl b/templates/graphs.tmpl
--- /dev/null
+++ b/templates/graphs.tmpl
@@ -0,0 +1,12 @@
+<section>
+ <h1>Graphs</h1>
+ <form action="/graphs" method="POST">
+ <input type="text" name="metrics-query" value="{{.Query}}"
+ class="query" placeholder="Search metrics" required />
+ <button type="submit">GO</button>
+ </form><br />
+{{if .Metrics}}
+ <img src="/graph/q/{{.Metrics}}" border="0" />
+{{end}}
+ <p> </p>
+</section>
diff --git a/templates/main.tmpl b/templates/main.tmpl
index 254a22ab714cbeb97f0a13bae3197d09338a0d99..e6049ebd41bc31b6cf04c049886b4b4db6daeba9 100644 (file)
--- a/templates/main.tmpl
+++ b/templates/main.tmpl
<a href="/hosts">Hosts</a>
<a href="/services">Services</a>
<a href="/metrics">Metrics</a>
+ <a href="/graphs">Graphs</a>
</nav></aside>
<div class="content">