2021-10-26 10:24:43 +02:00
|
|
|
package metricdata
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-11-26 10:32:36 +01:00
|
|
|
"fmt"
|
2021-10-26 10:24:43 +02:00
|
|
|
|
2021-12-08 10:14:45 +01:00
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/config"
|
2021-10-26 10:24:43 +02:00
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/graph/model"
|
|
|
|
"github.com/ClusterCockpit/cc-jobarchive/schema"
|
|
|
|
)
|
|
|
|
|
2021-12-08 10:14:45 +01:00
|
|
|
type MetricDataRepository interface {
|
2021-12-09 16:25:48 +01:00
|
|
|
// Initialize this MetricDataRepository. One instance of
|
|
|
|
// this interface will only ever be responsible for one cluster.
|
2021-12-08 10:14:45 +01:00
|
|
|
Init(url string) error
|
2021-12-09 16:25:48 +01:00
|
|
|
|
|
|
|
// Return the JobData for the given job, only with the requested metrics.
|
2021-12-08 10:14:45 +01:00
|
|
|
LoadData(job *model.Job, metrics []string, ctx context.Context) (schema.JobData, error)
|
2021-12-09 16:25:48 +01:00
|
|
|
|
|
|
|
// Return a map of metrics to a map of nodes to the metric statistics of the job.
|
2021-12-08 11:50:16 +01:00
|
|
|
LoadStats(job *model.Job, metrics []string, ctx context.Context) (map[string]map[string]schema.MetricStatistics, error)
|
2021-12-09 16:25:48 +01:00
|
|
|
|
|
|
|
// Return a map of nodes to a map of metrics to the data for the requested time.
|
2021-12-08 11:50:16 +01:00
|
|
|
LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error)
|
2021-12-08 10:14:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{}
|
2021-11-26 10:32:36 +01:00
|
|
|
|
2021-12-08 10:14:45 +01:00
|
|
|
var JobArchivePath string
|
|
|
|
|
|
|
|
func Init(jobArchivePath string) error {
|
|
|
|
JobArchivePath = jobArchivePath
|
|
|
|
for _, cluster := range config.Clusters {
|
|
|
|
if cluster.MetricDataRepository != nil {
|
|
|
|
switch cluster.MetricDataRepository.Kind {
|
|
|
|
case "cc-metric-store":
|
|
|
|
ccms := &CCMetricStore{}
|
|
|
|
if err := ccms.Init(cluster.MetricDataRepository.Url); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
metricDataRepos[cluster.ClusterID] = ccms
|
|
|
|
case "influxdb-v2":
|
|
|
|
idb := &InfluxDBv2DataRepository{}
|
|
|
|
if err := idb.Init(cluster.MetricDataRepository.Url); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
metricDataRepos[cluster.ClusterID] = idb
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unkown metric data repository '%s' for cluster '%s'", cluster.MetricDataRepository.Kind, cluster.ClusterID)
|
|
|
|
}
|
|
|
|
}
|
2021-11-26 10:32:36 +01:00
|
|
|
}
|
2021-12-08 10:14:45 +01:00
|
|
|
return nil
|
2021-11-26 10:32:36 +01:00
|
|
|
}
|
|
|
|
|
2021-10-26 10:24:43 +02:00
|
|
|
// Fetches the metric data for a job.
|
|
|
|
func LoadData(job *model.Job, metrics []string, ctx context.Context) (schema.JobData, error) {
|
2021-11-26 10:32:36 +01:00
|
|
|
if job.State == model.JobStateRunning {
|
2021-12-08 10:14:45 +01:00
|
|
|
repo, ok := metricDataRepos[job.ClusterID]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("no metric data repository configured for '%s'", job.ClusterID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return repo.LoadData(job, metrics, ctx)
|
2021-11-26 10:32:36 +01:00
|
|
|
}
|
|
|
|
|
2021-10-26 10:24:43 +02:00
|
|
|
data, err := loadFromArchive(job)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if metrics != nil {
|
|
|
|
res := schema.JobData{}
|
|
|
|
for _, metric := range metrics {
|
|
|
|
if metricdata, ok := data[metric]; ok {
|
|
|
|
res[metric] = metricdata
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used for the jobsFootprint GraphQL-Query. TODO: Rename/Generalize.
|
|
|
|
func LoadAverages(job *model.Job, metrics []string, data [][]schema.Float, ctx context.Context) error {
|
2021-12-08 11:50:16 +01:00
|
|
|
if job.State != model.JobStateRunning {
|
|
|
|
return loadAveragesFromArchive(job, metrics, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
repo, ok := metricDataRepos[job.ClusterID]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("no metric data repository configured for '%s'", job.ClusterID)
|
|
|
|
}
|
|
|
|
|
|
|
|
stats, err := repo.LoadStats(job, metrics, ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, m := range metrics {
|
|
|
|
nodes, ok := stats[m]
|
|
|
|
if !ok {
|
|
|
|
data[i] = append(data[i], schema.NaN)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
sum := 0.0
|
|
|
|
for _, node := range nodes {
|
|
|
|
sum += node.Avg
|
|
|
|
}
|
|
|
|
data[i] = append(data[i], schema.Float(sum))
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|
|
|
|
|
2021-12-08 11:50:16 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error) {
|
|
|
|
repo, ok := metricDataRepos[clusterId]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("no metric data repository configured for '%s'", clusterId)
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := repo.LoadNodeData(clusterId, metrics, nodes, from, to, ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if data == nil {
|
|
|
|
return nil, fmt.Errorf("the metric data repository for '%s' does not support this query", clusterId)
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
2021-10-26 10:24:43 +02:00
|
|
|
}
|