Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
74 lines
2.0 KiB
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))
|
|
}
|