monfront - show groups

This is the first step to view the group status. The templates were
split into multipe parts, to make reuse easier.
This commit is contained in:
Gibheer 2019-01-02 16:09:04 +01:00
parent 887e8ea089
commit c646179fa9

View File

@ -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 },