icinga

Icinga2 API client in Go
git clone https://git.olowe.co/icinga
Log | Files | Refs | README | LICENSE

commit 43c71de36cec7d072d2436925b93cf2e94b71606
parent f21c51c745099616c3451e39569df1b46feae6af
Author: Oliver Lowe <o@olowe.co>
Date:   Thu,  1 Dec 2022 13:58:55 +1100

wip

Diffstat:
Mchecker.go | 18++++++++++++++++++
Acmd/i2cache/i2cache.go | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awatch.go | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 154 insertions(+), 0 deletions(-)

diff --git a/checker.go b/checker.go @@ -124,3 +124,21 @@ func scheduleCheck(c *Client, filter checkFilter) error { } return fmt.Errorf("%s", resp.Status) } + +func nextCheck(objects []checker) time.Time { + soonest := time.Time{} + for _, obj := range objects { + next := time.Time{} + switch obj.(type); obj { + case Host, Service: + next = obj.NextCheck + } + if soonest.IsZero() { + soonest = next + } + if next.Before(soonest) { + soonest = next + } + } + return soonest +} diff --git a/cmd/i2cache/i2cache.go b/cmd/i2cache/i2cache.go @@ -0,0 +1,65 @@ +package main + +type Cache struct { + client *icinga.Client + hosts map[string]*hostEntry + services map[string]*serviceEntry +} + +func NewCache(client *icinga.Client) *Cache { + return &Cache{ + client: client, + hosts: make(map[string]*hostEntry), + services: make(map[string]*serviceEntry), + } +} + +type serviceEntry struct { + accessTime time.Time + watcher *ServiceWatcher + services []Service +} + +func (c *Cache) GetServices(expr string) []icinga.Service { + entry, ok := c.services[expr] + if !ok { + return nil + } + entry.accessTime = time.Now() + if entry.wacher == nil { + entry := entry + entry.watcher = WatchServices(c.client, expr) + go func() { + for msg := range entry.watcher.Updates { + if msg.Err != nil { + // TODO(otl) ?? + } + entry.services = msg.Services + } + }() + } + return entry.services +} + +type hostEntry struct { + accessTime time.Time + hosts []Host +} + +func (c *Cache) GetHosts(expr string) *hostEntry {} + +func (c *Cache) evictOlderThan(d time.Duration) { + t := time.Now().Sub(d) + for _, entry := range c.services { + if entry.accessTime.Before(t) { + entry.kill <- true + delete(c.services[expr]) + } + } + for _, entry := range c.Hosts { + if entry.accessTime.Before(t) { + entry.kill <- true + delete(c.hosts[expr]) + } + } +} diff --git a/watch.go b/watch.go @@ -0,0 +1,71 @@ +package icinga + +// A ServiceWatcher continuously queries Icinga about a set of Services. +// A ServiceWatcher uses the next check time of Services to determine when to query Icinga again. +// If it cannot determine the next check time, the Watcher waits 1 minute before trying again. +type ServiceWatcher struct { + kill chan bool + Updates chan ServiceMsg +} + +type HostWatcher struct { + Kill chan bool + Updates chan HostMsg +} + +type ServiceMsg struct { + Services []Service + Err error +} + +type HostMsg struct { + Hosts []Host + Err error +} + +// Kill stops the Watcher irrevocably. +// A new Watcher must be created with WatchServices. +func (w *ServiceWatcher) Kill() { + w.kill <- true +} + +// WatchServices returns a new Watcher which uses client to +// continuously query Icinga for Services matching the given filter +// expression. +func WatchServices(client *Client, expr string) *Watcher { + timer := time.NewTimer(0) + kill := make(chan bool) + ch := make(chan ServiceMsg) + go func() { + select { + case <-kill: + close(ch) + return + case <-timer.C: + services, err := client.Services(expr) + ch <- ServiceMsg{services, err} + if err != nil { + timer.Reset(1*time.Minute) + } else { + timer.Reset(time.Until(nextCheck(services))) + } + } + } + return &Watcher{ + kill: kill + Updates: ch + } +} + +func WatchHosts(expr string, ch chan HostMsg, stop chan bool) { + timer := time.NewTimer(0) + select { + case <-stop: + close(ch) + return + case <-timer.C: + hosts, err := client.Hosts(expr) + ch <- HostMsg{hosts, err} + timer.Reset(time.Until(NextCheck(hosts))) + } +}