summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 35c9956)
raw | patch | inline | side by side (parent: 35c9956)
author | Sebastian Harl <sh@tokkee.org> | |
Wed, 13 May 2015 20:53:48 +0000 (22:53 +0200) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Wed, 13 May 2015 20:53:48 +0000 (22:53 +0200) |
server/query.go | patch | blob | history |
diff --git a/server/query.go b/server/query.go
index 6d8fddf252d8159e7c3facbf5b7e24b43d5120f5..962e3ef56c704ae3548abf52155da114954f1dcd 100644 (file)
--- a/server/query.go
+++ b/server/query.go
if req.r.Method != "POST" {
return nil, errors.New("Method not allowed")
}
- tokens, err := tokenize(req.r.PostForm.Get("query"))
+ raw, err := parseQuery(req.r.PostForm.Get("query"))
if err != nil {
return nil, err
}
- if len(tokens) == 0 {
- return nil, errors.New("Empty query")
- }
- typ := "hosts"
var args string
- for i, tok := range tokens {
+ for name, value := range raw.args {
if len(args) > 0 {
args += " AND"
}
- if fields := strings.SplitN(tok, ":", 2); len(fields) == 2 {
- // Query: [<type>:] [<sibling-type>.]<attribute>:<value> ...
- if i == 0 && fields[1] == "" {
- typ = fields[0]
- } else if elems := strings.Split(fields[0], "."); len(elems) > 1 {
- objs := elems[:len(elems)-1]
- for _, o := range objs {
- if o != "host" && o != "service" && o != "metric" {
- return nil, fmt.Errorf("Invalid object type %q", o)
- }
- }
- args += fmt.Sprintf(" %s.attribute[%s] = %s",
- strings.Join(objs, "."), proto.EscapeString(elems[len(elems)-1]),
- proto.EscapeString(fields[1]))
- } else {
- args += fmt.Sprintf(" attribute[%s] = %s",
- proto.EscapeString(fields[0]), proto.EscapeString(fields[1]))
- }
+ if name == "name" {
+ args += fmt.Sprintf(" name =~ %s", value)
} else {
- args += fmt.Sprintf(" name =~ %s", proto.EscapeString(tok))
+ args += fmt.Sprintf(" %s = %s", name, value)
}
}
- q, err := client.QueryString("LOOKUP %s MATCHING"+args, client.Identifier(typ))
+ q, err := client.QueryString("LOOKUP %s MATCHING"+args, client.Identifier(raw.typ))
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
- if t, ok := s.results[typ]; ok {
+ if t, ok := s.results[raw.typ]; ok {
return tmpl(t, res)
}
- return nil, fmt.Errorf("Unsupported type %s", typ)
+ return nil, fmt.Errorf("Unsupported type %s", raw.typ)
}
func fetch(req request, s *Server) (*page, error) {
return tmpl(s.results["metric"], &p)
}
+type query struct {
+ typ string
+ args map[string]string
+}
+
+func (q *query) arg(name, value string) error {
+ if _, ok := q.args[name]; ok {
+ return fmt.Errorf("Duplicate key %q", name)
+ }
+ q.args[name] = proto.EscapeString(value)
+ return nil
+}
+
+func (q *query) attr(parent, name, value string) error {
+ var k string
+ if parent != "" {
+ k = fmt.Sprintf("%s.attribute[%s]", parent, proto.EscapeString(name))
+ } else {
+ k = fmt.Sprintf("attribute[%s]", proto.EscapeString(name))
+ }
+
+ return q.arg(k, value)
+}
+
+func parseQuery(s string) (*query, error) {
+ tokens, err := tokenize(s)
+ if err != nil {
+ return nil, err
+ }
+ if len(tokens) == 0 {
+ return nil, errors.New("Empty query")
+ }
+
+ q := &query{typ: "hosts", 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> ...
+ if i == 0 && fields[1] == "" {
+ q.typ = fields[0]
+ } else if elems := strings.Split(fields[0], "."); len(elems) > 1 {
+ objs := elems[:len(elems)-1]
+ for _, o := range objs {
+ if o != "host" && o != "service" && o != "metric" {
+ return nil, fmt.Errorf("Invalid object type %q", o)
+ }
+ }
+ if err := q.attr(strings.Join(objs, "."), elems[len(elems)-1], fields[1]); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := q.attr("", fields[0], fields[1]); err != nil {
+ return nil, err
+ }
+ }
+ } else {
+ if err := q.arg("name", tok); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return q, nil
+}
+
// tokenize split the string s into its tokens where a token is either a quoted
// string or surrounded by one or more consecutive whitespace characters.
func tokenize(s string) ([]string, error) {