Added MIA status for hosts that haven't been updated for 30 days (#570)

This commit is contained in:
John Murphy 2016-12-07 01:37:22 +08:00 committed by GitHub
parent 21ec1ed1b1
commit e6b1ed9ade
8 changed files with 159 additions and 72 deletions

View file

@ -9,6 +9,19 @@ type TargetSearchResults struct {
Labels []Label
}
// TargetMetrics contains information about the state
// of hosts that are tracked by the app
type TargetMetrics struct {
TotalHosts uint
// OnlineHosts have updated within the last 30 minutes
OnlineHosts uint
// OfflineHosts are hosts that haven't updated in 30 minutes
OfflineHosts uint
// MissingInActionHosts are hosts that haven't had an update for more
// than thirty days
MissingInActionHosts uint
}
type TargetService interface {
// SearchTargets will accept a search query, a slice of IDs of hosts to omit,
// and a slice of IDs of labels to omit, and it will return a set of targets
@ -17,8 +30,10 @@ type TargetService interface {
// CountHostsInTargets returns the count of hosts in the selected
// targets. The first return uint is the total number of hosts in the
// targets. The second return uint is the total online hosts.
CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (total uint, online uint, err error)
// targets. The second return uint is the total online hosts. The third
// returned uint is the total number of hosts that have been offline for more
// than 30 days. (Missing in action)
CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (*TargetMetrics, error)
}
type TargetType int

View file

@ -1,11 +1,28 @@
package service
import (
"time"
"github.com/go-kit/kit/endpoint"
"github.com/kolide/kolide-ose/server/kolide"
"golang.org/x/net/context"
)
const (
// StatusOnline host is active
StatusOnline string = "online"
// StatusOffline no communication with host for OfflineDuration
StatusOffline string = "offline"
// StatusMIA no communition with host for MIADuration
StatusMIA string = "mia"
// OfflineDuration if a host hasn't been in communition for this
// period it is considered offline
OfflineDuration time.Duration = 30 * time.Minute
// OfflineDuration if a host hasn't been in communition for this
// period it is considered MIA
MIADuration time.Duration = 30 * 24 * time.Hour
)
type hostResponse struct {
kolide.Host
Status string `json:"status"`

View file

@ -16,9 +16,11 @@ type getLabelRequest struct {
type labelResponse struct {
kolide.Label
DisplayText string `json:"display_text"`
Count uint `json:"count"`
Online uint `json:"online"`
DisplayText string `json:"display_text"`
Count uint `json:"count"`
Online uint `json:"online"`
Offline uint `json:"offline"`
MissingInAction uint `json:"missing_in_action"`
}
type getLabelResponse struct {
@ -35,7 +37,7 @@ func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint {
if err != nil {
return getLabelResponse{Err: err}, nil
}
total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
if err != nil {
return getLabelResponse{Err: err}, nil
}
@ -43,8 +45,10 @@ func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint {
Label: labelResponse{
*label,
label.Name,
total,
online,
metrics.TotalHosts,
metrics.OnlineHosts,
metrics.OfflineHosts,
metrics.MissingInActionHosts,
},
}, nil
}
@ -75,7 +79,7 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint {
resp := listLabelsResponse{}
for _, label := range labels {
total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
if err != nil {
return listLabelsResponse{Err: err}, nil
}
@ -83,8 +87,10 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint {
labelResponse{
*label,
label.Name,
total,
online,
metrics.TotalHosts,
metrics.OnlineHosts,
metrics.OfflineHosts,
metrics.MissingInActionHosts,
},
)
}
@ -114,7 +120,7 @@ func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint {
if err != nil {
return createLabelResponse{Err: err}, nil
}
total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
if err != nil {
return createLabelResponse{Err: err}, nil
}
@ -122,8 +128,10 @@ func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint {
Label: labelResponse{
*label,
label.Name,
total,
online,
metrics.TotalHosts,
metrics.OnlineHosts,
metrics.OfflineHosts,
metrics.MissingInActionHosts,
},
}, nil
}

View file

@ -25,9 +25,10 @@ type hostSearchResult struct {
type labelSearchResult struct {
kolide.Label
DisplayText string `json:"display_text"`
Count uint `json:"count"`
Online uint `json:"online"`
DisplayText string `json:"display_text"`
Count uint `json:"count"`
Online uint `json:"online"`
MissingInAction uint `json:"missing_in_action"`
}
type targetsData struct {
@ -36,10 +37,11 @@ type targetsData struct {
}
type searchTargetsResponse struct {
Targets *targetsData `json:"targets,omitempty"`
SelectedTargetsCount uint `json:"selected_targets_count"`
SelectedTargetsOnline uint `json:"selected_targets_online"`
Err error `json:"error,omitempty"`
Targets *targetsData `json:"targets,omitempty"`
SelectedTargetsCount uint `json:"selected_targets_count"`
SelectedTargetsOnline uint `json:"selected_targets_online"`
SelectedTargetsMissingInAction uint `json:"selected_targets_missing_in_action"`
Err error `json:"error,omitempty"`
}
func (r searchTargetsResponse) error() error { return r.Err }
@ -68,7 +70,7 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint {
}
for _, label := range results.Labels {
total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID})
if err != nil {
return searchTargetsResponse{Err: err}, nil
}
@ -76,21 +78,23 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint {
labelSearchResult{
label,
label.Name,
total,
online,
metrics.TotalHosts,
metrics.OnlineHosts,
metrics.MissingInActionHosts,
},
)
}
total, online, err := svc.CountHostsInTargets(ctx, req.Selected.Hosts, req.Selected.Labels)
metrics, err := svc.CountHostsInTargets(ctx, req.Selected.Hosts, req.Selected.Labels)
if err != nil {
return searchTargetsResponse{Err: err}, nil
}
return searchTargetsResponse{
Targets: targets,
SelectedTargetsCount: total,
SelectedTargetsOnline: online,
Targets: targets,
SelectedTargetsCount: metrics.TotalHosts,
SelectedTargetsOnline: metrics.OnlineHosts,
SelectedTargetsMissingInAction: metrics.MissingInActionHosts,
}, nil
}
}

View file

@ -63,8 +63,10 @@ func (svc service) NewDistributedQueryCampaign(ctx context.Context, queryString
}
type targetTotals struct {
Total uint `json:"count"`
Online uint `json:"online"`
Total uint `json:"count"`
Online uint `json:"online"`
Offline uint `json:"offline"`
MissingInAction uint `json:"missing_in_action"`
}
func (svc service) StreamCampaignResults(ctx context.Context, conn *websocket.Conn, campaignID uint) {
@ -126,16 +128,20 @@ func (svc service) StreamCampaignResults(ctx context.Context, conn *websocket.Co
}
}
var totals targetTotals
totals.Total, totals.Online, err = svc.CountHostsInTargets(
context.Background(), hostIDs, labelIDs,
)
metrics, err := svc.CountHostsInTargets(context.Background(), hostIDs, labelIDs)
if err != nil {
if err = conn.WriteJSONError("error retrieving target counts"); err != nil {
return
}
}
totals := targetTotals{
Total: metrics.TotalHosts,
Online: metrics.OnlineHosts,
Offline: metrics.OfflineHosts,
MissingInAction: metrics.MissingInActionHosts,
}
if err = conn.WriteJSONMessage("totals", totals); err != nil {
return
}

View file

@ -1,8 +1,6 @@
package service
import (
"time"
"github.com/kolide/kolide-ose/server/kolide"
"golang.org/x/net/context"
)
@ -16,11 +14,14 @@ func (svc service) GetHost(ctx context.Context, id uint) (*kolide.Host, error) {
}
func (svc service) HostStatus(ctx context.Context, host kolide.Host) string {
if host.UpdatedAt.Add(30 * time.Minute).Before(svc.clock.Now()) {
return "offline"
} else {
return "online"
if host.UpdatedAt.Add(OfflineDuration).Before(svc.clock.Now()) {
if host.UpdatedAt.Add(MIADuration).Before(svc.clock.Now()) {
return StatusMIA
}
return StatusOffline
}
return StatusOnline
}
func (svc service) DeleteHost(ctx context.Context, id uint) error {

View file

@ -23,30 +23,39 @@ func (svc service) SearchTargets(ctx context.Context, query string, selectedHost
return results, nil
}
func (svc service) CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (total uint, online uint, err error) {
func (svc service) CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (*kolide.TargetMetrics, error) {
hosts, err := svc.ds.ListUniqueHostsInLabels(labelIDs)
if err != nil {
return 0, 0, err
return nil, err
}
for _, id := range hostIDs {
h, err := svc.ds.Host(id)
if err != nil {
return 0, 0, err
return nil, err
}
hosts = append(hosts, *h)
}
hostLookup := map[uint]bool{}
online = uint(0)
result := &kolide.TargetMetrics{}
for _, host := range hosts {
if !hostLookup[host.ID] {
hostLookup[host.ID] = true
if svc.HostStatus(ctx, host) == "online" {
online++
switch svc.HostStatus(ctx, host) {
case StatusOnline:
result.OnlineHosts++
case StatusOffline:
result.OfflineHosts++
case StatusMIA:
result.MissingInActionHosts++
}
}
}
return uint(len(hostLookup)), online, nil
result.TotalHosts = uint(len(hostLookup))
return result, nil
}

View file

@ -96,6 +96,15 @@ func TestCountHostsInTargets(t *testing.T) {
require.Nil(t, err)
require.Nil(t, ds.MarkHostSeen(h5, mockClock.Now()))
h6, err := ds.NewHost(&kolide.Host{
HostName: "zzz.local",
NodeKey: "6",
UUID: "6",
})
require.Nil(t, err)
const thirtyDaysAndAMinuteAgo = -1 * (30*24*60 + 1)
require.Nil(t, ds.MarkHostSeen(h6, mockClock.Now().Add(thirtyDaysAndAMinuteAgo*time.Minute)))
l1, err := ds.NewLabel(&kolide.Label{
Name: "label foo",
Query: "query foo",
@ -112,7 +121,7 @@ func TestCountHostsInTargets(t *testing.T) {
require.NotZero(t, l2.ID)
l2ID := fmt.Sprintf("%d", l2.ID)
for _, h := range []*kolide.Host{h1, h2, h3} {
for _, h := range []*kolide.Host{h1, h2, h3, h6} {
err = ds.RecordLabelQueryExecutions(h, map[string]bool{l1ID: true}, time.Now())
assert.Nil(t, err)
}
@ -122,37 +131,55 @@ func TestCountHostsInTargets(t *testing.T) {
assert.Nil(t, err)
}
total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID})
assert.Nil(t, err)
assert.Equal(t, uint(5), total)
assert.Equal(t, uint(4), online)
metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID})
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(6), metrics.TotalHosts)
assert.Equal(t, uint(1), metrics.OfflineHosts)
assert.Equal(t, uint(4), metrics.OnlineHosts)
assert.Equal(t, uint(1), metrics.MissingInActionHosts)
total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, []uint{l1.ID, l2.ID})
assert.Nil(t, err)
assert.Equal(t, uint(5), total)
assert.Equal(t, uint(4), online)
metrics, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, []uint{l1.ID, l2.ID})
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(6), metrics.TotalHosts)
assert.Equal(t, uint(1), metrics.OfflineHosts)
assert.Equal(t, uint(4), metrics.OnlineHosts)
assert.Equal(t, uint(1), metrics.MissingInActionHosts)
total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, nil)
assert.Nil(t, err)
assert.Equal(t, uint(2), total)
assert.Equal(t, uint(1), online)
metrics, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, nil)
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(2), metrics.TotalHosts)
assert.Equal(t, uint(1), metrics.OnlineHosts)
assert.Equal(t, uint(1), metrics.OfflineHosts)
assert.Equal(t, uint(0), metrics.MissingInActionHosts)
total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID}, []uint{l2.ID})
assert.Nil(t, err)
assert.Equal(t, uint(4), total)
assert.Equal(t, uint(4), online)
metrics, err = svc.CountHostsInTargets(ctx, []uint{h1.ID}, []uint{l2.ID})
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(4), metrics.TotalHosts)
assert.Equal(t, uint(4), metrics.OnlineHosts)
assert.Equal(t, uint(0), metrics.OfflineHosts)
assert.Equal(t, uint(0), metrics.MissingInActionHosts)
total, online, err = svc.CountHostsInTargets(ctx, nil, nil)
assert.Nil(t, err)
assert.Equal(t, uint(0), total)
assert.Equal(t, uint(0), online)
metrics, err = svc.CountHostsInTargets(ctx, nil, nil)
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(0), metrics.TotalHosts)
assert.Equal(t, uint(0), metrics.OnlineHosts)
assert.Equal(t, uint(0), metrics.OfflineHosts)
assert.Equal(t, uint(0), metrics.MissingInActionHosts)
// Advance clock so all hosts are offline
mockClock.AddTime(1 * time.Hour)
total, online, err = svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID})
assert.Nil(t, err)
assert.Equal(t, uint(5), total)
assert.Equal(t, uint(0), online)
metrics, err = svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID})
require.Nil(t, err)
require.NotNil(t, metrics)
assert.Equal(t, uint(6), metrics.TotalHosts)
assert.Equal(t, uint(0), metrics.OnlineHosts)
assert.Equal(t, uint(5), metrics.OfflineHosts)
assert.Equal(t, uint(1), metrics.MissingInActionHosts)
}