Files
nmon/internal/groups/flags.go

74 lines
2.0 KiB
Go

package groups
import (
"context"
"fmt"
"sort"
"time"
"github.com/v10networks/nmon/internal/store"
)
// Flag kinds.
const (
// FlagBothEnds marks a group holding both ends of one link, which would
// count that link's traffic twice.
FlagBothEnds = "both_ends_of_link"
)
// Flag is a warning about a group's membership.
type Flag struct {
Kind string
// A and B are the two interfaces involved.
A, B string
}
// Flags returns the warnings for the group's current membership.
func (s *Service) Flags(ctx context.Context, groupID int64) ([]Flag, error) {
now := s.o.Clock.Now()
p, err := s.newPlan(ctx, groupID, now, now.Add(time.Nanosecond))
if err != nil {
return nil, err
}
return s.flagsOf(ctx, p)
}
// flagsOf finds pairs of members that are the two ends of one link and were in
// force together at some instant. Without LLDP hints (Options.LinkPeer nil)
// nothing is known about links and nothing is flagged.
func (s *Service) flagsOf(ctx context.Context, p plan) ([]Flag, error) {
ids := p.interfaceIDs()
if s.o.LinkPeer == nil || len(ids) < 2 {
return nil, nil
}
peers, err := s.o.LinkPeer(ctx, ids)
if err != nil {
return nil, fmt.Errorf("groups: link peers: %w", err)
}
var out []Flag
seen := map[[2]string]bool{}
for _, a := range p.members {
for _, b := range p.members {
if a.InterfaceID >= b.InterfaceID || !overlap(a, b) {
continue
}
if peers[a.InterfaceID] != b.InterfaceID && peers[b.InterfaceID] != a.InterfaceID {
continue
}
k := [2]string{a.InterfaceID, b.InterfaceID}
if !seen[k] {
seen[k] = true
out = append(out, Flag{Kind: FlagBothEnds, A: k[0], B: k[1]})
}
}
}
sort.Slice(out, func(i, j int) bool { return out[i].A+out[i].B < out[j].A+out[j].B })
return out, nil
}
// overlap reports whether two memberships were in force at a common instant.
func overlap(a, b store.GroupMember) bool {
return (b.EffectiveTo == nil || a.EffectiveFrom.Before(*b.EffectiveTo)) &&
(a.EffectiveTo == nil || b.EffectiveFrom.Before(*a.EffectiveTo))
}