mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
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.
88 lines
1.9 KiB
Go
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
|
|
}
|