aboutsummaryrefslogtreecommitdiff
path: root/cmd/monfront/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/monfront/main.go')
-rw-r--r--cmd/monfront/main.go202
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 },