diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/monfront/main.go | 202 |
1 files changed, 138 insertions, 64 deletions
diff --git a/cmd/monfront/main.go b/cmd/monfront/main.go index 87ae717..10dd803 100644 --- a/cmd/monfront/main.go +++ b/cmd/monfront/main.go @@ -19,6 +19,7 @@ import ( var ( configPath = flag.String("config", "monfront.conf", "path to the config file") DB *sql.DB + Tmpl *template.Template ) type ( @@ -28,8 +29,10 @@ type ( } Context struct { + Title string Mappings map[int]map[int]MapEntry Checks []check + Groups []group } MapEntry struct { @@ -38,6 +41,7 @@ type ( } check struct { + NodeId int NodeName string CommandName string CheckID int64 @@ -48,6 +52,15 @@ type ( NextTime time.Time Msg string } + + group struct { + GroupId int + Name string + NodeId int + NodeName string + State int + MappingId int + } ) func main() { @@ -68,11 +81,21 @@ func main() { } DB = db + tmpl := template.New("main") + tmpl.Funcs(Funcs) + for k, val := range Templates { + template.Must(tmpl.New(k).Parse(val)) + } + Tmpl = tmpl + http.HandleFunc("/", showChecks) + http.HandleFunc("/checks", showChecks) + http.HandleFunc("/hosts", showHosts) + http.HandleFunc("/groups", showGroups) http.HandleFunc("/action", checkAction) http.HandleFunc("/unhandled/checks", showChecks) - http.HandleFunc("/unhandled/hosts", showUnhandledHosts) - http.HandleFunc("/unhandled/groups", showUnhandledGroups) + http.HandleFunc("/unhandled/hosts", showHosts) + http.HandleFunc("/unhandled/groups", showGroups) http.ListenAndServe(config.Listen, nil) } @@ -161,11 +184,38 @@ func checkAction(w http.ResponseWriter, r *http.Request) { } func showChecks(w http.ResponseWriter, r *http.Request) { - query := SQLShowChecks + query := `select c.id, n.id, n.name, co.name, ac.mapping_id, ac.states[1] as state, + ac.enabled, ac.notice, ac.next_time, ac.msg + from active_checks ac + join checks c on ac.check_id = c.id + join nodes n on c.node_id = n.id + join commands co on c.command_id = co.id` + where := []string{} if strings.HasPrefix(r.URL.Path, "/unhandled") { - query = SQLShowUnhandledChecks + where = append(where, `ac.states[1] > 0`) } - rows, err := DB.Query(query) + idx := 0 + params := []interface{}{} + if id, found := r.URL.Query()["node_id"]; found { + idx += 1 + where = append(where, fmt.Sprintf("n.id = $%d::int", idx)) + params = append(params, id[0]) + } + if id, found := r.URL.Query()["command_id"]; found { + idx += 1 + where = append(where, fmt.Sprintf("co.id = $%d::int", idx)) + params = append(params, id[0]) + } + if id, found := r.URL.Query()["check_id"]; found { + idx += 1 + where = append(where, fmt.Sprintf("c.id = $%d::int", idx)) + params = append(params, id[0]) + } + if len(where) > 0 { + query += " where " + strings.Join(where, " and ") + } + query += ` order by n.name, co.name` + rows, err := DB.Query(query, params...) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problems with the database")) @@ -176,7 +226,7 @@ func showChecks(w http.ResponseWriter, r *http.Request) { checks := []check{} for rows.Next() { c := check{} - err := rows.Scan(&c.CheckID, &c.NodeName, &c.CommandName, &c.MappingId, &c.State, &c.Enabled, &c.Notice, &c.NextTime, &c.Msg) + err := rows.Scan(&c.CheckID, &c.NodeId, &c.NodeName, &c.CommandName, &c.MappingId, &c.State, &c.Enabled, &c.Notice, &c.NextTime, &c.Msg) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problems with the database")) @@ -185,15 +235,6 @@ func showChecks(w http.ResponseWriter, r *http.Request) { } checks = append(checks, c) } - tmpl := template.New("checklist") - tmpl.Funcs(Funcs) - tmpl, err = tmpl.Parse(TmplCheckList) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("problems with a template")) - log.Printf("could not parse template: %s", err) - return - } con := Context{ Checks: checks, } @@ -204,7 +245,7 @@ func showChecks(w http.ResponseWriter, r *http.Request) { return } w.Header()["Content-Type"] = []string{"text/html"} - if err := tmpl.Execute(w, con); err != nil { + if err := Tmpl.ExecuteTemplate(w, "checklist", con); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problem with a template")) log.Printf("could not execute template: %s", err) @@ -213,11 +254,26 @@ func showChecks(w http.ResponseWriter, r *http.Request) { return } -func showUnhandledHosts(w http.ResponseWriter, r *http.Request) { +func showHosts(w http.ResponseWriter, r *http.Request) { } -func showUnhandledGroups(w http.ResponseWriter, r *http.Request) { - rows, err := DB.Query(SQLShowUnhandledGroups) +func showGroups(w http.ResponseWriter, r *http.Request) { + query := `select groupid, groupname, nodeid, nodename, mapping_id, state + from ( + select g.id groupid, g.name groupname, n.id nodeid, n.name nodename, ac.mapping_id, + ac.states[1] state, max(ac.states[1]) over (partition by c.node_id) maxstate + from groups g + join nodes_groups ng on g.id = ng.group_id + join nodes n on ng.node_id = n.id + join checks c on n.id = c.node_id + join active_checks ac on c.id = ac.check_id + join mapping_level ml on ac.mapping_id = ml.mapping_id and ac.states[1] = ml.target + ) s + where state = maxstate` + if strings.HasPrefix(r.URL.Path, "/unhandled") { + query += ` and state > 0` + } + rows, err := DB.Query(query) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problems with the database")) @@ -225,32 +281,29 @@ func showUnhandledGroups(w http.ResponseWriter, r *http.Request) { return } - type check struct { - GroupName string - NodeName string - State int - } - checks := []check{} + groups := []group{} for rows.Next() { - c := check{} - err := rows.Scan(&c.GroupName, &c.NodeName, &c.State) + g := group{} + err := rows.Scan(&g.GroupId, &g.Name, &g.NodeId, &g.NodeName, &g.MappingId, &g.State) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problems with the database")) log.Printf("could not get check list: %s", err) return } - checks = append(checks, c) + groups = append(groups, g) } - tmpl, err := template.New("checklist").Parse(TmplUnhandledGroups) - if err != nil { + con := Context{ + Groups: groups, + } + if err := loadMappings(&con); err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("problems with a template")) - log.Printf("could not parse template: %s", err) + w.Write([]byte("problem with the mappings")) + log.Printf("could not load mappings: %s", err) return } w.Header()["Content-Type"] = []string{"text/html"} - if err := tmpl.Execute(w, checks); err != nil { + if err := Tmpl.ExecuteTemplate(w, "grouplist", con); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("problem with a template")) log.Printf("could not execute template: %s", err) @@ -292,36 +345,14 @@ func loadMappings(c *Context) error { var ( SQLShowMappings = `select mapping_id, target, title, color from mapping_level` - SQLShowChecks = `select c.id, n.name, co.name, ac.mapping_id, ac.states[1] as state, - ac.enabled, ac.notice, ac.next_time, ac.msg - from active_checks ac - join checks c on ac.check_id = c.id - join nodes n on c.node_id = n.id - join commands co on c.command_id = co.id - order by n.name, co.name;` - SQLShowUnhandledChecks = `select c.id, n.name, co.name, ac.mapping_id, ac.states[1] as state, - ac.enabled, ac.notice, ac.next_time, ac.msg - from active_checks ac - join checks c on ac.check_id = c.id - join nodes n on c.node_id = n.id - join commands co on c.command_id = co.id - where ac.states[1] > 0 - order by n.name, co.name;` - SQLShowUnhandledGroups = `select g.name, n.name, max(ac.state[1]) - from groups g - join nodes_groups ng on g.id = ng.group_id - join nodes n on ng.node_id = n.id - join checks c on n.id = c.node_id - join active_checks ac on c.id = ac.check_id - where ac.states[1] > 0 - group by g.name, n.name;` ) var ( - TmplCheckList = `<doctype html> + Templates = map[string]string{ + "header": `<doctype html> <html> - <head> - <title>check list</title> + <head> + <title>{{ .Title }}</title> <style type="text/css"> * { font-size: 100%; } form section { @@ -346,7 +377,7 @@ var ( td.state-99 { background: gray; } */ </style> <script> - setTimeout(function() { location.reload(true) }, 30000) + setTimeout(function() { location.reload(true) }, 30000) </script> </head> <body> @@ -357,7 +388,9 @@ var ( <li><a href="/unhandled/hosts">unhandled hosts</a></li> <li><a href="/unhandled/groups">unhandled groups</a></li> </ul> - </nav> + </nav>`, + "footer": `</body></html>`, + "checklist": `{{ template "header" . }} <form method="post" action="/action"> <section> <nav> @@ -388,7 +421,7 @@ var ( {{ range .Checks }} <tr> <td><input type="checkbox" name="checks" value="{{ .CheckID }}" /></td> - <td>{{ if ne $current .NodeName }}{{ $current = .NodeName }}{{ .NodeName }}{{ end }}</td> + <td>{{ if ne $current .NodeName }}{{ $current = .NodeName }}<a href="/checks?node_id={{ .NodeId }}">{{ .NodeName }}</a>{{ end }}</td> <td class="state-{{ .MappingId }}-{{ .State }}">{{ .CommandName }} - {{ (index $mapping .MappingId .State).Title }}</td> <td>{{ .NextTime.Format "2006.01.02 15:04:05" }}</td> <td><pre>{{ .Msg }}</pre></td> @@ -398,8 +431,49 @@ var ( </table> </content> </form> - </body> -</html>` + {{ template "footer" . }}`, + "grouplist": `{{ template "header" . }} + <form method="post" action="/action"> + <section> + <nav> + <div class="option"> + <label for="action">Action</label> + <select name="action"> + <option value="reschedule">run now</option> + <option value="mute">mute</option> + <option value="unmute">unmute</option> + <option value="ack">acknowledge</option> + <option value="disable">disable</option> + <option value="enable">enable</option> + <option value="comment">comment</option> + </select> + </div> + <div class="option"> + <label for="comment">comment</label> + <input name="comment" /> + </div> + <button type="submit">submit</button> + </nav> + <content> + <table> + <thead><tr><th></th><th>group</th><th>host</th><th>worst state</th></tr></thead> + <tbody> + {{ $current := "" }} + {{ $mapping := .Mappings }} + {{ range .Groups }} + <tr> + <td><input type="checkbox" name="nodes" value="{{ .NodeId }}" /></td> + <td>{{ if ne $current .Name }}{{ $current = .Name }}{{ .Name }}{{ end }}</td> + <td><a href="/checks?node_id={{ .NodeId }}">{{ .NodeName }}</a></td> + <td class="state-{{ .MappingId }}-{{ .State }}">{{ (index $mapping .MappingId .State).Title }}</td> + </tr> + {{ end }} + </tbody> + </table> + </content> + </form> + {{ template "footer" . }}`, + } TmplUnhandledGroups = `TODO` Funcs = template.FuncMap{ "sub": func(base, amount int) int { return base - amount }, |