intermediate save

- DOES NOT COMPILE
This commit is contained in:
Christoph Kluge 2023-12-01 13:22:01 +01:00
parent c1b944b838
commit 9bc36152d9
9 changed files with 605 additions and 40 deletions

View File

@ -198,7 +198,7 @@ type Query {
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList! jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
jobsStatistics(filter: [JobFilter!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]! jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]!
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!]!]!
@ -286,6 +286,11 @@ type HistoPoint {
value: Int! value: Int!
} }
type MetricHistoPoints {
metric: String!
data: [HistoPoint!]
}
type JobsStatistics { type JobsStatistics {
id: ID! # If `groupBy` was used, ID of the user/project/cluster id: ID! # If `groupBy` was used, ID of the user/project/cluster
name: String! # if User-Statistics: Given Name of Account (ID) Owner name: String! # if User-Statistics: Given Name of Account (ID) Owner
@ -303,6 +308,7 @@ type JobsStatistics {
histNumNodes: [HistoPoint!]! # value: number of nodes, count: number of jobs with that number of nodes histNumNodes: [HistoPoint!]! # value: number of nodes, count: number of jobs with that number of nodes
histNumCores: [HistoPoint!]! # value: number of cores, count: number of jobs with that number of cores histNumCores: [HistoPoint!]! # value: number of cores, count: number of jobs with that number of cores
histNumAccs: [HistoPoint!]! # value: number of accs, count: number of jobs with that number of accs histNumAccs: [HistoPoint!]! # value: number of accs, count: number of jobs with that number of accs
histMetrics: [MetricHistoPoints!]! # metric: metricname, data array of histopoints: value: metric average bin, count: number of jobs with that metric average
} }
input PageRequest { input PageRequest {

View File

@ -25,6 +25,7 @@ import (
// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
return &executableSchema{ return &executableSchema{
schema: cfg.Schema,
resolvers: cfg.Resolvers, resolvers: cfg.Resolvers,
directives: cfg.Directives, directives: cfg.Directives,
complexity: cfg.Complexity, complexity: cfg.Complexity,
@ -32,6 +33,7 @@ func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
} }
type Config struct { type Config struct {
Schema *ast.Schema
Resolvers ResolverRoot Resolvers ResolverRoot
Directives DirectiveRoot Directives DirectiveRoot
Complexity ComplexityRoot Complexity ComplexityRoot
@ -141,6 +143,7 @@ type ComplexityRoot struct {
JobsStatistics struct { JobsStatistics struct {
HistDuration func(childComplexity int) int HistDuration func(childComplexity int) int
HistMetrics func(childComplexity int) int
HistNumAccs func(childComplexity int) int HistNumAccs func(childComplexity int) int
HistNumCores func(childComplexity int) int HistNumCores func(childComplexity int) int
HistNumNodes func(childComplexity int) int HistNumNodes func(childComplexity int) int
@ -176,6 +179,11 @@ type ComplexityRoot struct {
Metric func(childComplexity int) int Metric func(childComplexity int) int
} }
MetricHistoPoints struct {
Data func(childComplexity int) int
Metric func(childComplexity int) int
}
MetricStatistics struct { MetricStatistics struct {
Avg func(childComplexity int) int Avg func(childComplexity int) int
Max func(childComplexity int) int Max func(childComplexity int) int
@ -208,7 +216,7 @@ type ComplexityRoot struct {
JobMetrics func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope) int JobMetrics func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope) int
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, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) int JobsStatistics func(childComplexity int, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) int
NodeMetrics func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) int NodeMetrics func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, 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
@ -322,7 +330,7 @@ type QueryResolver interface {
JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope) ([]*model.JobMetricWithName, error) JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope) ([]*model.JobMetricWithName, error)
JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error)
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, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, 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, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) NodeMetrics(ctx context.Context, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error)
} }
@ -331,12 +339,16 @@ type SubClusterResolver interface {
} }
type executableSchema struct { type executableSchema struct {
schema *ast.Schema
resolvers ResolverRoot resolvers ResolverRoot
directives DirectiveRoot directives DirectiveRoot
complexity ComplexityRoot complexity ComplexityRoot
} }
func (e *executableSchema) Schema() *ast.Schema { func (e *executableSchema) Schema() *ast.Schema {
if e.schema != nil {
return e.schema
}
return parsedSchema return parsedSchema
} }
@ -730,6 +742,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.JobsStatistics.HistDuration(childComplexity), true return e.complexity.JobsStatistics.HistDuration(childComplexity), true
case "JobsStatistics.histMetrics":
if e.complexity.JobsStatistics.HistMetrics == nil {
break
}
return e.complexity.JobsStatistics.HistMetrics(childComplexity), true
case "JobsStatistics.histNumAccs": case "JobsStatistics.histNumAccs":
if e.complexity.JobsStatistics.HistNumAccs == nil { if e.complexity.JobsStatistics.HistNumAccs == nil {
break break
@ -919,6 +938,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.MetricFootprints.Metric(childComplexity), true return e.complexity.MetricFootprints.Metric(childComplexity), true
case "MetricHistoPoints.data":
if e.complexity.MetricHistoPoints.Data == nil {
break
}
return e.complexity.MetricHistoPoints.Data(childComplexity), true
case "MetricHistoPoints.metric":
if e.complexity.MetricHistoPoints.Metric == nil {
break
}
return e.complexity.MetricHistoPoints.Metric(childComplexity), true
case "MetricStatistics.avg": case "MetricStatistics.avg":
if e.complexity.MetricStatistics.Avg == nil { if e.complexity.MetricStatistics.Avg == nil {
break break
@ -1112,7 +1145,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate)), true return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["metrics"].([]string), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate)), true
case "Query.nodeMetrics": case "Query.nodeMetrics":
if e.complexity.Query.NodeMetrics == nil { if e.complexity.Query.NodeMetrics == nil {
@ -1587,14 +1620,14 @@ func (ec *executionContext) introspectSchema() (*introspection.Schema, error) {
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapSchema(parsedSchema), nil return introspection.WrapSchema(ec.Schema()), nil
} }
func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
} }
var sources = []*ast.Source{ var sources = []*ast.Source{
@ -1798,7 +1831,7 @@ type Query {
jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints
jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList! jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList!
jobsStatistics(filter: [JobFilter!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]! jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]!
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!]!]!
@ -1886,6 +1919,11 @@ type HistoPoint {
value: Int! value: Int!
} }
type MetricHistoPoints {
metric: String!
data: [HistoPoint!]
}
type JobsStatistics { type JobsStatistics {
id: ID! # If ` + "`" + `groupBy` + "`" + ` was used, ID of the user/project/cluster id: ID! # If ` + "`" + `groupBy` + "`" + ` was used, ID of the user/project/cluster
name: String! # if User-Statistics: Given Name of Account (ID) Owner name: String! # if User-Statistics: Given Name of Account (ID) Owner
@ -1903,6 +1941,7 @@ type JobsStatistics {
histNumNodes: [HistoPoint!]! # value: number of nodes, count: number of jobs with that number of nodes histNumNodes: [HistoPoint!]! # value: number of nodes, count: number of jobs with that number of nodes
histNumCores: [HistoPoint!]! # value: number of cores, count: number of jobs with that number of cores histNumCores: [HistoPoint!]! # value: number of cores, count: number of jobs with that number of cores
histNumAccs: [HistoPoint!]! # value: number of accs, count: number of jobs with that number of accs histNumAccs: [HistoPoint!]! # value: number of accs, count: number of jobs with that number of accs
histMetrics: [MetricHistoPoints!]! # value: metric average bin, count: number of jobs with that metric average
} }
input PageRequest { input PageRequest {
@ -2142,33 +2181,42 @@ func (ec *executionContext) field_Query_jobsStatistics_args(ctx context.Context,
} }
} }
args["filter"] = arg0 args["filter"] = arg0
var arg1 *model.PageRequest var arg1 []string
if tmp, ok := rawArgs["metrics"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics"))
arg1, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["metrics"] = arg1
var arg2 *model.PageRequest
if tmp, ok := rawArgs["page"]; ok { if tmp, ok := rawArgs["page"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page"))
arg1, err = ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) arg2, err = ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["page"] = arg1 args["page"] = arg2
var arg2 *model.SortByAggregate var arg3 *model.SortByAggregate
if tmp, ok := rawArgs["sortBy"]; ok { if tmp, ok := rawArgs["sortBy"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sortBy")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sortBy"))
arg2, err = ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate(ctx, tmp) arg3, err = ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["sortBy"] = arg2 args["sortBy"] = arg3
var arg3 *model.Aggregate var arg4 *model.Aggregate
if tmp, ok := rawArgs["groupBy"]; ok { if tmp, ok := rawArgs["groupBy"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("groupBy")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("groupBy"))
arg3, err = ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate(ctx, tmp) arg4, err = ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["groupBy"] = arg3 args["groupBy"] = arg4
return args, nil return args, nil
} }
@ -5640,6 +5688,56 @@ func (ec *executionContext) fieldContext_JobsStatistics_histNumAccs(ctx context.
return fc, nil return fc, nil
} }
func (ec *executionContext) _JobsStatistics_histMetrics(ctx context.Context, field graphql.CollectedField, obj *model.JobsStatistics) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_JobsStatistics_histMetrics(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.HistMetrics, 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.([]*model.MetricHistoPoints)
fc.Result = res
return ec.marshalNMetricHistoPoints2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐMetricHistoPointsᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_JobsStatistics_histMetrics(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "JobsStatistics",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "metric":
return ec.fieldContext_MetricHistoPoints_metric(ctx, field)
case "data":
return ec.fieldContext_MetricHistoPoints_data(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type MetricHistoPoints", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _MetricConfig_name(ctx context.Context, field graphql.CollectedField, obj *schema.MetricConfig) (ret graphql.Marshaler) { func (ec *executionContext) _MetricConfig_name(ctx context.Context, field graphql.CollectedField, obj *schema.MetricConfig) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_MetricConfig_name(ctx, field) fc, err := ec.fieldContext_MetricConfig_name(ctx, field)
if err != nil { if err != nil {
@ -6185,6 +6283,97 @@ func (ec *executionContext) fieldContext_MetricFootprints_data(ctx context.Conte
return fc, nil return fc, nil
} }
func (ec *executionContext) _MetricHistoPoints_metric(ctx context.Context, field graphql.CollectedField, obj *model.MetricHistoPoints) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_MetricHistoPoints_metric(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Metric, 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) fieldContext_MetricHistoPoints_metric(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MetricHistoPoints",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _MetricHistoPoints_data(ctx context.Context, field graphql.CollectedField, obj *model.MetricHistoPoints) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_MetricHistoPoints_data(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
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 {
return graphql.Null
}
res := resTmp.([]*model.HistoPoint)
fc.Result = res
return ec.marshalOHistoPoint2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐHistoPointᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_MetricHistoPoints_data(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MetricHistoPoints",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "count":
return ec.fieldContext_HistoPoint_count(ctx, field)
case "value":
return ec.fieldContext_HistoPoint_value(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type HistoPoint", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _MetricStatistics_avg(ctx context.Context, field graphql.CollectedField, obj *schema.MetricStatistics) (ret graphql.Marshaler) { func (ec *executionContext) _MetricStatistics_avg(ctx context.Context, field graphql.CollectedField, obj *schema.MetricStatistics) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_MetricStatistics_avg(ctx, field) fc, err := ec.fieldContext_MetricStatistics_avg(ctx, field)
if err != nil { if err != nil {
@ -7374,7 +7563,7 @@ func (ec *executionContext) _Query_jobsStatistics(ctx context.Context, field gra
}() }()
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().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate)) return ec.resolvers.Query().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["metrics"].([]string), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate))
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -7431,6 +7620,8 @@ func (ec *executionContext) fieldContext_Query_jobsStatistics(ctx context.Contex
return ec.fieldContext_JobsStatistics_histNumCores(ctx, field) return ec.fieldContext_JobsStatistics_histNumCores(ctx, field)
case "histNumAccs": case "histNumAccs":
return ec.fieldContext_JobsStatistics_histNumAccs(ctx, field) return ec.fieldContext_JobsStatistics_histNumAccs(ctx, field)
case "histMetrics":
return ec.fieldContext_JobsStatistics_histMetrics(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type JobsStatistics", field.Name) return nil, fmt.Errorf("no field named %q was found under type JobsStatistics", field.Name)
}, },
@ -12910,6 +13101,11 @@ func (ec *executionContext) _JobsStatistics(ctx context.Context, sel ast.Selecti
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ out.Invalids++
} }
case "histMetrics":
out.Values[i] = ec._JobsStatistics_histMetrics(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -13058,6 +13254,47 @@ func (ec *executionContext) _MetricFootprints(ctx context.Context, sel ast.Selec
return out return out
} }
var metricHistoPointsImplementors = []string{"MetricHistoPoints"}
func (ec *executionContext) _MetricHistoPoints(ctx context.Context, sel ast.SelectionSet, obj *model.MetricHistoPoints) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, metricHistoPointsImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("MetricHistoPoints")
case "metric":
out.Values[i] = ec._MetricHistoPoints_metric(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "data":
out.Values[i] = ec._MetricHistoPoints_data(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.processDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var metricStatisticsImplementors = []string{"MetricStatistics"} var metricStatisticsImplementors = []string{"MetricStatistics"}
func (ec *executionContext) _MetricStatistics(ctx context.Context, sel ast.SelectionSet, obj *schema.MetricStatistics) graphql.Marshaler { func (ec *executionContext) _MetricStatistics(ctx context.Context, sel ast.SelectionSet, obj *schema.MetricStatistics) graphql.Marshaler {
@ -15310,6 +15547,60 @@ func (ec *executionContext) marshalNMetricFootprints2ᚖgithubᚗcomᚋClusterCo
return ec._MetricFootprints(ctx, sel, v) return ec._MetricFootprints(ctx, sel, v)
} }
func (ec *executionContext) marshalNMetricHistoPoints2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐMetricHistoPointsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.MetricHistoPoints) 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.marshalNMetricHistoPoints2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐMetricHistoPoints(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) marshalNMetricHistoPoints2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐMetricHistoPoints(ctx context.Context, sel ast.SelectionSet, v *model.MetricHistoPoints) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._MetricHistoPoints(ctx, sel, v)
}
func (ec *executionContext) unmarshalNMetricScope2githubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScope(ctx context.Context, v interface{}) (schema.MetricScope, error) { func (ec *executionContext) unmarshalNMetricScope2githubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScope(ctx context.Context, v interface{}) (schema.MetricScope, error) {
var res schema.MetricScope var res schema.MetricScope
err := res.UnmarshalGQL(v) err := res.UnmarshalGQL(v)
@ -16117,6 +16408,53 @@ func (ec *executionContext) marshalOFootprints2ᚖgithubᚗcomᚋClusterCockpit
return ec._Footprints(ctx, sel, v) return ec._Footprints(ctx, sel, v)
} }
func (ec *executionContext) marshalOHistoPoint2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐHistoPointᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.HistoPoint) graphql.Marshaler {
if v == nil {
return graphql.Null
}
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.marshalNHistoPoint2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐHistoPoint(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) unmarshalOID2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { func (ec *executionContext) unmarshalOID2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil

View File

@ -85,22 +85,23 @@ type JobResultList struct {
} }
type JobsStatistics struct { type JobsStatistics struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
TotalJobs int `json:"totalJobs"` TotalJobs int `json:"totalJobs"`
RunningJobs int `json:"runningJobs"` RunningJobs int `json:"runningJobs"`
ShortJobs int `json:"shortJobs"` ShortJobs int `json:"shortJobs"`
TotalWalltime int `json:"totalWalltime"` TotalWalltime int `json:"totalWalltime"`
TotalNodes int `json:"totalNodes"` TotalNodes int `json:"totalNodes"`
TotalNodeHours int `json:"totalNodeHours"` TotalNodeHours int `json:"totalNodeHours"`
TotalCores int `json:"totalCores"` TotalCores int `json:"totalCores"`
TotalCoreHours int `json:"totalCoreHours"` TotalCoreHours int `json:"totalCoreHours"`
TotalAccs int `json:"totalAccs"` TotalAccs int `json:"totalAccs"`
TotalAccHours int `json:"totalAccHours"` TotalAccHours int `json:"totalAccHours"`
HistDuration []*HistoPoint `json:"histDuration"` HistDuration []*HistoPoint `json:"histDuration"`
HistNumNodes []*HistoPoint `json:"histNumNodes"` HistNumNodes []*HistoPoint `json:"histNumNodes"`
HistNumCores []*HistoPoint `json:"histNumCores"` HistNumCores []*HistoPoint `json:"histNumCores"`
HistNumAccs []*HistoPoint `json:"histNumAccs"` HistNumAccs []*HistoPoint `json:"histNumAccs"`
HistMetrics []*MetricHistoPoints `json:"histMetrics"`
} }
type MetricFootprints struct { type MetricFootprints struct {
@ -108,6 +109,11 @@ type MetricFootprints struct {
Data []schema.Float `json:"data"` Data []schema.Float `json:"data"`
} }
type MetricHistoPoints struct {
Metric string `json:"metric"`
Data []*HistoPoint `json:"data,omitempty"`
}
type NodeMetrics struct { type NodeMetrics struct {
Host string `json:"host"` Host string `json:"host"`
SubCluster string `json:"subCluster"` SubCluster string `json:"subCluster"`

View File

@ -2,7 +2,7 @@ package graph
// This file will be automatically regenerated based on the schema, any resolver implementations // This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end. // will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.36 // Code generated by github.com/99designs/gqlgen version v0.17.40
import ( import (
"context" "context"
@ -244,7 +244,7 @@ func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, pag
} }
// JobsStatistics is the resolver for the jobsStatistics field. // JobsStatistics is the resolver for the jobsStatistics field.
func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) { func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) {
var err error var err error
var stats []*model.JobsStatistics var stats []*model.JobsStatistics
@ -291,6 +291,17 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF
} }
} }
if requireField(ctx, "histMetrics") {
if groupBy == nil {
stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0])
if err != nil {
return nil, err
}
} else {
return nil, errors.New("metric histograms only implemented without groupBy argument")
}
}
return stats, nil return stats, nil
} }

