From: Sebastian Harl Date: Thu, 20 Nov 2014 22:19:18 +0000 (+0100) Subject: Initial commit of the SysDB web interface. X-Git-Url: https://git.tokkee.org/?p=sysdb%2Fwebui.git;a=commitdiff_plain;h=97fc96d8f8b5ac83ade0af269ccb98cb1a2abdf0 Initial commit of the SysDB web interface. This is the initial version of a web-based client programm for querying SysDB (https://sysdb.io/). For now, it supports browsing, querying, and displaying basic information about hosts, services, and metrics. --- 97fc96d8f8b5ac83ade0af269ccb98cb1a2abdf0 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b7a1f1b --- /dev/null +++ b/COPYING @@ -0,0 +1,24 @@ +Copyright (c) 2014 Sebastian 'tokkee' Harl +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README b/README new file mode 100644 index 0000000..6a49783 --- /dev/null +++ b/README @@ -0,0 +1,99 @@ + Web User-Interface for SysDB + ============================== + + The SysDB web user-interface provides a graphical client application for + browsing and querying the system database. + +What is SysDB? +-------------- + + “System DataBase” (SysDB) is a multi-backend system management and inventory + collection service. It stores system and inventory information about + hardware and software systems. This information is (continuously) collected + from various configurable backends (inventory services, monitoring services, + etc.) and stored in a graph-like hierarchy of generic objects. The store may + be queried through a generic interface independent of the active backends. + Object names are canonicalized before they are added to the store to ensure + a consistent view of your infrastructure. + + The central object type is a host, which generally represents a physical or + virtual machine or any other type of physical resource. Hosts, in turn, may + reference a list of services which represent any kind of logical resource + like a software system. Both, hosts and services, may reference a list of + attributes which represent further information about the respective host or + service object. For example, attributes may specify static information like + a host's architecture or the software version. A host may also reference a + list of metrics which are references to performance data stored about the + host. SysDB supports querying the respective time-series from a backend's + data store. + + SysDB is free and open source software, licensed under the 2-clause BSD + license. See COPYING for details. + + + +Install the web-interface +------------------------- + + The SysDB webui is written in Go. It can be installed along with all of its + dependencies as easy as running the following command: + + go get github.com/sysdb/webui/... + + This will download and install the packages and all of their dependencies + into GOPATH. See ‘go help get’ for more details. + +Running the web-interface +------------------------- + + The SysDB webui is a standalone web application. It can run all on its own + but it can also be put behind a reverse proxy (e.g. using Apache or nginx). + It is composed of a dynamic application and a set of static files (images + and style-sheets) which are shipped along with the source distribution. You + can start the application using the following command, using the --address + option to point it at a running SysDB daemon: + + ./webui \ + --address=/var/run/sysdbd.sock \ + --listen=:8080 \ + --static-path=$GOPATH/src/github.com/sysdb/webui/static \ + --template-path=$GOPATH/src/github.com/sysdb/webui/templates + + You can then access the interface by pointing your browser at + http://localhost:8080 + +Packages +-------- + + While the webui is a standalone application, most of its functionality is + implemented in a reusable library allowing to integrate it into other + software written in Go. + + * github.com/sysdb/webui/server: The core of the SysDB web server. + + It makes use of the following packages: + + * github.com/sysdb/go/client (and related packages): A SysDB client + implementation. + +Documentation +------------- + + The documentation for all Go packages is included with the source code in a + way compatible with the godoc tool. As such, it can be viewed and browsed + online at . + +Getting Help +------------ + + Various channels for asynchronous and real-time communication with + developers and users are available. See for + details about the mailing list, IRC channel, and social media. + +Author +------ + + Sebastian “tokkee” Harl + + Want to contribute? Check out the website for details. + diff --git a/main.go b/main.go new file mode 100644 index 0000000..9d099da --- /dev/null +++ b/main.go @@ -0,0 +1,81 @@ +// +// Copyright (C) 2014 Sebastian 'tokkee' Harl +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// webui is a web-based user-interface for SysDB. +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "os" + + "github.com/sysdb/go/client" + "github.com/sysdb/webui/server" +) + +var ( + addr = flag.String("address", "/var/run/sysdbd.sock", "SysDB server address") + user = flag.String("user", "sysdb", "SysDB user name") + + listen = flag.String("listen", ":8080", "address to listen for incoming connections") + tmpl = flag.String("template-path", "templates", "location of template files") + static = flag.String("static-path", "static", "location of static files") +) + +func main() { + flag.Parse() + + log.Printf("Connecting to SysDB at %s.", *addr) + conn, err := client.Connect(*addr, *user) + if err != nil { + fatalf("Failed to connect to SysDB at %q: %v", *addr, err) + } + + srv, err := server.New(server.Config{ + Conn: conn, + TemplatePath: *tmpl, + StaticPath: *static, + }) + if err != nil { + fatalf("Failed to construct web-server: %v", err) + } + + log.Printf("Listening on %s.", *listen) + http.Handle("/", srv) + err = http.ListenAndServe(*listen, nil) + if err != nil { + fatalf("Failed to set up HTTP server on address %q: %v", *listen, err) + } +} + +func fatalf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format, a...) + fmt.Fprintln(os.Stderr) + os.Exit(1) +} + +// vim: set tw=78 sw=4 sw=4 noexpandtab : diff --git a/server/error.go b/server/error.go new file mode 100644 index 0000000..49fec43 --- /dev/null +++ b/server/error.go @@ -0,0 +1,71 @@ +// +// Copyright (C) 2014 Sebastian 'tokkee' Harl +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package server + +// Error handlers for the SysDB web interface. + +import ( + "bytes" + "fmt" + "html/template" + "io" + "log" + "net/http" +) + +func (s *Server) notfound(w http.ResponseWriter, r *http.Request) { + s.err(w, http.StatusNotFound, fmt.Errorf("%s not found", r.RequestURI)) +} + +func (s *Server) internal(w http.ResponseWriter, err error) { + s.err(w, http.StatusInternalServerError, err) +} + +func (s *Server) err(w http.ResponseWriter, status int, err error) { + log.Printf("%s: %v", http.StatusText(status), err) + + page := struct { + Title string + Query string + Content template.HTML + }{ + Title: "SysDB - Error", + Content: "
" + html(err.Error()) + "
", + } + + var buf bytes.Buffer + err = s.main.Execute(&buf, &page) + if err != nil { + // Nothing more we can do about this. + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(status) + io.Copy(w, &buf) +} + +// vim: set tw=78 sw=4 sw=4 noexpandtab : diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..995f255 --- /dev/null +++ b/server/server.go @@ -0,0 +1,299 @@ +// +// Copyright (C) 2014 Sebastian 'tokkee' Harl +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package server implements the core of the SysDB web server. +package server + +import ( + "bytes" + "errors" + "fmt" + "html/template" + "io" + "log" + "net/http" + "path/filepath" + "strings" + + "github.com/sysdb/go/client" + "github.com/sysdb/go/proto" + "github.com/sysdb/go/sysdb" +) + +// A Config specifies configuration values for a SysDB web server. +type Config struct { + // Conn specifies a connection to a SysDB server instance. + Conn *client.Conn + + // TemplatePath specifies the relative or absolute location of template files. + TemplatePath string + + // StaticPath specifies the relative or absolute location of static files. + StaticPath string +} + +// A Server implements an http.Handler that serves the SysDB user interface. +type Server struct { + c *client.Conn + + // Templates: + main *template.Template + results map[string]*template.Template + + // Static content: + static http.Handler +} + +// New constructs a new SysDB web server using the specified configuration. +func New(cfg Config) (*Server, error) { + s := &Server{c: cfg.Conn, results: make(map[string]*template.Template)} + + var err error + s.main, err = cfg.parse("main.tmpl") + if err != nil { + return nil, err + } + + types := []string{"host", "hosts", "service", "services", "metric", "metrics"} + for _, t := range types { + s.results[t], err = cfg.parse(t + ".tmpl") + if err != nil { + return nil, err + } + } + + s.static = http.FileServer(http.Dir(cfg.StaticPath)) + return s, nil +} + +func (cfg Config) parse(name string) (*template.Template, error) { + t := template.New(filepath.Base(name)) + return t.ParseFiles(filepath.Join(cfg.TemplatePath, name)) +} + +type request struct { + r *http.Request + cmd string + args []string +} + +var handlers = map[string]func(request, *Server) (template.HTML, error){ + "": index, + + // Queries + "host": fetch, + "service": fetch, + "metric": fetch, + "hosts": listAll, + "services": listAll, + "metrics": listAll, + "lookup": lookup, +} + +// ServeHTTP implements the http.Handler interface and serves +// the SysDB user interface. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + fields := strings.Split(path, "/") + + if fields[0] == "style" || fields[0] == "images" { + s.static.ServeHTTP(w, r) + return + } + + req := request{ + r: r, + cmd: fields[0], + } + if len(fields) > 1 { + if fields[len(fields)-1] == "" { + // Slash at the end of the URL + fields = fields[:len(fields)-1] + } + if len(fields) > 1 { + req.args = fields[1:] + } + } + + f, ok := handlers[req.cmd] + if !ok { + s.notfound(w, r) + return + } + r.ParseForm() + content, err := f(req, s) + if err != nil { + s.err(w, http.StatusBadRequest, fmt.Errorf("Error: %v", err)) + return + } + + page := struct { + Title string + Query string + Content template.HTML + }{ + Title: "SysDB - The System Database", + Query: r.FormValue("query"), + Content: content, + } + + var buf bytes.Buffer + err = s.main.Execute(&buf, &page) + if err != nil { + s.internal(w, err) + return + } + + w.WriteHeader(http.StatusOK) + io.Copy(w, &buf) +} + +// Content handlers. + +func index(_ request, s *Server) (template.HTML, error) { + return "

