start working on non-node-scoped metrics in node/system view

This commit is contained in:
Lou Knauer 2022-01-31 15:16:05 +01:00
parent 84431585f9
commit 29a25dbaff
8 changed files with 107 additions and 264 deletions

View File

@ -156,13 +156,8 @@ type ComplexityRoot struct {
UpdateConfiguration func(childComplexity int, name string, value string) int UpdateConfiguration func(childComplexity int, name string, value string) int
} }
NodeMetric struct {
Data func(childComplexity int) int
Name func(childComplexity int) int
}
NodeMetrics struct { NodeMetrics struct {
ID func(childComplexity int) int Host func(childComplexity int) int
Metrics func(childComplexity int) int Metrics func(childComplexity int) int
} }
@ -185,7 +180,7 @@ type ComplexityRoot struct {
Jobs func(childComplexity int, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) int Jobs func(childComplexity int, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) int
JobsFootprints func(childComplexity int, filter []*model.JobFilter, metrics []string) int JobsFootprints func(childComplexity int, filter []*model.JobFilter, metrics []string) int
JobsStatistics func(childComplexity int, filter []*model.JobFilter, groupBy *model.Aggregate) int JobsStatistics func(childComplexity int, filter []*model.JobFilter, groupBy *model.Aggregate) int
NodeMetrics func(childComplexity int, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) int NodeMetrics func(childComplexity int, cluster string, partition string, nodes []string, scopes []string, metrics []string, from time.Time, to time.Time) int
RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int
Tags func(childComplexity int) int Tags func(childComplexity int) int
} }
@ -250,7 +245,7 @@ type QueryResolver interface {
Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error) Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error)
JobsStatistics(ctx context.Context, filter []*model.JobFilter, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) JobsStatistics(ctx context.Context, filter []*model.JobFilter, groupBy *model.Aggregate) ([]*model.JobsStatistics, error)
RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error) RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error)
NodeMetrics(ctx context.Context, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) NodeMetrics(ctx context.Context, cluster string, partition string, nodes []string, scopes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error)
} }
type executableSchema struct { type executableSchema struct {
@ -769,26 +764,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.UpdateConfiguration(childComplexity, args["name"].(string), args["value"].(string)), true return e.complexity.Mutation.UpdateConfiguration(childComplexity, args["name"].(string), args["value"].(string)), true
case "NodeMetric.data": case "NodeMetrics.host":
if e.complexity.NodeMetric.Data == nil { if e.complexity.NodeMetrics.Host == nil {
break break
} }
return e.complexity.NodeMetric.Data(childComplexity), true return e.complexity.NodeMetrics.Host(childComplexity), true
case "NodeMetric.name":
if e.complexity.NodeMetric.Name == nil {
break
}
return e.complexity.NodeMetric.Name(childComplexity), true
case "NodeMetrics.id":
if e.complexity.NodeMetrics.ID == nil {
break
}
return e.complexity.NodeMetrics.ID(childComplexity), true
case "NodeMetrics.metrics": case "NodeMetrics.metrics":
if e.complexity.NodeMetrics.Metrics == nil { if e.complexity.NodeMetrics.Metrics == nil {
@ -937,7 +918,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Query.NodeMetrics(childComplexity, args["cluster"].(string), args["nodes"].([]string), args["metrics"].([]string), args["from"].(time.Time), args["to"].(time.Time)), true return e.complexity.Query.NodeMetrics(childComplexity, args["cluster"].(string), args["partition"].(string), args["nodes"].([]string), args["scopes"].([]string), args["metrics"].([]string), args["from"].(time.Time), args["to"].(time.Time)), true
case "Query.rooflineHeatmap": case "Query.rooflineHeatmap":
if e.complexity.Query.RooflineHeatmap == nil { if e.complexity.Query.RooflineHeatmap == nil {
@ -1299,19 +1280,14 @@ type MetricFootprints {
enum Aggregate { USER, PROJECT, CLUSTER } enum Aggregate { USER, PROJECT, CLUSTER }
type NodeMetric {
name: String!
data: [NullableFloat!]!
}
type NodeMetrics { type NodeMetrics {
id: String! host: String!
metrics: [NodeMetric!]! metrics: [JobMetricWithName!]!
} }
type Query { type Query {
clusters: [Cluster!]! # List of all clusters clusters: [Cluster!]! # List of all clusters
tags: [Tag!]! # List of all tags tags: [Tag!]! # List of all tags
job(id: ID!): Job job(id: ID!): Job
jobMetrics(id: ID!, metrics: [String!], scopes: [MetricScope!]): [JobMetricWithName!]! jobMetrics(id: ID!, metrics: [String!], scopes: [MetricScope!]): [JobMetricWithName!]!
@ -1322,7 +1298,7 @@ type Query {
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]! rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
nodeMetrics(cluster: ID!, nodes: [String!], metrics: [String!], from: Time!, to: Time!): [NodeMetrics!]! nodeMetrics(cluster: String!, partition: String!, nodes: [String!], scopes: [String!], metrics: [String!], from: Time!, to: Time!): [NodeMetrics!]!
} }
type Mutation { type Mutation {
@ -1681,48 +1657,66 @@ func (ec *executionContext) field_Query_nodeMetrics_args(ctx context.Context, ra
var arg0 string var arg0 string
if tmp, ok := rawArgs["cluster"]; ok { if tmp, ok := rawArgs["cluster"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster"))
arg0, err = ec.unmarshalNID2string(ctx, tmp) arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["cluster"] = arg0 args["cluster"] = arg0
var arg1 []string var arg1 string
if tmp, ok := rawArgs["nodes"]; ok { if tmp, ok := rawArgs["partition"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nodes")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("partition"))
arg1, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) arg1, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["nodes"] = arg1 args["partition"] = arg1
var arg2 []string var arg2 []string
if tmp, ok := rawArgs["metrics"]; ok { if tmp, ok := rawArgs["nodes"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nodes"))
arg2, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) arg2, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["metrics"] = arg2 args["nodes"] = arg2
var arg3 time.Time var arg3 []string
if tmp, ok := rawArgs["scopes"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes"))
arg3, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["scopes"] = arg3
var arg4 []string
if tmp, ok := rawArgs["metrics"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics"))
arg4, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["metrics"] = arg4
var arg5 time.Time
if tmp, ok := rawArgs["from"]; ok { if tmp, ok := rawArgs["from"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("from")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("from"))
arg3, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp) arg5, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["from"] = arg3 args["from"] = arg5
var arg4 time.Time var arg6 time.Time
if tmp, ok := rawArgs["to"]; ok { if tmp, ok := rawArgs["to"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("to")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("to"))
arg4, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp) arg6, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["to"] = arg4 args["to"] = arg6
return args, nil return args, nil
} }
@ -4230,77 +4224,7 @@ func (ec *executionContext) _Mutation_updateConfiguration(ctx context.Context, f
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _NodeMetric_name(ctx context.Context, field graphql.CollectedField, obj *model.NodeMetric) (ret graphql.Marshaler) { func (ec *executionContext) _NodeMetrics_host(ctx context.Context, field graphql.CollectedField, obj *model.NodeMetrics) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "NodeMetric",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Name, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _NodeMetric_data(ctx context.Context, field graphql.CollectedField, obj *model.NodeMetric) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "NodeMetric",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Data, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]schema.Float)
fc.Result = res
return ec.marshalNNullableFloat2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋschemaᚐFloatᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _NodeMetrics_id(ctx context.Context, field graphql.CollectedField, obj *model.NodeMetrics) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -4318,7 +4242,7 @@ func (ec *executionContext) _NodeMetrics_id(ctx context.Context, field graphql.C
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.ID, nil return obj.Host, nil
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -4365,9 +4289,9 @@ func (ec *executionContext) _NodeMetrics_metrics(ctx context.Context, field grap
} }
return graphql.Null return graphql.Null
} }
res := resTmp.([]*model.NodeMetric) res := resTmp.([]*model.JobMetricWithName)
fc.Result = res fc.Result = res
return ec.marshalNNodeMetric2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetric(ctx, field.Selections, res) return ec.marshalNJobMetricWithName2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐJobMetricWithName(ctx, field.Selections, res)
} }
func (ec *executionContext) _Partition_name(ctx context.Context, field graphql.CollectedField, obj *model.Partition) (ret graphql.Marshaler) { func (ec *executionContext) _Partition_name(ctx context.Context, field graphql.CollectedField, obj *model.Partition) (ret graphql.Marshaler) {
@ -5029,7 +4953,7 @@ func (ec *executionContext) _Query_nodeMetrics(ctx context.Context, field graphq
fc.Args = args fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().NodeMetrics(rctx, args["cluster"].(string), args["nodes"].([]string), args["metrics"].([]string), args["from"].(time.Time), args["to"].(time.Time)) return ec.resolvers.Query().NodeMetrics(rctx, args["cluster"].(string), args["partition"].(string), args["nodes"].([]string), args["scopes"].([]string), args["metrics"].([]string), args["from"].(time.Time), args["to"].(time.Time))
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -7928,38 +7852,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
return out return out
} }
var nodeMetricImplementors = []string{"NodeMetric"}
func (ec *executionContext) _NodeMetric(ctx context.Context, sel ast.SelectionSet, obj *model.NodeMetric) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, nodeMetricImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("NodeMetric")
case "name":
out.Values[i] = ec._NodeMetric_name(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "data":
out.Values[i] = ec._NodeMetric_data(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var nodeMetricsImplementors = []string{"NodeMetrics"} var nodeMetricsImplementors = []string{"NodeMetrics"}
func (ec *executionContext) _NodeMetrics(ctx context.Context, sel ast.SelectionSet, obj *model.NodeMetrics) graphql.Marshaler { func (ec *executionContext) _NodeMetrics(ctx context.Context, sel ast.SelectionSet, obj *model.NodeMetrics) graphql.Marshaler {
@ -7971,8 +7863,8 @@ func (ec *executionContext) _NodeMetrics(ctx context.Context, sel ast.SelectionS
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("NodeMetrics") out.Values[i] = graphql.MarshalString("NodeMetrics")
case "id": case "host":
out.Values[i] = ec._NodeMetrics_id(ctx, field, obj) out.Values[i] = ec._NodeMetrics_host(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
@ -9310,53 +9202,6 @@ func (ec *executionContext) marshalNMetricScope2githubᚗcomᚋClusterCockpitᚋ
return v return v
} }
func (ec *executionContext) marshalNNodeMetric2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetricᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NodeMetric) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
if !isLen1 {
wg.Add(len(v))
}
for i := range v {
i := i
fc := &graphql.FieldContext{
Index: &i,
Result: &v[i],
}
ctx := graphql.WithFieldContext(ctx, fc)
f := func(i int) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = nil
}
}()
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNNodeMetric2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetric(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNNodeMetric2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetric(ctx context.Context, sel ast.SelectionSet, v *model.NodeMetric) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._NodeMetric(ctx, sel, v)
}
func (ec *executionContext) marshalNNodeMetrics2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetricsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NodeMetrics) graphql.Marshaler { func (ec *executionContext) marshalNNodeMetrics2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐNodeMetricsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NodeMetrics) graphql.Marshaler {
ret := make(graphql.Array, len(v)) ret := make(graphql.Array, len(v))
var wg sync.WaitGroup var wg sync.WaitGroup

View File

@ -1,5 +1,7 @@
package model package model
import "strconv"
type Cluster struct { type Cluster struct {
Name string `json:"name"` Name string `json:"name"`
MetricConfig []*MetricConfig `json:"metricConfig"` MetricConfig []*MetricConfig `json:"metricConfig"`
@ -76,3 +78,15 @@ func (topo *Topology) GetCoresFromHWThreads(hwthreads []int) (cores []int, exclu
return cores, exclusive return cores, exclusive
} }
func (topo *Topology) GetAcceleratorIDs() ([]int, error) {
accels := make([]int, 0)
for _, accel := range topo.Accelerators {
id, err := strconv.Atoi(accel.ID)
if err != nil {
return nil, err
}
accels = append(accels, id)
}
return accels, nil
}

View File

@ -101,14 +101,9 @@ type MetricFootprints struct {
Footprints []schema.Float `json:"footprints"` Footprints []schema.Float `json:"footprints"`
} }
type NodeMetric struct {
Name string `json:"name"`
Data []schema.Float `json:"data"`
}
type NodeMetrics struct { type NodeMetrics struct {
ID string `json:"id"` Host string `json:"host"`
Metrics []*NodeMetric `json:"metrics"` Metrics []*JobMetricWithName `json:"metrics"`
} }
type OrderByInput struct { type OrderByInput struct {

View File

@ -121,19 +121,14 @@ type MetricFootprints {
enum Aggregate { USER, PROJECT, CLUSTER } enum Aggregate { USER, PROJECT, CLUSTER }
type NodeMetric {
name: String!
data: [NullableFloat!]!
}
type NodeMetrics { type NodeMetrics {
id: String! host: String!
metrics: [NodeMetric!]! metrics: [JobMetricWithName!]!
} }
type Query { type Query {
clusters: [Cluster!]! # List of all clusters clusters: [Cluster!]! # List of all clusters
tags: [Tag!]! # List of all tags tags: [Tag!]! # List of all tags
job(id: ID!): Job job(id: ID!): Job
jobMetrics(id: ID!, metrics: [String!], scopes: [MetricScope!]): [JobMetricWithName!]! jobMetrics(id: ID!, metrics: [String!], scopes: [MetricScope!]): [JobMetricWithName!]!
@ -144,7 +139,7 @@ type Query {
rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]! rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]!
nodeMetrics(cluster: ID!, nodes: [String!], metrics: [String!], from: Time!, to: Time!): [NodeMetrics!]! nodeMetrics(cluster: String!, partition: String!, nodes: [String!], scopes: [String!], metrics: [String!], from: Time!, to: Time!): [NodeMetrics!]!
} }
type Mutation { type Mutation {

View File

@ -207,34 +207,13 @@ func (r *queryResolver) RooflineHeatmap(ctx context.Context, filter []*model.Job
return r.rooflineHeatmap(ctx, filter, rows, cols, minX, minY, maxX, maxY) return r.rooflineHeatmap(ctx, filter, rows, cols, minX, minY, maxX, maxY)
} }
func (r *queryResolver) NodeMetrics(ctx context.Context, cluster string, nodes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) { func (r *queryResolver) NodeMetrics(ctx context.Context, cluster string, partition string, nodes []string, scopes []string, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) {
user := auth.GetUser(ctx) user := auth.GetUser(ctx)
if user != nil && !user.HasRole(auth.RoleAdmin) { if user != nil && !user.HasRole(auth.RoleAdmin) {
return nil, errors.New("you need to be an administrator for this query") return nil, errors.New("you need to be an administrator for this query")
} }
data, err := metricdata.LoadNodeData(cluster, metrics, nodes, from.Unix(), to.Unix(), ctx) return nil, nil
if err != nil {
return nil, err
}
res := make([]*model.NodeMetrics, 0, len(data))
for node, metrics := range data {
nodeMetrics := make([]*model.NodeMetric, 0, len(metrics))
for metric, data := range metrics {
nodeMetrics = append(nodeMetrics, &model.NodeMetric{
Name: metric,
Data: data,
})
}
res = append(res, &model.NodeMetrics{
ID: node,
Metrics: nodeMetrics,
})
}
return res, nil
} }
// Job returns generated.JobResolver implementation. // Job returns generated.JobResolver implementation.

View File

@ -449,11 +449,12 @@ func (ccms *CCMetricStore) LoadStats(job *schema.Job, metrics []string, ctx cont
return stats, nil return stats, nil
} }
func (ccms *CCMetricStore) LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error) { // TODO: Support sub-node-scope metrics! For this, the partition of a node needs to be known!
func (ccms *CCMetricStore) LoadNodeData(cluster, partition string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error) {
req := ApiQueryRequest{ req := ApiQueryRequest{
Cluster: clusterId, Cluster: cluster,
From: from, From: from.Unix(),
To: to, To: to.Unix(),
WithStats: false, WithStats: false,
WithData: true, WithData: true,
} }
@ -476,21 +477,34 @@ func (ccms *CCMetricStore) LoadNodeData(clusterId string, metrics, nodes []strin
return nil, err return nil, err
} }
data := make(map[string]map[string][]schema.Float) _ = resBody
data := make(map[string]map[string][]*schema.JobMetric)
for i, res := range resBody { for i, res := range resBody {
query := req.Queries[i] query := req.Queries[i]
metric := ccms.toLocalName(query.Metric)
qdata := res[0] qdata := res[0]
if qdata.Error != nil { if qdata.Error != nil {
return nil, fmt.Errorf("fetching %s for node %s failed: %s", query.Metric, query.Hostname, *qdata.Error) return nil, fmt.Errorf("fetching %s for node %s failed: %s", query.Metric, query.Hostname, *qdata.Error)
} }
nodedata, ok := data[query.Hostname] hostdata, ok := data[query.Hostname]
if !ok { if !ok {
nodedata = make(map[string][]schema.Float) hostdata = make(map[string][]*schema.JobMetric)
data[query.Hostname] = nodedata data[query.Hostname] = hostdata
} }
nodedata[ccms.toLocalName(query.Metric)] = qdata.Data mc := config.GetMetricConfig(cluster, metric)
hostdata[query.Metric] = append(hostdata[query.Metric], &schema.JobMetric{
Unit: mc.Unit,
Scope: schema.MetricScopeNode,
Timestep: mc.Timestep,
Series: []schema.Series{
{
Hostname: query.Hostname,
Data: qdata.Data,
},
},
})
} }
return data, nil return data, nil

View File

@ -21,8 +21,8 @@ type MetricDataRepository interface {
// Return a map of metrics to a map of nodes to the metric statistics of the job. node scope assumed for now. // Return a map of metrics to a map of nodes to the metric statistics of the job. node scope assumed for now.
LoadStats(job *schema.Job, metrics []string, ctx context.Context) (map[string]map[string]schema.MetricStatistics, error) LoadStats(job *schema.Job, metrics []string, ctx context.Context) (map[string]map[string]schema.MetricStatistics, error)
// Return a map of nodes to a map of metrics to the data for the requested time. // Return a map of hosts to a map of metrics at the requested scopes for that node.
LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error) LoadNodeData(cluster, partition string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error)
} }
var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{} var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{}
@ -152,26 +152,26 @@ func LoadAverages(job *schema.Job, metrics []string, data [][]schema.Float, ctx
return nil return nil
} }
// Used for the node/system view. Returns a map of nodes to a map of metrics (at node scope). // Used for the node/system view. Returns a map of nodes to a map of metrics.
func LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error) { func LoadNodeData(cluster, partition string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error) {
repo, ok := metricDataRepos[clusterId] repo, ok := metricDataRepos[cluster]
if !ok { if !ok {
return nil, fmt.Errorf("no metric data repository configured for '%s'", clusterId) return nil, fmt.Errorf("no metric data repository configured for '%s'", cluster)
} }
if metrics == nil { if metrics == nil {
for _, m := range config.GetClusterConfig(clusterId).MetricConfig { for _, m := range config.GetClusterConfig(cluster).MetricConfig {
metrics = append(metrics, m.Name) metrics = append(metrics, m.Name)
} }
} }
data, err := repo.LoadNodeData(clusterId, metrics, nodes, from, to, ctx) data, err := repo.LoadNodeData(cluster, partition, metrics, nodes, scopes, from, to, ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if data == nil { if data == nil {
return nil, fmt.Errorf("the metric data repository for '%s' does not support this query", clusterId) return nil, fmt.Errorf("the metric data repository for '%s' does not support this query", cluster)
} }
return data, nil return data, nil

View File

@ -2,6 +2,7 @@ package metricdata
import ( import (
"context" "context"
"time"
"github.com/ClusterCockpit/cc-backend/schema" "github.com/ClusterCockpit/cc-backend/schema"
) )
@ -31,6 +32,6 @@ func (tmdr *TestMetricDataRepository) LoadStats(job *schema.Job, metrics []strin
panic("TODO") panic("TODO")
} }
func (tmdr *TestMetricDataRepository) LoadNodeData(clusterId string, metrics, nodes []string, from, to int64, ctx context.Context) (map[string]map[string][]schema.Float, error) { func (tmdr *TestMetricDataRepository) LoadNodeData(cluster, partition string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error) {
panic("TODO") panic("TODO")
} }