From 97fc96d8f8b5ac83ade0af269ccb98cb1a2abdf0 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 20 Nov 2014 23:19:18 +0100 Subject: [PATCH 1/1] 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. --- COPYING | 24 +++ README | 99 +++++++++++++ main.go | 81 +++++++++++ server/error.go | 71 +++++++++ server/server.go | 299 ++++++++++++++++++++++++++++++++++++++ static/images/favicon.png | Bin 0 -> 891 bytes static/images/owl.png | Bin 0 -> 6187 bytes static/style/main.css | 160 ++++++++++++++++++++ templates/host.tmpl | 33 +++++ templates/hosts.tmpl | 14 ++ templates/main.tmpl | 48 ++++++ templates/metric.tmpl | 18 +++ templates/metrics.tmpl | 18 +++ templates/service.tmpl | 18 +++ templates/services.tmpl | 18 +++ 15 files changed, 901 insertions(+) create mode 100644 COPYING create mode 100644 README create mode 100644 main.go create mode 100644 server/error.go create mode 100644 server/server.go create mode 100644 static/images/favicon.png create mode 100644 static/images/owl.png create mode 100644 static/style/main.css create mode 100644 templates/host.tmpl create mode 100644 templates/hosts.tmpl create mode 100644 templates/main.tmpl create mode 100644 templates/metric.tmpl create mode 100644 templates/metrics.tmpl create mode 100644 templates/service.tmpl create mode 100644 templates/services.tmpl 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 0000000000000000000000000000000000000000..f56ba499011a6f23e70b663b99fa178da34c7b01 GIT binary patch literal 891 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=DcIAk3)sa3&{6u&lr_9Y}-qGsGNQdzgWNsovAYF~s6@?q%PMm{gI5htGExPfnO= z(c2j~NhMQ(=6iSkcY2;<;og)7ALFWj z?>5cPEqQs1cem9$aVG;aJKYw6WTjQx1X=~|+`f{rION;6PL@7nzc_u#%3T)QzF*Yy*3OQa^=@0(oe$T~MsM4`eO7$L zrOP+p9WNI5jo+>J%il{|qoQZ!ON$|KO;- z+S?Fh`SGN=@~NjTmcH4}tBz@6PW&Z=-Ijv_N zU2R(RglpgWbIRR091HDQCL}RF{;{UYXR?8{)tmTpKOXx1p7r7}FkPyaxJHzuB$lLF zB^RXvDF!10Lla#CQ(a?&5JO8VLjx-lQ*8qyD+7Z!%a2#0Xvob^$xN%ntzqu3`lCP% wk{}y`^V3So6N^$A%FE03GV`*FlM@S4_413-XTP(N0xDwgboFyt=akR{0O~)G{{R30 literal 0 HcmV?d00001 diff --git a/static/images/owl.png b/static/images/owl.png new file mode 100644 index 0000000000000000000000000000000000000000..bb54e88ef27c2831d9982244c2aa479397ca4aa7 GIT binary patch literal 6187 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 literal 0 HcmV?d00001 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}} +

 

+
-- 2.30.2