Welcome to the System Database.

", nil +} + +func listAll(req request, s *Server) (template.HTML, error) { + if len(req.args) != 0 { + return "", fmt.Errorf("%s not found", strings.Title(req.cmd)) + } + + res, err := s.query(fmt.Sprintf("LIST %s", req.cmd)) + if err != nil { + return "", err + } + // the template *must* exist + return tmpl(s.results[req.cmd], res) +} + +func lookup(req request, s *Server) (template.HTML, error) { + if req.r.Method != "POST" { + return "", errors.New("Method not allowed") + } + q := proto.EscapeString(req.r.FormValue("query")) + if q == "''" { + return "", errors.New("Empty query") + } + + res, err := s.query(fmt.Sprintf("LOOKUP hosts MATCHING name =~ %s", q)) + if err != nil { + return "", err + } + return tmpl(s.results["hosts"], res) +} + +func fetch(req request, s *Server) (template.HTML, error) { + if len(req.args) == 0 { + return "", fmt.Errorf("%s not found", strings.Title(req.cmd)) + } + + var q string + switch req.cmd { + case "host": + if len(req.args) != 1 { + return "", fmt.Errorf("%s not found", strings.Title(req.cmd)) + } + q = fmt.Sprintf("FETCH host %s", proto.EscapeString(req.args[0])) + case "service", "metric": + if len(req.args) < 2 { + return "", fmt.Errorf("%s not found", strings.Title(req.cmd)) + } + host := proto.EscapeString(req.args[0]) + name := proto.EscapeString(strings.Join(req.args[1:], "/")) + q = fmt.Sprintf("FETCH %s %s.%s", req.cmd, host, name) + default: + panic("Unknown request: fetch(" + req.cmd + ")") + } + + res, err := s.query(q) + if err != nil { + return "", err + } + return tmpl(s.results[req.cmd], res) +} + +func tmpl(t *template.Template, data interface{}) (template.HTML, error) { + var buf bytes.Buffer + if err := t.Execute(&buf, data); err != nil { + return "", fmt.Errorf("Template error: %v", err) + } + return template.HTML(buf.String()), nil +} + +func html(s string) template.HTML { + return template.HTML(template.HTMLEscapeString(s)) +} + +func (s *Server) query(cmd string) (interface{}, error) { + m := &proto.Message{ + Type: proto.ConnectionQuery, + Raw: []byte(cmd), + } + if err := s.c.Send(m); err != nil { + return nil, fmt.Errorf("Query %q: %v", cmd, err) + } + + for { + m, err := s.c.Receive() + if err != nil { + return nil, fmt.Errorf("Failed to receive server response: %v", err) + } + if m.Type == proto.ConnectionLog { + log.Println(string(m.Raw[4:])) + continue + } else if m.Type == proto.ConnectionError { + return nil, errors.New(string(m.Raw)) + } + + t, err := m.DataType() + if err != nil { + return nil, fmt.Errorf("Failed to unmarshal response: %v", err) + } + + var res interface{} + switch t { + case proto.HostList: + var hosts []sysdb.Host + err = proto.Unmarshal(m, &hosts) + res = hosts + case proto.Host: + var host sysdb.Host + err = proto.Unmarshal(m, &host) + res = host + default: + return nil, fmt.Errorf("Unsupported data type %d", t) + } + if err != nil { + return nil, fmt.Errorf("Failed to unmarshal response: %v", err) + } + return res, nil + } +} + +// vim: set tw=78 sw=4 sw=4 noexpandtab : diff --git a/static/images/favicon.png b/static/images/favicon.png new file mode 100644 index 0000000..f56ba49 Binary files /dev/null and b/static/images/favicon.png differ diff --git a/static/images/owl.png b/static/images/owl.png new file mode 100644 index 0000000..bb54e88 Binary files /dev/null and b/static/images/owl.png differ diff --git a/static/style/main.css b/static/style/main.css new file mode 100644 index 0000000..d358e9b --- /dev/null +++ b/static/style/main.css @@ -0,0 +1,160 @@ +body { + font-family: sans-serif; + background-color: #f0f0f0; + margin: 0px; + padding: 0px; +} + +a, a:link, a:active, a:visited, a:hover, a:focus, a:active { + text-decoration: none; + background-color: transparent; +} + +iframe { + border: none; + overflow: hidden; +} + +img { + border: none; +} + +header { + padding: 0px; + margin: 0px; + width: 100%; +} + +div.topmenu { + background-color: #000; + color: #fff; + font-size: small; + height: 1em; + vertical-align: middle; + text-align: right; + margin: 0px; + padding: .7em 3em; +} + +div.topmenu a, div.topmenu a:link, div.topmenu a:active, div.topmenu a:visited { + background-color: transparent; + color: #fff; + padding: 2px; +} + +div.topmenu a:hover, div.topmenu a:focus, div.topmenu a:active { + background-color: #454545; + color: #fff; + padding: 2px; +} + +div.searchbar { + background-color: #1e466d; + font-size: small; + color: #fff; + margin: 0px; + padding: 0px; + border-bottom: 1px solid #000; +} + +div.logo { + padding: 5px 0px 5px 25px; + margin-top: -2.4em; + vertical-align: middle; + display: inline-block; + float: left; +} + +div.searchbox { + margin-left: 152px; + padding: 0px 5px; + overflow: hidden; +} + +div.searchbox input[type=text] { + width: 440px; + height: 25px; + border: 0px; + margin: 10px 0px; + padding: 0px 3px; +} + +div.searchbox button { + background-color: #000; + color: #fff; + border: 0px; + width: 50px; + height: 25px; + margin: 0px; + padding: 0px; +} + +div.main { + margin: 0px; + padding: 0px; + width: 100%; + overflow: hidden; +} + +div.content { + margin-left: 152px; + padding: 10px 10px 0px 0px; + overflow: hidden; +} + +div.content section { + background-color: #fff; + margin: 10px 0px; + border: 1px solid #000; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 1em; +} + +div.content section.error { + border: 1px solid #f00; +} + +aside { + width: 142px; + float: left; + padding: 0px; + margin: 0x; +} + +nav { + line-height: 1.5em; + padding: 25px 10px; +} + +nav a, nav a:link, nav a:active, nav a:visited { + color: #000; + background-color: #cdcdcd; + padding: 3px; + margin: 5px 0px; + display: block; +} + +nav a:hover, nav a:focus, nav a:active { + color: #000; + background-color: #ababab; + padding: 3px; + margin: 5px 0px; + display: block; +} + +section h1 { + margin-top: 0px; + border-bottom: 1px solid #000; +} + +table.results { + border-collapse: collapse; +} + +table.results th, table.results td { + border: 1px solid #1e466d; + padding: 3px; + vertical-align: top; +} diff --git a/templates/host.tmpl b/templates/host.tmpl new file mode 100644 index 0000000..6b56446 --- /dev/null +++ b/templates/host.tmpl @@ -0,0 +1,33 @@ +
+

