ddc8c4d17e5f6aec59531fbcf2a5e4f33b50c852
1 //
2 // Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
18 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 package server
28 // Helper functions for handling and plotting graphs.
30 import (
31 "bytes"
32 "fmt"
33 "io"
34 "net/http"
35 "time"
37 "github.com/gonum/plot/vg"
38 "github.com/sysdb/go/sysdb"
39 "github.com/sysdb/webui/graph"
40 )
42 var urldate = "20060102150405"
44 func (s *Server) graph(w http.ResponseWriter, req request) {
45 if len(req.args) < 2 || 4 < len(req.args) {
46 s.badrequest(w, fmt.Errorf("Missing host/metric information"))
47 return
48 }
50 end := time.Now()
51 start := end.Add(-24 * time.Hour)
52 var err error
53 if len(req.args) > 2 {
54 if start, err = time.Parse(urldate, req.args[2]); err != nil {
55 s.badrequest(w, fmt.Errorf("Invalid start time: %v", err))
56 return
57 }
58 }
59 if len(req.args) > 3 {
60 if end, err = time.Parse(urldate, req.args[3]); err != nil {
61 s.badrequest(w, fmt.Errorf("Invalid start time: %v", err))
62 return
63 }
64 }
66 g := &graph.Graph{
67 Start: start,
68 End: end,
69 }
70 if req.args[0] == "q" {
71 if g.Metrics, err = s.queryMetrics(req.args[1]); err != nil {
72 s.badrequest(w, fmt.Errorf("Failed to query metrics: %v", err))
73 return
74 }
75 } else {
76 g.Metrics = []graph.Metric{{Hostname: req.args[0], Identifier: req.args[1]}}
77 }
79 p, err := g.Plot(s.c)
80 if err != nil {
81 s.internal(w, err)
82 return
83 }
85 pw, err := p.WriterTo(vg.Length(500), vg.Length(200), "svg")
86 if err != nil {
87 s.internal(w, fmt.Errorf("Failed to write plot: %v", err))
88 return
89 }
91 var buf bytes.Buffer
92 if _, err := pw.WriteTo(&buf); err != nil {
93 s.internal(w, fmt.Errorf("Failed to write plot: %v", err))
94 return
95 }
96 w.Header().Set("Content-Type", "image/svg+xml")
97 w.WriteHeader(http.StatusOK)
98 io.Copy(w, &buf)
99 }
101 func (s *Server) queryMetrics(q string) ([]graph.Metric, error) {
102 raw, err := parseQuery(q)
103 if err != nil {
104 return nil, err
105 }
106 if raw.typ != "" && raw.typ != "metrics" {
107 return nil, fmt.Errorf("Invalid object type %q for graphs", raw.typ)
108 }
110 var args string
111 for name, value := range raw.args {
112 if len(args) > 0 {
113 args += " AND"
114 }
116 if name == "name" {
117 args += fmt.Sprintf(" name =~ %s", value)
118 } else {
119 args += fmt.Sprintf(" %s = %s", name, value)
120 }
121 }
123 res, err := s.c.Query("LOOKUP metrics MATCHING" + args)
124 if err != nil {
125 return nil, err
126 }
127 hosts, ok := res.([]sysdb.Host)
128 if !ok {
129 return nil, fmt.Errorf("LOOKUP did not return a list of hosts but %T", res)
130 }
131 var metrics []graph.Metric
132 for _, h := range hosts {
133 for _, m := range h.Metrics {
134 metrics = append(metrics, graph.Metric{Hostname: h.Name, Identifier: m.Name})
135 }
136 }
137 return metrics, nil
138 }
140 // vim: set tw=78 sw=4 sw=4 noexpandtab :