Merge branch 'master' of github.com:ClusterCockpit/cc-backend

This commit is contained in:
Jan Eitzinger 2022-06-17 10:08:47 +02:00
commit 45359cca9d
10 changed files with 559 additions and 196 deletions

View File

@ -8,6 +8,29 @@ import (
"github.com/ClusterCockpit/cc-backend/log" "github.com/ClusterCockpit/cc-backend/log"
) )
type NodeList [][]interface {
consume(input string) (next string, ok bool)
}
func (nl *NodeList) Contains(name string) bool {
var ok bool
for _, term := range *nl {
str := name
for _, expr := range term {
str, ok = expr.consume(str)
if !ok {
break
}
}
if ok && str == "" {
return true
}
}
return false
}
type NLExprString string type NLExprString string
func (nle NLExprString) consume(input string) (next string, ok bool) { func (nle NLExprString) consume(input string) (next string, ok bool) {
@ -18,6 +41,17 @@ func (nle NLExprString) consume(input string) (next string, ok bool) {
return "", false return "", false
} }
type NLExprIntRanges []NLExprIntRange
func (nles NLExprIntRanges) consume(input string) (next string, ok bool) {
for _, nle := range nles {
if next, ok := nle.consume(input); ok {
return next, ok
}
}
return "", false
}
type NLExprIntRange struct { type NLExprIntRange struct {
start, end int64 start, end int64
zeroPadded bool zeroPadded bool
@ -51,36 +85,31 @@ func (nle NLExprIntRange) consume(input string) (next string, ok bool) {
return "", false return "", false
} }
type NodeList [][]interface {
consume(input string) (next string, ok bool)
}
func (nl *NodeList) Contains(name string) bool {
var ok bool
for _, term := range *nl {
str := name
for _, expr := range term {
str, ok = expr.consume(str)
if !ok {
break
}
}
if ok && str == "" {
return true
}
}
return false
}
func ParseNodeList(raw string) (NodeList, error) { func ParseNodeList(raw string) (NodeList, error) {
nl := NodeList{}
isLetter := func(r byte) bool { return ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') } isLetter := func(r byte) bool { return ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') }
isDigit := func(r byte) bool { return '0' <= r && r <= '9' } isDigit := func(r byte) bool { return '0' <= r && r <= '9' }
for _, rawterm := range strings.Split(raw, ",") { rawterms := []string{}
prevterm := 0
for i := 0; i < len(raw); i++ {
if raw[i] == '[' {
for i < len(raw) && raw[i] != ']' {
i++
}
if i == len(raw) {
return nil, fmt.Errorf("node list: unclosed '['")
}
} else if raw[i] == ',' {
rawterms = append(rawterms, raw[prevterm:i])
prevterm = i + 1
}
}
if prevterm != len(raw) {
rawterms = append(rawterms, raw[prevterm:])
}
nl := NodeList{}
for _, rawterm := range rawterms {
exprs := []interface { exprs := []interface {
consume(input string) (next string, ok bool) consume(input string) (next string, ok bool)
}{} }{}
@ -99,31 +128,37 @@ func ParseNodeList(raw string) (NodeList, error) {
return nil, fmt.Errorf("node list: unclosed '['") return nil, fmt.Errorf("node list: unclosed '['")
} }
minus := strings.Index(rawterm[i:i+end], "-") parts := strings.Split(rawterm[i+1:i+end], ",")
if minus == -1 { nles := NLExprIntRanges{}
return nil, fmt.Errorf("node list: no '-' found inside '[...]'") for _, part := range parts {
minus := strings.Index(part, "-")
if minus == -1 {
return nil, fmt.Errorf("node list: no '-' found inside '[...]'")
}
s1, s2 := part[0:minus], part[minus+1:]
if len(s1) != len(s2) || len(s1) == 0 {
return nil, fmt.Errorf("node list: %#v and %#v are not of equal length or of length zero", s1, s2)
}
x1, err := strconv.ParseInt(s1, 10, 32)
if err != nil {
return nil, fmt.Errorf("node list: %w", err)
}
x2, err := strconv.ParseInt(s2, 10, 32)
if err != nil {
return nil, fmt.Errorf("node list: %w", err)
}
nles = append(nles, NLExprIntRange{
start: x1,
end: x2,
digits: len(s1),
zeroPadded: true,
})
} }
s1, s2 := rawterm[i+1:i+minus], rawterm[i+minus+1:i+end] exprs = append(exprs, nles)
if len(s1) != len(s2) || len(s1) == 0 {
return nil, fmt.Errorf("node list: %#v and %#v are not of equal length or of length zero", s1, s2)
}
x1, err := strconv.ParseInt(s1, 10, 32)
if err != nil {
return nil, fmt.Errorf("node list: %w", err)
}
x2, err := strconv.ParseInt(s2, 10, 32)
if err != nil {
return nil, fmt.Errorf("node list: %w", err)
}
exprs = append(exprs, NLExprIntRange{
start: x1,
end: x2,
digits: len(s1),
zeroPadded: true,
})
i += end i += end
} else { } else {
return nil, fmt.Errorf("node list: invalid character: %#v", rune(c)) return nil, fmt.Errorf("node list: invalid character: %#v", rune(c))

View File

@ -10,11 +10,6 @@ func TestNodeList(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// fmt.Printf("terms\n")
// for i, term := range nl.terms {
// fmt.Printf("term %d: %#v\n", i, term)
// }
if nl.Contains("hello") || nl.Contains("woody") { if nl.Contains("hello") || nl.Contains("woody") {
t.Fail() t.Fail()
} }
@ -35,3 +30,26 @@ func TestNodeList(t *testing.T) {
t.Fail() t.Fail()
} }
} }
func TestNodeListCommasInBrackets(t *testing.T) {
nl, err := ParseNodeList("a[1000-2000,2010-2090,3000-5000]")
if err != nil {
t.Fatal(err)
}
if nl.Contains("hello") || nl.Contains("woody") {
t.Fatal("1")
}
if nl.Contains("a0") || nl.Contains("a0000") || nl.Contains("a5001") || nl.Contains("a2005") {
t.Fatal("2")
}
if !nl.Contains("a1001") || !nl.Contains("a2000") {
t.Fatal("3")
}
if !nl.Contains("a2042") || !nl.Contains("a4321") || !nl.Contains("a3000") {
t.Fatal("4")
}
}

@ -1 +1 @@
Subproject commit 4b0d020dd416e6b6b2a70b476b158804e62b3d7c Subproject commit 94ef11aa9fc3c194f1df497e3e06c60a7125883d

View File

@ -150,6 +150,7 @@ type ComplexityRoot struct {
Normal func(childComplexity int) int Normal func(childComplexity int) int
Peak func(childComplexity int) int Peak func(childComplexity int) int
Scope func(childComplexity int) int Scope func(childComplexity int) int
SubClusters func(childComplexity int) int
Timestep func(childComplexity int) int Timestep func(childComplexity int) int
Unit func(childComplexity int) int Unit func(childComplexity int) int
} }
@ -228,6 +229,14 @@ type ComplexityRoot struct {
Topology func(childComplexity int) int Topology func(childComplexity int) int
} }
SubClusterConfig struct {
Alert func(childComplexity int) int
Caution func(childComplexity int) int
Name func(childComplexity int) int
Normal func(childComplexity int) int
Peak func(childComplexity int) int
}
Tag struct { Tag struct {
ID func(childComplexity int) int ID func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
@ -763,6 +772,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.MetricConfig.Scope(childComplexity), true return e.complexity.MetricConfig.Scope(childComplexity), true
case "MetricConfig.subClusters":
if e.complexity.MetricConfig.SubClusters == nil {
break
}
return e.complexity.MetricConfig.SubClusters(childComplexity), true
case "MetricConfig.timestep": case "MetricConfig.timestep":
if e.complexity.MetricConfig.Timestep == nil { if e.complexity.MetricConfig.Timestep == nil {
break break
@ -1181,6 +1197,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.SubCluster.Topology(childComplexity), true return e.complexity.SubCluster.Topology(childComplexity), true
case "SubClusterConfig.alert":
if e.complexity.SubClusterConfig.Alert == nil {
break
}
return e.complexity.SubClusterConfig.Alert(childComplexity), true
case "SubClusterConfig.caution":
if e.complexity.SubClusterConfig.Caution == nil {
break
}
return e.complexity.SubClusterConfig.Caution(childComplexity), true
case "SubClusterConfig.name":
if e.complexity.SubClusterConfig.Name == nil {
break
}
return e.complexity.SubClusterConfig.Name(childComplexity), true
case "SubClusterConfig.normal":
if e.complexity.SubClusterConfig.Normal == nil {
break
}
return e.complexity.SubClusterConfig.Normal(childComplexity), true
case "SubClusterConfig.peak":
if e.complexity.SubClusterConfig.Peak == nil {
break
}
return e.complexity.SubClusterConfig.Peak(childComplexity), true
case "Tag.id": case "Tag.id":
if e.complexity.Tag.ID == nil { if e.complexity.Tag.ID == nil {
break break
@ -1413,16 +1464,25 @@ type Accelerator {
model: String! model: String!
} }
type SubClusterConfig {
name: String!
peak: Float!
normal: Float!
caution: Float!
alert: Float!
}
type MetricConfig { type MetricConfig {
name: String! name: String!
unit: String! unit: String!
scope: MetricScope! scope: MetricScope!
aggregation: String aggregation: String
timestep: Int! timestep: Int!
peak: Float! peak: Float
normal: Float! normal: Float
caution: Float! caution: Float
alert: Float! alert: Float
subClusters: [SubClusterConfig]
} }
type Tag { type Tag {
@ -4352,14 +4412,11 @@ func (ec *executionContext) _MetricConfig_peak(ctx context.Context, field graphq
return graphql.Null return graphql.Null
} }
if resTmp == nil { if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null return graphql.Null
} }
res := resTmp.(float64) res := resTmp.(*float64)
fc.Result = res fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res) return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
} }
func (ec *executionContext) _MetricConfig_normal(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) { func (ec *executionContext) _MetricConfig_normal(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) {
@ -4387,14 +4444,11 @@ func (ec *executionContext) _MetricConfig_normal(ctx context.Context, field grap
return graphql.Null return graphql.Null
} }
if resTmp == nil { if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null return graphql.Null
} }
res := resTmp.(float64) res := resTmp.(*float64)
fc.Result = res fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res) return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
} }
func (ec *executionContext) _MetricConfig_caution(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) { func (ec *executionContext) _MetricConfig_caution(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) {
@ -4422,14 +4476,11 @@ func (ec *executionContext) _MetricConfig_caution(ctx context.Context, field gra
return graphql.Null return graphql.Null
} }
if resTmp == nil { if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null return graphql.Null
} }
res := resTmp.(float64) res := resTmp.(*float64)
fc.Result = res fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res) return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
} }
func (ec *executionContext) _MetricConfig_alert(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) { func (ec *executionContext) _MetricConfig_alert(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) {
@ -4457,14 +4508,43 @@ func (ec *executionContext) _MetricConfig_alert(ctx context.Context, field graph
return graphql.Null return graphql.Null
} }
if resTmp == nil { if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null return graphql.Null
} }
res := resTmp.(float64) res := resTmp.(*float64)
fc.Result = res fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res) return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
}
func (ec *executionContext) _MetricConfig_subClusters(ctx context.Context, field graphql.CollectedField, obj *model.MetricConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MetricConfig",
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.SubClusters, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.([]*model.SubClusterConfig)
fc.Result = res
return ec.marshalOSubClusterConfig2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐSubClusterConfig(ctx, field.Selections, res)
} }
func (ec *executionContext) _MetricFootprints_metric(ctx context.Context, field graphql.CollectedField, obj *model.MetricFootprints) (ret graphql.Marshaler) { func (ec *executionContext) _MetricFootprints_metric(ctx context.Context, field graphql.CollectedField, obj *model.MetricFootprints) (ret graphql.Marshaler) {
@ -6261,6 +6341,181 @@ func (ec *executionContext) _SubCluster_topology(ctx context.Context, field grap
return ec.marshalNTopology2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐTopology(ctx, field.Selections, res) return ec.marshalNTopology2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐTopology(ctx, field.Selections, res)
} }
func (ec *executionContext) _SubClusterConfig_name(ctx context.Context, field graphql.CollectedField, obj *model.SubClusterConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "SubClusterConfig",
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) _SubClusterConfig_peak(ctx context.Context, field graphql.CollectedField, obj *model.SubClusterConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "SubClusterConfig",
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.Peak, 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.(float64)
fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res)
}
func (ec *executionContext) _SubClusterConfig_normal(ctx context.Context, field graphql.CollectedField, obj *model.SubClusterConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "SubClusterConfig",
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.Normal, 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.(float64)
fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res)
}
func (ec *executionContext) _SubClusterConfig_caution(ctx context.Context, field graphql.CollectedField, obj *model.SubClusterConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "SubClusterConfig",
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.Caution, 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.(float64)
fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res)
}
func (ec *executionContext) _SubClusterConfig_alert(ctx context.Context, field graphql.CollectedField, obj *model.SubClusterConfig) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "SubClusterConfig",
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.Alert, 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.(float64)
fc.Result = res
return ec.marshalNFloat2float64(ctx, field.Selections, res)
}
func (ec *executionContext) _Tag_id(ctx context.Context, field graphql.CollectedField, obj *schema.Tag) (ret graphql.Marshaler) { func (ec *executionContext) _Tag_id(ctx context.Context, field graphql.CollectedField, obj *schema.Tag) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -9217,9 +9472,6 @@ func (ec *executionContext) _MetricConfig(ctx context.Context, sel ast.Selection
out.Values[i] = innerFunc(ctx) out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "normal": case "normal":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) { innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._MetricConfig_normal(ctx, field, obj) return ec._MetricConfig_normal(ctx, field, obj)
@ -9227,9 +9479,6 @@ func (ec *executionContext) _MetricConfig(ctx context.Context, sel ast.Selection
out.Values[i] = innerFunc(ctx) out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "caution": case "caution":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) { innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._MetricConfig_caution(ctx, field, obj) return ec._MetricConfig_caution(ctx, field, obj)
@ -9237,9 +9486,6 @@ func (ec *executionContext) _MetricConfig(ctx context.Context, sel ast.Selection
out.Values[i] = innerFunc(ctx) out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "alert": case "alert":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) { innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._MetricConfig_alert(ctx, field, obj) return ec._MetricConfig_alert(ctx, field, obj)
@ -9247,9 +9493,13 @@ func (ec *executionContext) _MetricConfig(ctx context.Context, sel ast.Selection
out.Values[i] = innerFunc(ctx) out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null { case "subClusters":
invalids++ innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._MetricConfig_subClusters(ctx, field, obj)
} }
out.Values[i] = innerFunc(ctx)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -10081,6 +10331,77 @@ func (ec *executionContext) _SubCluster(ctx context.Context, sel ast.SelectionSe
return out return out
} }
var subClusterConfigImplementors = []string{"SubClusterConfig"}
func (ec *executionContext) _SubClusterConfig(ctx context.Context, sel ast.SelectionSet, obj *model.SubClusterConfig) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, subClusterConfigImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SubClusterConfig")
case "name":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._SubClusterConfig_name(ctx, field, obj)
}
out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "peak":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._SubClusterConfig_peak(ctx, field, obj)
}
out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "normal":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._SubClusterConfig_normal(ctx, field, obj)
}
out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "caution":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._SubClusterConfig_caution(ctx, field, obj)
}
out.Values[i] = innerFunc(ctx)
if out.Values[i] == graphql.Null {
invalids++
}
case "alert":
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
return ec._SubClusterConfig_alert(ctx, field, obj)
}
out.Values[i] = innerFunc(ctx)
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 tagImplementors = []string{"Tag"} var tagImplementors = []string{"Tag"}
func (ec *executionContext) _Tag(ctx context.Context, sel ast.SelectionSet, obj *schema.Tag) graphql.Marshaler { func (ec *executionContext) _Tag(ctx context.Context, sel ast.SelectionSet, obj *schema.Tag) graphql.Marshaler {
@ -12214,6 +12535,22 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
return res return res
} }
func (ec *executionContext) unmarshalOFloat2ᚖfloat64(ctx context.Context, v interface{}) (*float64, error) {
if v == nil {
return nil, nil
}
res, err := graphql.UnmarshalFloatContext(ctx, v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalOFloat2ᚖfloat64(ctx context.Context, sel ast.SelectionSet, v *float64) graphql.Marshaler {
if v == nil {
return graphql.Null
}
res := graphql.MarshalFloatContext(*v)
return graphql.WrapContextMarshaler(ctx, res)
}
func (ec *executionContext) unmarshalOFloatRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐFloatRange(ctx context.Context, v interface{}) (*model.FloatRange, error) { func (ec *executionContext) unmarshalOFloatRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐFloatRange(ctx context.Context, v interface{}) (*model.FloatRange, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil
@ -12627,6 +12964,54 @@ func (ec *executionContext) unmarshalOStringInput2ᚖgithubᚗcomᚋClusterCockp
return &res, graphql.ErrorOnPath(ctx, err) return &res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) marshalOSubClusterConfig2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐSubClusterConfig(ctx context.Context, sel ast.SelectionSet, v []*model.SubClusterConfig) 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.marshalOSubClusterConfig2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐSubClusterConfig(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalOSubClusterConfig2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋgraphᚋmodelᚐSubClusterConfig(ctx context.Context, sel ast.SelectionSet, v *model.SubClusterConfig) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._SubClusterConfig(ctx, sel, v)
}
func (ec *executionContext) unmarshalOTime2ᚖtimeᚐTime(ctx context.Context, v interface{}) (*time.Time, error) { func (ec *executionContext) unmarshalOTime2ᚖtimeᚐTime(ctx context.Context, v interface{}) (*time.Time, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil

View File

@ -97,15 +97,16 @@ type JobsStatistics struct {
} }
type MetricConfig struct { type MetricConfig struct {
Name string `json:"name"` Name string `json:"name"`
Unit string `json:"unit"` Unit string `json:"unit"`
Scope schema.MetricScope `json:"scope"` Scope schema.MetricScope `json:"scope"`
Aggregation *string `json:"aggregation"` Aggregation *string `json:"aggregation"`
Timestep int `json:"timestep"` Timestep int `json:"timestep"`
Peak float64 `json:"peak"` Peak *float64 `json:"peak"`
Normal float64 `json:"normal"` Normal *float64 `json:"normal"`
Caution float64 `json:"caution"` Caution *float64 `json:"caution"`
Alert float64 `json:"alert"` Alert *float64 `json:"alert"`
SubClusters []*SubClusterConfig `json:"subClusters"`
} }
type MetricFootprints struct { type MetricFootprints struct {
@ -150,6 +151,14 @@ type SubCluster struct {
Topology *Topology `json:"topology"` Topology *Topology `json:"topology"`
} }
type SubClusterConfig struct {
Name string `json:"name"`
Peak float64 `json:"peak"`
Normal float64 `json:"normal"`
Caution float64 `json:"caution"`
Alert float64 `json:"alert"`
}
type TimeRange struct { type TimeRange struct {
From *time.Time `json:"from"` From *time.Time `json:"from"`
To *time.Time `json:"to"` To *time.Time `json:"to"`

View File

@ -68,16 +68,25 @@ type Accelerator {
model: String! model: String!
} }
type SubClusterConfig {
name: String!
peak: Float!
normal: Float!
caution: Float!
alert: Float!
}
type MetricConfig { type MetricConfig {
name: String! name: String!
unit: String! unit: String!
scope: MetricScope! scope: MetricScope!
aggregation: String aggregation: String
timestep: Int! timestep: Int!
peak: Float! peak: Float
normal: Float! normal: Float
caution: Float! caution: Float
alert: Float! alert: Float
subClusters: [SubClusterConfig]
} }
type Tag { type Tag {

View File

@ -1 +0,0 @@
# Helper scripts

View File

@ -1,40 +0,0 @@
import fetch from 'node-fetch'
// Just for testing
const job = {
jobId: 123,
user: 'lou',
project: 'testproj',
cluster: 'heidi',
partition: 'default',
arrayJobId: 0,
numNodes: 1,
numHwthreads: 8,
numAcc: 0,
exclusive: 1,
monitoringStatus: 1,
smt: 1,
jobState: 'running',
duration: 2*60*60,
tags: [],
resources: [
{
hostname: 'heidi',
hwthreads: [0, 1, 2, 3, 4, 5, 6, 7]
}
],
metaData: null,
startTime: 1641427200
}
fetch('http://localhost:8080/api/jobs/start_job/', {
method: 'POST',
body: JSON.stringify(job),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc19hZG1pbiI6dHJ1ZSwiaXNfYXBpIjpmYWxzZSwic3ViIjoibG91In0.nY6dCgLSdm7zXz1xPkrb_3JnnUCgExXeXcrTlAAySs4p72VKJhmzzC1RxgkJE26l8tDYUilM-o-urzlaqK5aDA'
}
})
.then(res => res.status == 200 ? res.json() : res.text())
.then(res => console.log(res))

View File

@ -1,22 +0,0 @@
package main
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"fmt"
"os"
)
func main() {
// rand.Reader uses /dev/urandom on Linux
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
os.Exit(1)
}
fmt.Fprintf(os.Stdout, "JWT_PUBLIC_KEY=%#v\nJWT_PRIVATE_KEY=%#v\n",
base64.StdEncoding.EncodeToString(pub),
base64.StdEncoding.EncodeToString(priv))
}

View File

@ -1,30 +0,0 @@
# How to run this as a systemd deamon
The files in this directory assume that you install the Golang version of ClusterCockpit to `/var/clustercockpit`. If you do not like that, you can choose any other location, but make sure to replace all paths that begin with `/var/clustercockpit` in the `clustercockpit.service` file!
If you have not installed [yarn](https://yarnpkg.com/getting-started/install) and [go](https://go.dev/doc/install) already, do that (Golang is available in most package managers).
The `config.json` can have the optional fields *user* and *group*. If provided, the application will call [setuid](https://man7.org/linux/man-pages/man2/setuid.2.html) and [setgid](https://man7.org/linux/man-pages/man2/setgid.2.html) after having read the config file and having bound to a TCP port (so that it can take a privileged port), but before it starts accepting any connections. This is good for security, but means that the directories `frontend/public`, `var/` and `templates/` must be readable by that user and `var/` writable as well (All paths relative to the repos root). The `.env` and `config.json` files might contain secrets and should not be readable by that user. If those files are changed, the server has to be restarted.
```sh
# 1.: Clone this repository to /var/clustercockpit
git clone git@github.com:ClusterCockpit/cc-backend.git /var/clustercockpit
# 2.: Install all dependencies and build everything
cd /var/clustercockpit
go get && go build && (cd ./frontend && yarn install && yarn build)
# 3.: Modify the `./config.json` file from the directory which contains this README.md to your liking and put it in the repo root
cp ./utils/systemd/config.json ./config.json
vim ./config.json # do your thing...
# 4.: Add the systemd service unit file
sudo ln -s /var/clustercockpit/utils/systemd/clustercockpit.service /etc/systemd/system/clustercockpit.service
# 5.: Enable and start the server
sudo systemctl enable clustercockpit.service # optional (if done, (re-)starts automatically)
sudo systemctl start clustercockpit.service
# Check whats going on:
sudo journalctl -u clustercockpit.service
```