Host {{.Name}}

+ + + + +{{if len .Attributes}} + + {{range .Attributes}} + + {{end}} +{{else}} + +{{end}} +{{if len .Services}} + + {{range .Services}} + + {{end}} +{{else}} + +{{end}} +{{if len .Metrics}} + + {{range .Metrics}} + + {{end}} +{{else}} + +{{end}} +
Last update{{.LastUpdate}}
Update interval{{.UpdateInterval}}
Backends{{.Backends}}
Attributes
{{.Name}}{{.Value}}
No attributes
Services
{{.Name}}
No services
Metrics
{{.Name}}
No Metrics
+

 

+
diff --git a/templates/hosts.tmpl b/templates/hosts.tmpl new file mode 100644 index 0000000..4eabaef --- /dev/null +++ b/templates/hosts.tmpl @@ -0,0 +1,14 @@ +
+

Hosts

+{{if len .}} + + + {{range .}} + + {{end}} +
HostLast update
{{.Name}}{{.LastUpdate}}
+{{else}} +

No results found.

+{{end}} +

 

+
diff --git a/templates/main.tmpl b/templates/main.tmpl new file mode 100644 index 0000000..c48b223 --- /dev/null +++ b/templates/main.tmpl @@ -0,0 +1,48 @@ + + + + {{.Title}} + + + + + + + + + + + + +
+
+ SysDB +
+ +
+ +
+ + +
+{{.Content}} +
+
+ + diff --git a/templates/metric.tmpl b/templates/metric.tmpl new file mode 100644 index 0000000..e4a0efb --- /dev/null +++ b/templates/metric.tmpl @@ -0,0 +1,18 @@ +
{{$m := index .Metrics 0}} +