View File

@ -12,7 +12,9 @@ import (
"github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/config"
"github.com/ClusterCockpit/cc-backend/internal/graph/model" "github.com/ClusterCockpit/cc-backend/internal/graph/model"
"github.com/ClusterCockpit/cc-backend/pkg/archive"
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
"github.com/ClusterCockpit/cc-backend/pkg/schema"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
) )
@ -450,6 +452,32 @@ func (r *JobRepository) AddHistograms(
return stat, nil return stat, nil
} }
// Requires thresholds for metric from config for cluster? Of all clusters and use largest? split to 10 + 1 for artifacts?
func (r *JobRepository) AddMetricHistograms(
ctx context.Context,
filter []*model.JobFilter,
metrics []string,
stat *model.JobsStatistics) (*model.JobsStatistics, error) {
start := time.Now()
for i, m := range metrics {
// DEBUG
fmt.Println(i, m)
var err error
var metricHisto *model.MetricHistoPoints
metricHisto, err = r.jobsMetricStatisticsHistogram(ctx, m, filter)
if err != nil {
log.Warnf("Error while loading job metric statistics histogram: %s", m)
continue
}
stat.HistMetrics = append(stat.HistMetrics, metricHisto)
}
log.Debugf("Timer AddMetricHistograms %s", time.Since(start))
return stat, nil
}
// `value` must be the column grouped by, but renamed to "value" // `value` must be the column grouped by, but renamed to "value"
func (r *JobRepository) jobsStatisticsHistogram( func (r *JobRepository) jobsStatisticsHistogram(
ctx context.Context, ctx context.Context,
@ -487,3 +515,71 @@ func (r *JobRepository) jobsStatisticsHistogram(
log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start)) log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start))
return points, nil return points, nil
} }
func (r *JobRepository) jobsMetricStatisticsHistogram(
ctx context.Context,
metric string,
filters []*model.JobFilter) (*model.MetricHistoPoints, error) {
// "job.load_avg as value"
// switch m {
// case "cpu_load":
// Get specific Peak or largest Peak
var metricConfig *schema.MetricConfig
var peak float64 = 0.0
for _, f := range filters {
if f.Cluster != nil {
metricConfig = archive.GetMetricConfig(*f.Cluster.Eq, metric)
peak = metricConfig.Peak
} else {
for _, c := range archive.Clusters {
for _, m := range c.MetricConfig {
if m.Name == metric {
if m.Peak > peak {
peak = m.Peak
}
}
}
}
}
}
// Make bins
start := time.Now()
query, qerr := SecurityCheck(ctx,
sq.Select(value, "COUNT(job.id) AS count").From("job"))
if qerr != nil {
return nil, qerr
}
for _, f := range filters {
if f.Cluster != nil {
metricConfig = archive.GetMetricConfig(*f.Cluster.Eq, metric)
peak = metricConfig.Peak
}
query = BuildWhereClause(f, query)
}
rows, err := query.GroupBy("value").RunWith(r.DB).Query()
if err != nil {
log.Error("Error while running query")
return nil, err
}
points := make([]*model.HistoPoint, 0)
for rows.Next() {
point := model.HistoPoint{}
if err := rows.Scan(&point.Value, &point.Count); err != nil {
log.Warn("Error while scanning rows")
return nil, err
}
points = append(points, &point)
}
log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start))
return points, nil
}

