Commit Diff


commit - f21c51c745099616c3451e39569df1b46feae6af
commit + 43c71de36cec7d072d2436925b93cf2e94b71606
blob - c24b1c8b4b98bd3a7e656f48e9f62c5cc40912a7
blob + 493802b3395fcdbcea77e51d9182872d21248345
--- checker.go
+++ checker.go
@@ -124,3 +124,21 @@ func scheduleCheck(c *Client, filter checkFilter) erro
 	}
 	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
+}
blob - /dev/null
blob + d619bf13ce259580676700dc4f34061acc56a17d (mode 644)
--- /dev/null
+++ 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])
+		}
+	}
+}
blob - /dev/null
blob + 053c8422dbb89f47a6db1921ef5d82dcc969e749 (mode 644)
--- /dev/null
+++ 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)))
+	}
+}