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.
95 lines
2.5 KiB
Go
95 lines
2.5 KiB
Go
// SPDX-FileCopyrightText: 2023 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
// Package static is a metadata provider using static configuration to answer to
|
|
// requests.
|
|
package static
|
|
|
|
import (
|
|
"time"
|
|
|
|
"akvorado/common/helpers"
|
|
"akvorado/common/remotedatasourcefetcher"
|
|
"akvorado/common/reporter"
|
|
"akvorado/outlet/metadata/provider"
|
|
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// Provider represents the static provider.
|
|
type Provider struct {
|
|
r *reporter.Reporter
|
|
|
|
exporterSourcesFetcher *remotedatasourcefetcher.Component[exporterInfo]
|
|
exportersMap map[string][]exporterInfo
|
|
exporters atomic.Pointer[helpers.SubnetMap[ExporterConfiguration]]
|
|
exportersLock sync.Mutex
|
|
|
|
errLogger reporter.Logger
|
|
|
|
metrics struct {
|
|
notReady reporter.Counter
|
|
}
|
|
}
|
|
|
|
// New creates a new static provider from configuration
|
|
func (configuration Configuration) New(r *reporter.Reporter) (provider.Provider, error) {
|
|
p := &Provider{
|
|
r: r,
|
|
exportersMap: map[string][]exporterInfo{},
|
|
errLogger: r.Sample(reporter.BurstSampler(time.Minute, 3)),
|
|
}
|
|
p.exporters.Store(configuration.Exporters)
|
|
p.initStaticExporters()
|
|
|
|
var err error
|
|
p.exporterSourcesFetcher, err = remotedatasourcefetcher.New[exporterInfo](r,
|
|
p.UpdateRemoteDataSource, "metadata", configuration.ExporterSources)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to initialize remote data source fetcher component: %w", err)
|
|
}
|
|
if err := p.exporterSourcesFetcher.Start(); err != nil {
|
|
return nil, fmt.Errorf("unable to start network sources fetcher component: %w", err)
|
|
}
|
|
|
|
p.metrics.notReady = r.Counter(
|
|
reporter.CounterOpts{
|
|
Name: "not_ready_total",
|
|
Help: "Number of queries failing because the remote data sources are not ready",
|
|
})
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// Query queries static configuration.
|
|
func (p *Provider) Query(ctx context.Context, query provider.Query) (provider.Answer, error) {
|
|
// We wait for all data sources to be ready
|
|
select {
|
|
case <-ctx.Done():
|
|
p.metrics.notReady.Inc()
|
|
p.errLogger.Warn().Msg("remote datasources are not ready")
|
|
return provider.Answer{}, ctx.Err()
|
|
case <-p.exporterSourcesFetcher.DataSourcesReady:
|
|
}
|
|
exporter, ok := p.exporters.Load().Lookup(query.ExporterIP)
|
|
if !ok {
|
|
return provider.Answer{}, provider.ErrSkipProvider
|
|
}
|
|
|
|
iface, ok := exporter.IfIndexes[query.IfIndex]
|
|
if !ok {
|
|
if exporter.SkipMissingInterfaces {
|
|
return provider.Answer{}, provider.ErrSkipProvider
|
|
}
|
|
iface = exporter.Default
|
|
}
|
|
return provider.Answer{
|
|
Found: true,
|
|
Exporter: exporter.Exporter,
|
|
Interface: iface,
|
|
}, nil
|
|
}
|