View File

@ -8,8 +8,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ClusterCockpit/cc-backend/pkg/schema"
"github.com/ClusterCockpit/cc-backend/pkg/log" "github.com/ClusterCockpit/cc-backend/pkg/log"
"github.com/ClusterCockpit/cc-backend/pkg/schema"
) )
var Clusters []*schema.Cluster var Clusters []*schema.Cluster

View File

@ -0,0 +1,73 @@
<script>
import { Modal, ModalBody, ModalHeader, ModalFooter,
Button, ListGroup, ListGroupItem, Icon } from 'sveltestrap'
import { gql, getContextClient , mutationStore } from '@urql/svelte'
export let cluster
export let availableMetrics
export let metricsInHistograms
const client = getContextClient();
const updateConfigurationMutation = ({ name, value }) => {
return mutationStore({
client: client,
query: gql`mutation($name: String!, $value: String!) {
updateConfiguration(name: $name, value: $value)
}`,
variables: { name, value }
})
}
let isHistogramConfigOpen = false
function updateConfiguration(data) {
updateConfigurationMutation({
name: data.name,
value: JSON.stringify(data.value)
}).subscribe(res => {
if (res.fetching === false && res.error) {
throw res.error
// console.log('Error on subscription: ' + res.error)
}
})
}
</script>
<Button outline
on:click={() => (isHistogramConfigOpen = true)}>
<Icon name=""/>
Select Histograms
</Button>
<Modal isOpen={isHistogramConfigOpen}
toggle={() => (isHistogramConfigOpen = !isHistogramConfigOpen)}>
<ModalHeader>
Select metrics presented in histograms
</ModalHeader>
<ModalBody>
<ListGroup>
<!-- <li class="list-group-item">
<input type="checkbox" bind:checked={pendingShowFootprint}> Show Footprint
</li>
<hr/> -->
{#each availableMetrics as metric (metric)}
<ListGroupItem>
<input type="checkbox" bind:group={metricsInHistograms}
value={metric}
on:change={() => updateConfiguration({
name: cluster ? `user_view_histogramMetrics:${cluster}` : 'user_view_histogramMetrics',
value: metricsInHistograms
})} />
{metric}
</ListGroupItem>
{/each}
</ListGroup>
</ModalBody>
<ModalFooter>
<Button color="primary"
on:click={() => (isHistogramConfigOpen = false)}>
Close
</Button>
</ModalFooter>
</Modal>

View File

@ -63,6 +63,8 @@
option.key == ccconfig.status_view_selectedTopUserCategory option.key == ccconfig.status_view_selectedTopUserCategory
); );
let metricsInHistograms = ccconfig[`status_view_histogramMetrics:${cluster}`] || ccconfig.status_view_histogramMetrics
const client = getContextClient(); const client = getContextClient();
$: mainQuery = queryStore({ $: mainQuery = queryStore({
client: client, client: client,

View File

@ -1,7 +1,7 @@
<script> <script>
import { onMount, getContext } from 'svelte' import { onMount, getContext } from 'svelte'
import { init, convert2uplot } from './utils.js' import { init, convert2uplot } from './utils.js'
import { Table, Row, Col, Button, Icon, Card, Spinner, Input } from 'sveltestrap' import { Table, Row, Col, Button, Icon, Card, Spinner } from 'sveltestrap'
import { queryStore, gql, getContextClient } from '@urql/svelte' import { queryStore, gql, getContextClient } from '@urql/svelte'
import Filters from './filters/Filters.svelte' import Filters from './filters/Filters.svelte'
import JobList from './joblist/JobList.svelte' import JobList from './joblist/JobList.svelte'
@ -9,11 +9,14 @@
import Refresher from './joblist/Refresher.svelte' import Refresher from './joblist/Refresher.svelte'
import Histogram from './plots/Histogram.svelte' import Histogram from './plots/Histogram.svelte'
import MetricSelection from './MetricSelection.svelte' import MetricSelection from './MetricSelection.svelte'
import HistogramSelection from './HistogramSelection.svelte'
import PlotTable from './PlotTable.svelte'
import { scramble, scrambleNames } from './joblist/JobInfo.svelte' import { scramble, scrambleNames } from './joblist/JobInfo.svelte'
const { query: initq } = init() const { query: initq } = init()
const ccconfig = getContext('cc-config') const ccconfig = getContext('cc-config')
// const metricConfig = getContext('metrics')
export let user export let user
export let filterPresets export let filterPresets
@ -25,21 +28,23 @@
let metrics = ccconfig.plot_list_selectedMetrics, isMetricsSelectionOpen = false let metrics = ccconfig.plot_list_selectedMetrics, isMetricsSelectionOpen = false
let w1, w2, histogramHeight = 250 let w1, w2, histogramHeight = 250
let selectedCluster = filterPresets?.cluster ? filterPresets.cluster : null let selectedCluster = filterPresets?.cluster ? filterPresets.cluster : null
let metricsInHistograms = ccconfig[`user_view_histogramMetrics:${cluster}`] || ccconfig.user_view_histogramMetrics
const client = getContextClient(); const client = getContextClient();
$: stats = queryStore({ $: stats = queryStore({
client: client, client: client,
query: gql` query: gql`
query($jobFilters: [JobFilter!]!) { query($jobFilters: [JobFilter!]!, $metricsInHistograms: [String!]!) {
jobsStatistics(filter: $jobFilters) { jobsStatistics(filter: $jobFilters, metrics: $metricsInHistograms) {
totalJobs totalJobs
shortJobs shortJobs
totalWalltime totalWalltime
totalCoreHours totalCoreHours
histDuration { count, value } histDuration { count, value }
histNumNodes { count, value } histNumNodes { count, value }
histMetrics { metric, data { count, value } }
}}`, }}`,
variables: { jobFilters } variables: { jobFilters, metricsInHistograms}
}) })
onMount(() => filterComponent.update()) onMount(() => filterComponent.update())
@ -68,6 +73,11 @@
on:click={() => (isMetricsSelectionOpen = true)}> on:click={() => (isMetricsSelectionOpen = true)}>
<Icon name="graph-up"/> Metrics <Icon name="graph-up"/> Metrics
</Button> </Button>
<HistogramSelection
bind:cluster={selectedCluster}
bind:availableMetrics={metrics}
bind:metricsInHistograms={metricsInHistograms}/>
</Col> </Col>
<Col xs="auto"> <Col xs="auto">
<Filters <Filters
@ -159,6 +169,29 @@
</div> </div>
{/if} {/if}
</Row> </Row>
<Row>
<Col>
<PlotTable
let:item
let:width
renderFor="analysis"
items={$stats.data.jobsStatistics[0].hostMetrics}>
{item}
<!-- <Histogram
data={convert2uplot(item.bins)}
width={width} height={250}
title="Average Distribution of '{item.metric}'"
xlabel={`${item.metric} bin maximum [${(metricConfig(selectedCluster, item.metric)?.unit?.prefix ? metricConfig(selectedCluster, item.metric)?.unit?.prefix : '') +
(metricConfig(selectedCluster, item.metric)?.unit?.base ? metricConfig(selectedCluster, item.metric)?.unit?.base : '')}]`}
xunit={`${(metricConfig(selectedCluster, item.metric)?.unit?.prefix ? metricConfig(selectedCluster, item.metric)?.unit?.prefix : '') +
(metricConfig(selectedCluster, item.metric)?.unit?.base ? metricConfig(selectedCluster, item.metric)?.unit?.base : '')}`}
ylabel="Normalized Hours"
yunit="Hours"/> -->
</PlotTable>
</Col>
</Row>
<br/> <br/>
<Row> <Row>
<Col> <Col>