Files
nmon/internal/api/device_extras.go

99 lines
2.9 KiB
Go

package api
import (
"context"
"sort"
"github.com/v10networks/nmon/internal/metrics"
"github.com/v10networks/nmon/internal/store"
)
// maxPushedNames bounds the pushed metric names listed on a device.
const maxPushedNames = 200
// PushedSeries summarises what a device's push senders write.
type PushedSeries struct {
Count int `json:"count" doc:"Distinct series pushed in the last hour."`
Names []string `json:"names" doc:"Their metric names, sorted, at most 200."`
}
// fillM5 adds what M5 attached to a device: its host or guests, the checks that watch it and the series pushed for it.
func (s *server) fillM5(ctx context.Context, d *store.Device, v *DeviceView) error {
v.Guests = []DeviceRef{}
v.Checks = []CheckView{}
v.PushedSeries = PushedSeries{Names: []string{}}
hosts, err := s.Store.ListParents(ctx, d.ID, store.RelHostedOn)
if err != nil {
return err
}
if len(hosts) > 0 {
if p, err := s.Store.GetDeviceByID(ctx, hosts[0].ParentID); err == nil {
v.HostedOn = &DeviceRef{Slug: p.Slug, Name: p.Name}
}
}
guests, err := s.Store.ListChildren(ctx, d.ID, store.RelHostedOn)
if err != nil {
return err
}
for _, g := range guests {
if c, err := s.Store.GetDeviceByID(ctx, g.DeviceID); err == nil && c.RetiredAt == nil {
v.Guests = append(v.Guests, DeviceRef{Slug: c.Slug, Name: c.Name})
}
}
sort.Slice(v.Guests, func(i, j int) bool { return v.Guests[i].Name < v.Guests[j].Name })
if err := s.fillChecks(ctx, d, v); err != nil {
return err
}
v.PushedSeries = s.pushedSeries(ctx, d.Slug)
return nil
}
func (s *server) fillChecks(ctx context.Context, d *store.Device, v *DeviceView) error {
rows, err := s.Store.ListChecks(ctx, true)
if err != nil {
return err
}
res := s.checkResults()
for i := range rows {
if rows[i].DeviceID != nil && *rows[i].DeviceID == d.ID {
v.Checks = append(v.Checks, s.checkView(ctx, &rows[i], res))
}
}
return nil
}
// pushedSeries lists the series the device's push senders wrote in the last hour. A store that is down or slow
// leaves it empty, as for the live rates.
func (s *server) pushedSeries(ctx context.Context, slug string) PushedSeries {
out := PushedSeries{Names: []string{}}
release, err := s.admit(ctx, false)
if err != nil {
return out
}
defer release()
qctx, cancel := context.WithTimeout(ctx, QueryTimeout)
defer cancel()
expr := `count by (__name__) (last_over_time({device=` + quote(slug) + `,` + metrics.LabelPush + `="1"}[1h]))`
body, err := s.Querier.Query(qctx, "hot", expr, s.Clock.Now())
if err != nil {
s.Logger.Warn("pushed series unavailable", "device", slug, "err", err)
return out
}
rows, err := parseVM(body)
if err != nil {
return out
}
for _, r := range rows {
if r.Value == nil || !r.Value.OK || r.Metric["__name__"] == "" {
continue
}
out.Count += int(r.Value.V)
out.Names = append(out.Names, r.Metric["__name__"])
}
sort.Strings(out.Names)
if len(out.Names) > maxPushedNames {
out.Names = out.Names[:maxPushedNames]
}
return out
}