Files
nmon/internal/groups/sum.go

88 lines
2.1 KiB
Go

package groups
import (
"time"
"github.com/v10networks/nmon/internal/p95"
"github.com/v10networks/nmon/internal/store"
)
// slot is the group's oriented traffic in one 5-minute window.
type slot struct {
TS int64 // window start, Unix ms
In, Out float64
// AnyIn and AnyOut say at least one member contributed a sample.
AnyIn, AnyOut bool
// FullIn and FullOut say every contributing member had a sample, so the
// sum is not understated.
FullIn, FullOut bool
}
// orient maps a member's raw directions to the group's: "toward" keeps them,
// "away" swaps them, since "in" on the port is then traffic leaving the group's
// subject.
func orient(m store.GroupMember) (inDir, outDir string) {
if m.Direction == store.DirAway {
return p95.DirOut, p95.DirIn
}
return p95.DirIn, p95.DirOut
}
// index turns sorted points into a timestamp lookup.
func index(rates map[p95.Key][]p95.Point) map[p95.Key]map[int64]float64 {
out := make(map[p95.Key]map[int64]float64, len(rates))
for k, pts := range rates {
m := make(map[int64]float64, len(pts))
for _, p := range pts {
m[p.TS] = p.V
}
out[k] = m
}
return out
}
// sum computes the slots of [from, to). A window with no member in force has
// no slot: no membership is not missing data.
func (p plan) sum(rates map[p95.Key][]p95.Point, from, to time.Time) []slot {
idx := index(rates)
var out []slot
for t := from; t.Before(to); t = t.Add(p95.Window) {
members := p.eligibleAt(t)
if len(members) == 0 {
continue
}
s := slot{TS: t.UnixMilli(), FullIn: true, FullOut: true}
for _, m := range members {
inDir, outDir := orient(m)
if v, ok := idx[p95.Key{Iface: m.InterfaceID, Dir: p95.DirIn}][s.TS]; ok {
s.add(inDir, v)
} else {
s.miss(inDir)
}
if v, ok := idx[p95.Key{Iface: m.InterfaceID, Dir: p95.DirOut}][s.TS]; ok {
s.add(outDir, v)
} else {
s.miss(outDir)
}
}
out = append(out, s)
}
return out
}
func (s *slot) add(dir string, v float64) {
if dir == p95.DirIn {
s.In, s.AnyIn = s.In+v, true
return
}
s.Out, s.AnyOut = s.Out+v, true
}
func (s *slot) miss(dir string) {
if dir == p95.DirIn {
s.FullIn = false
return
}
s.FullOut = false
}