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:
parent
887e8ea089
commit
c646179fa9
|
@ -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 },
|
||||
|
|
Loading…
Reference in New Issue
Block a user