// NewPoller returns a new poller instance func NewPoller(c Config) *Poller { p := &Poller{ config: c, Workers: make([]*Worker, 0), wg: &sync.WaitGroup{}, } logger.Info("poller", "starting poller workers") names := set.New(set.ThreadSafe) for _, n := range c.Nodes { if n.Name == "" { logger.Error("poller", "missing mandatory `name' node parameter") continue } else if n.Address == "" { logger.Error("poller", "missing mandatory `remote_addr' node parameter") continue } else if names.Has(n.Name) { logger.Error("poller", "duplicate name `%s' found in node definition", n.Name) continue } w := NewWorker(n, p.wg) p.Workers = append(p.Workers, w) p.wg.Add(1) go w.Start() w.Refresh() names.Add(n.Name) } return p }
func getComments(r livestatus.Record) ([]Comment, error) { data, err := r.GetSlice("comments_with_extra_info") if err != nil { return []Comment{}, err } result := []Comment{} for _, entry := range data { slice, ok := entry.([]interface{}) if !ok || len(slice) != 5 { logger.Error("poller", "skipping comment entry %v", entry) continue } ct, ok := slice[3].(float64) if !ok { logger.Error("poller", "unable to parse comment type: skipping entry %v", entry) continue } cd, ok := slice[4].(float64) if !ok { logger.Error("poller", "unable to parse comment date: skipping entry %v", entry) continue } result = append(result, Comment{ Author: fmt.Sprintf("%v", slice[1]), Content: fmt.Sprintf("%v", slice[2]), Type: int64(ct), Date: time.Unix(int64(cd), 0), }) } return result, nil }
// Run starts the server processing func (s *Server) Run() error { var err error logger.Info("server", "service started") // Initialize poller s.poller = poller.NewPoller(s.config.Poller) // Initialize HTTP router gin.SetMode(gin.ReleaseMode) s.router = gin.New() s.router.RedirectTrailingSlash = true // Set static files handlers s.router.Any("/", s.handleStatic) s.router.Any("/static/*path", s.handleStatic) // Set API handlers s.router.Any("/api/hosts/:name", s.handleAPIHost) s.router.Any("/api/hosts/", s.handleAPIHosts) s.router.Any("/api/groups/", s.handleAPIGroups) s.router.Any("/api/nodes/", s.handleAPINodes) s.router.Any("/api/search/", s.handleAPISearch) // Start HTTP router logger.Info("server", "listening on %q", s.config.BindAddr) err = s.router.Run(s.config.BindAddr) if err != nil { logger.Error("server", "failed to initialize router: %s", err) return err } return nil }
func (w *Worker) poll() { var ( l *livestatus.Livestatus q *livestatus.Query resp *livestatus.Response services map[string][]livestatus.Record err error ) // Skip refresh if already in refreshing state if w.refreshing { logger.Warning("poller", "worker is already refreshing data from %q node", w.Config.Name) return } w.refreshing = true logger.Debug("poller", "refreshing data from %q node", w.Config.Name) c := NewCatalog(w) // Create new LiveStatus instance args := strings.SplitN(w.Config.Address, ":", 2) if len(args) != 2 { logger.Error("poller", "invalid node address %q", w.Config.Address) goto end } l = livestatus.NewLivestatus(args[0], args[1]) // Query services data q = l.Query("services") q.Columns( "host_name", "description", "state", "last_state_change", "comments_with_extra_info", "plugin_output", "groups", ) q.Filter("state_type > 0") resp, err = q.Exec() if err != nil { logger.Error("poller", "unable to retrieve services from %q node: %s", w.Config.Name, err) goto end } services = make(map[string][]livestatus.Record) for _, r := range resp.Records { name, err := r.GetString("host_name") if err != nil { logger.Error("poller", "unable to retrieve \"host_name\" from %q node: %s", w.Config.Name, err) goto end } if _, ok := services[name]; !ok { services[name] = make([]livestatus.Record, 0) } services[name] = append(services[name], r) } // Query hosts data q = l.Query("hosts") q.Columns( "host_name", "state", "last_state_change", "comments_with_extra_info", "groups", ) resp, err = q.Exec() if err != nil { logger.Error("poller", "unable to retrieve hosts from %q node: %s", w.Config.Name, err) goto end } // Fill catalog with retrieved data for _, r := range resp.Records { h := &Host{ Catalog: c, } h.Name, _ = r.GetString("host_name") h.State, _ = r.GetInt("state") h.StateChanged, _ = r.GetTime("last_state_change") h.Comments, _ = getComments(r) h.Links, _ = getLinks(h.Name, "", w.Config.Links) h.Groups, _ = getStringSlice(r, "groups") h.Services = make([]*Service, 0) if _, ok := services[h.Name]; ok { for _, sr := range services[h.Name] { s := &Service{Host: h} s.Name, _ = sr.GetString("description") s.State, _ = sr.GetInt("state") s.StateChanged, _ = sr.GetTime("last_state_change") s.Comments, _ = getComments(sr) s.Links, _ = getLinks(h.Name, s.Name, w.Config.Links) s.Output, _ = sr.GetString("plugin_output") s.Groups, _ = getStringSlice(sr, "groups") h.Services = append(h.Services, s) } } c.Hosts = append(c.Hosts, h) } // Replace catalog w.Catalog = c end: // Reset refreshing state w.refreshing = false }