Metric {{$.Name}} — {{$m.Name}}

+ + + + + +{{if len $m.Attributes}} + + {{range $m.Attributes}} + + {{end}} +{{else}} + +{{end}} +
Host{{$.Name}}
Last update{{$m.LastUpdate}}
Update interval{{$m.UpdateInterval}}
Backends{{$m.Backends}}
Attributes
{{.Name}}{{.Value}}
No attributes
+

 

+
diff --git a/templates/metrics.tmpl b/templates/metrics.tmpl new file mode 100644 index 0000000..88fe308 --- /dev/null +++ b/templates/metrics.tmpl @@ -0,0 +1,18 @@ +
+

Metrics

+{{if len .}} + + + {{range $h := .}} + {{range $i, $m := $h.Metrics}} + {{if not $i}} + + {{else}} + + {{end}}{{end}}{{end}} +
HostMetricLast update
{{$h.Name}}{{$m.Name}}{{$m.LastUpdate}}
{{$m.Name}}{{$m.LastUpdate}}
+{{else}} +

No results found.

+{{end}} +

 

+
diff --git a/templates/service.tmpl b/templates/service.tmpl new file mode 100644 index 0000000..c5716da --- /dev/null +++ b/templates/service.tmpl @@ -0,0 +1,18 @@ +
{{$s := index .Services 0}} +

Service {{$.Name}} — {{$s.Name}}

+ + + + + +{{if len $s.Attributes}} + + {{range $s.Attributes}} + + {{end}} +{{else}} + +{{end}} +
Host{{$.Name}}
Last update{{$s.LastUpdate}}
Update interval{{$s.UpdateInterval}}
Backends{{$s.Backends}}
Attributes
{{.Name}}{{.Value}}
No attributes
+

 

+
diff --git a/templates/services.tmpl b/templates/services.tmpl new file mode 100644 index 0000000..151d239 --- /dev/null +++ b/templates/services.tmpl @@ -0,0 +1,18 @@ +
+

Services

+{{if len .}} + + + {{range $h := .}} + {{range $i, $s := $h.Services}} + {{if not $i}} + + {{else}} + + {{end}}{{end}}{{end}} +
HostServiceLast update
{{$h.Name}}{{$s.Name}}{{$s.LastUpdate}}
{{$s.Name}}{{$s.LastUpdate}}
+{{else}} +

No results found.

+{{end}} +

 

+