Files
akvorado/outlet/metadata/provider/gnmi/root.go
Vincent Bernat e20645c92e outlet/metadata: synchronous fetching of metadata
As we are not constrained by time that much in the outlet, we can
simplify the fetching of metadata by doing it synchronously. We still
keep the breaker design to avoid continously polling a source that is
not responsive, so we still can loose some data if we are not able to
poll metadata. We also keep the background cache refresh. We also
introduce a grace time of 1 minute to avoid loosing data during start.

For the static provider, we wait for the remote data sources to be
ready. For the gNMI provider, there are target windows of availability
during which the cached data can be polled. The SNMP provider is loosing
its ability to coalesce requests.
2025-07-27 21:44:28 +02:00

88 lines
1.9 KiB
Go

// SPDX-FileCopyrightText: 2023 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
// Package gnmi use gNMI to get interface names and descriptions.
package gnmi
import (
"context"
"net/netip"
"sync"
"akvorado/common/reporter"
"akvorado/outlet/metadata/provider"
)
// Provider represents the gNMI provider.
type Provider struct {
r *reporter.Reporter
config *Configuration
metrics metrics
state map[netip.Addr]*exporterState
stateLock sync.Mutex
refresh chan bool
}
// New creates a new gNMI provider from configuration
func (configuration Configuration) New(r *reporter.Reporter) (provider.Provider, error) {
p := Provider{
r: r,
config: &configuration,
state: map[netip.Addr]*exporterState{},
refresh: make(chan bool),
}
p.initMetrics()
return &p, nil
}
// Query queries exporter to get information through gNMI.
func (p *Provider) Query(ctx context.Context, q provider.Query) (provider.Answer, error) {
p.stateLock.Lock()
state, ok := p.state[q.ExporterIP]
if !ok {
state = &exporterState{
Ready: make(chan bool),
}
p.state[q.ExporterIP] = state
p.metrics.collectorCount.Inc()
go p.startCollector(ctx, q.ExporterIP, state)
}
// Trigger a refresh
select {
case p.refresh <- true:
default:
}
// Wait for the collector to be ready.
select {
case <-state.Ready:
// Most common case, keep the lock
default:
// Not ready, release the lock until ready
p.stateLock.Unlock()
select {
case <-state.Ready:
p.stateLock.Lock()
case <-ctx.Done():
p.metrics.errors.WithLabelValues(q.ExporterIP.Unmap().String(), "not ready").Inc()
return provider.Answer{}, ctx.Err()
}
}
defer p.stateLock.Unlock()
// Return the result from the state
iface, ok := state.Interfaces[q.IfIndex]
if !ok {
return provider.Answer{}, nil
}
return provider.Answer{
Found: true,
Exporter: provider.Exporter{
Name: state.Name,
},
Interface: iface,
}, nil
}