mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-31 07:55:06 +01:00 
			
		
		
		
	Merge branch 'dev' into metricstore
This commit is contained in:
		| @@ -113,6 +113,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) { | ||||
|  | ||||
| 	for key, vals := range r.URL.Query() { | ||||
| 		switch key { | ||||
| 		// TODO: add project filter | ||||
| 		case "state": | ||||
| 			for _, s := range vals { | ||||
| 				state := schema.JobState(s) | ||||
| @@ -125,7 +126,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) { | ||||
| 			} | ||||
| 		case "cluster": | ||||
| 			filter.Cluster = &model.StringInput{Eq: &vals[0]} | ||||
| 		case "start-time": | ||||
| 		case "start-time": // ?startTime=1753707480-1754053139 | ||||
| 			st := strings.Split(vals[0], "-") | ||||
| 			if len(st) != 2 { | ||||
| 				handleError(fmt.Errorf("invalid query parameter value: startTime"), | ||||
|   | ||||
| @@ -402,7 +402,7 @@ func (auth *Authentication) AuthUserApi( | ||||
| 					return | ||||
| 				} | ||||
| 			case len(user.Roles) >= 2: | ||||
| 				if user.HasRole(schema.RoleApi) && user.HasAnyRole([]schema.Role{schema.RoleUser, schema.RoleManager, schema.RoleAdmin}) { | ||||
| 				if user.HasRole(schema.RoleApi) && user.HasAnyRole([]schema.Role{schema.RoleUser, schema.RoleManager, schema.RoleSupport, schema.RoleAdmin}) { | ||||
| 					ctx := context.WithValue(r.Context(), repository.ContextUserKey, user) | ||||
| 					onsuccess.ServeHTTP(rw, r.WithContext(ctx)) | ||||
| 					return | ||||
| @@ -530,6 +530,7 @@ func securedCheck(user *schema.User, r *http.Request) error { | ||||
| 		IPAddress = r.RemoteAddr | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: IPV6 not handled | ||||
| 	if strings.Contains(IPAddress, ":") { | ||||
| 		IPAddress = strings.Split(IPAddress, ":")[0] | ||||
| 	} | ||||
|   | ||||
| @@ -202,6 +202,7 @@ type ComplexityRoot struct { | ||||
| 		TotalJobs      func(childComplexity int) int | ||||
| 		TotalNodeHours func(childComplexity int) int | ||||
| 		TotalNodes     func(childComplexity int) int | ||||
| 		TotalUsers     func(childComplexity int) int | ||||
| 		TotalWalltime  func(childComplexity int) int | ||||
| 	} | ||||
|  | ||||
| @@ -277,6 +278,7 @@ type ComplexityRoot struct { | ||||
| 		ID          func(childComplexity int) int | ||||
| 		MetaData    func(childComplexity int) int | ||||
| 		NodeState   func(childComplexity int) int | ||||
| 		RunningJobs func(childComplexity int) int | ||||
| 		SubCluster  func(childComplexity int) int | ||||
| 	} | ||||
|  | ||||
| @@ -291,7 +293,7 @@ type ComplexityRoot struct { | ||||
| 		Items func(childComplexity int) int | ||||
| 	} | ||||
|  | ||||
| 	NodeStats struct { | ||||
| 	NodeStates struct { | ||||
| 		Count func(childComplexity int) int | ||||
| 		State func(childComplexity int) int | ||||
| 	} | ||||
| @@ -319,7 +321,7 @@ type ComplexityRoot struct { | ||||
| 		Node            func(childComplexity int, id string) int | ||||
| 		NodeMetrics     func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) int | ||||
| 		NodeMetricsList func(childComplexity int, cluster string, subCluster string, nodeFilter string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time, page *model.PageRequest, resolution *int) int | ||||
| 		NodeStats       func(childComplexity int, filter []*model.NodeFilter) int | ||||
| 		NodeStates      func(childComplexity int, filter []*model.NodeFilter) int | ||||
| 		Nodes           func(childComplexity int, filter []*model.NodeFilter, order *model.OrderByInput) int | ||||
| 		RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int | ||||
| 		ScopedJobStats  func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope) int | ||||
| @@ -445,6 +447,7 @@ type MutationResolver interface { | ||||
| 	UpdateConfiguration(ctx context.Context, name string, value string) (*string, error) | ||||
| } | ||||
| type NodeResolver interface { | ||||
| 	RunningJobs(ctx context.Context, obj *schema.Node) (int, error) | ||||
| 	NodeState(ctx context.Context, obj *schema.Node) (string, error) | ||||
| 	HealthState(ctx context.Context, obj *schema.Node) (schema.NodeState, error) | ||||
| 	MetaData(ctx context.Context, obj *schema.Node) (any, error) | ||||
| @@ -457,7 +460,7 @@ type QueryResolver interface { | ||||
| 	AllocatedNodes(ctx context.Context, cluster string) ([]*model.Count, error) | ||||
| 	Node(ctx context.Context, id string) (*schema.Node, error) | ||||
| 	Nodes(ctx context.Context, filter []*model.NodeFilter, order *model.OrderByInput) (*model.NodeStateResultList, error) | ||||
| 	NodeStats(ctx context.Context, filter []*model.NodeFilter) ([]*model.NodeStats, error) | ||||
| 	NodeStates(ctx context.Context, filter []*model.NodeFilter) ([]*model.NodeStates, error) | ||||
| 	Job(ctx context.Context, id string) (*schema.Job, error) | ||||
| 	JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope, resolution *int) ([]*model.JobMetricWithName, error) | ||||
| 	JobStats(ctx context.Context, id string, metrics []string) ([]*model.NamedStats, error) | ||||
| @@ -1165,6 +1168,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin | ||||
|  | ||||
| 		return e.complexity.JobsStatistics.TotalNodes(childComplexity), true | ||||
|  | ||||
| 	case "JobsStatistics.totalUsers": | ||||
| 		if e.complexity.JobsStatistics.TotalUsers == nil { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		return e.complexity.JobsStatistics.TotalUsers(childComplexity), true | ||||
|  | ||||
| 	case "JobsStatistics.totalWalltime": | ||||
| 		if e.complexity.JobsStatistics.TotalWalltime == nil { | ||||
| 			break | ||||
| @@ -1475,7 +1485,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin | ||||
|  | ||||
| 		return e.complexity.Node.Cluster(childComplexity), true | ||||
|  | ||||
| 	case "Node.HealthState": | ||||
| 	case "Node.healthState": | ||||
| 		if e.complexity.Node.HealthState == nil { | ||||
| 			break | ||||
| 		} | ||||
| @@ -1510,6 +1520,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin | ||||
|  | ||||
| 		return e.complexity.Node.NodeState(childComplexity), true | ||||
|  | ||||
| 	case "Node.runningJobs": | ||||
| 		if e.complexity.Node.RunningJobs == nil { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		return e.complexity.Node.RunningJobs(childComplexity), true | ||||
|  | ||||
| 	case "Node.subCluster": | ||||
| 		if e.complexity.Node.SubCluster == nil { | ||||
| 			break | ||||
| @@ -1552,19 +1569,19 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin | ||||
|  | ||||
| 		return e.complexity.NodeStateResultList.Items(childComplexity), true | ||||
|  | ||||
| 	case "NodeStats.count": | ||||
| 		if e.complexity.NodeStats.Count == nil { | ||||
| 	case "NodeStates.count": | ||||
| 		if e.complexity.NodeStates.Count == nil { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		return e.complexity.NodeStats.Count(childComplexity), true | ||||
| 		return e.complexity.NodeStates.Count(childComplexity), true | ||||
|  | ||||
| 	case "NodeStats.state": | ||||
| 		if e.complexity.NodeStats.State == nil { | ||||
| 	case "NodeStates.state": | ||||
| 		if e.complexity.NodeStates.State == nil { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		return e.complexity.NodeStats.State(childComplexity), true | ||||
| 		return e.complexity.NodeStates.State(childComplexity), true | ||||
|  | ||||
| 	case "NodesResultList.count": | ||||
| 		if e.complexity.NodesResultList.Count == nil { | ||||
| @@ -1754,17 +1771,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin | ||||
|  | ||||
| 		return e.complexity.Query.NodeMetricsList(childComplexity, args["cluster"].(string), args["subCluster"].(string), args["nodeFilter"].(string), args["scopes"].([]schema.MetricScope), args["metrics"].([]string), args["from"].(time.Time), args["to"].(time.Time), args["page"].(*model.PageRequest), args["resolution"].(*int)), true | ||||
|  | ||||
| 	case "Query.nodeStats": | ||||
| 		if e.complexity.Query.NodeStats == nil { | ||||
| 	case "Query.nodeStates": | ||||
| 		if e.complexity.Query.NodeStates == nil { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		args, err := ec.field_Query_nodeStats_args(ctx, rawArgs) | ||||
| 		args, err := ec.field_Query_nodeStates_args(ctx, rawArgs) | ||||
| 		if err != nil { | ||||
| 			return 0, false | ||||
| 		} | ||||
|  | ||||
| 		return e.complexity.Query.NodeStats(childComplexity, args["filter"].([]*model.NodeFilter)), true | ||||
| 		return e.complexity.Query.NodeStates(childComplexity, args["filter"].([]*model.NodeFilter)), true | ||||
|  | ||||
| 	case "Query.nodes": | ||||
| 		if e.complexity.Query.Nodes == nil { | ||||
| @@ -2334,12 +2351,13 @@ type Node { | ||||
|   hostname: String! | ||||
|   cluster: String! | ||||
|   subCluster: String! | ||||
|   runningJobs: Int! | ||||
|   nodeState: NodeState! | ||||
|   HealthState: MonitoringState! | ||||
|   healthState: MonitoringState! | ||||
|   metaData: Any | ||||
| } | ||||
|  | ||||
| type NodeStats { | ||||
| type NodeStates { | ||||
|   state: String! | ||||
|   count: Int! | ||||
| } | ||||
| @@ -2558,10 +2576,12 @@ enum Aggregate { | ||||
|   USER | ||||
|   PROJECT | ||||
|   CLUSTER | ||||
|   SUBCLUSTER | ||||
| } | ||||
| enum SortByAggregate { | ||||
|   TOTALWALLTIME | ||||
|   TOTALJOBS | ||||
|   TOTALUSERS | ||||
|   TOTALNODES | ||||
|   TOTALNODEHOURS | ||||
|   TOTALCORES | ||||
| @@ -2622,9 +2642,10 @@ type Query { | ||||
|   user(username: String!): User | ||||
|   allocatedNodes(cluster: String!): [Count!]! | ||||
|  | ||||
|   ## Node Queries New | ||||
|   node(id: ID!): Node | ||||
|   nodes(filter: [NodeFilter!], order: OrderByInput): NodeStateResultList! | ||||
|   nodeStats(filter: [NodeFilter!]): [NodeStats!]! | ||||
|   nodeStates(filter: [NodeFilter!]): [NodeStates!]! | ||||
|  | ||||
|   job(id: ID!): Job | ||||
|   jobMetrics( | ||||
| @@ -2679,6 +2700,7 @@ type Query { | ||||
|     from: Time! | ||||
|     to: Time! | ||||
|   ): [NodeMetrics!]! | ||||
|    | ||||
|   nodeMetricsList( | ||||
|     cluster: String! | ||||
|     subCluster: String! | ||||
| @@ -2715,6 +2737,7 @@ type TimeRangeOutput { | ||||
| input NodeFilter { | ||||
|   hostname: StringInput | ||||
|   cluster: StringInput | ||||
|   subcluster: StringInput | ||||
|   nodeState: NodeState | ||||
|   healthState: MonitoringState | ||||
| } | ||||
| @@ -2819,11 +2842,12 @@ type MetricHistoPoint { | ||||
| } | ||||
|  | ||||
| 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/subcluster | ||||
|   name: String! # if User-Statistics: Given Name of Account (ID) Owner | ||||
|   totalUsers: Int! # if *not* User-Statistics: Number of active users (based on running jobs) | ||||
|   totalJobs: Int! # Number of jobs | ||||
|   runningJobs: Int! # Number of running jobs | ||||
|   shortJobs: Int! # Number of jobs with a duration of less than duration | ||||
|   shortJobs: Int! # Number of jobs with a duration of less than config'd ShortRunningJobsDuration | ||||
|   totalWalltime: Int! # Sum of the duration of all matched jobs in hours | ||||
|   totalNodes: Int! # Sum of the nodes of all matched jobs | ||||
|   totalNodeHours: Int! # Sum of the node hours of all matched jobs | ||||
| @@ -3197,7 +3221,7 @@ func (ec *executionContext) field_Query_nodeMetrics_args(ctx context.Context, ra | ||||
| 	return args, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) field_Query_nodeStats_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { | ||||
| func (ec *executionContext) field_Query_nodeStates_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { | ||||
| 	var err error | ||||
| 	args := map[string]any{} | ||||
| 	arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeFilterᚄ) | ||||
| @@ -7125,6 +7149,50 @@ func (ec *executionContext) fieldContext_JobsStatistics_name(_ context.Context, | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _JobsStatistics_totalUsers(ctx context.Context, field graphql.CollectedField, obj *model.JobsStatistics) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_JobsStatistics_totalUsers(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) (any, error) { | ||||
| 		ctx = rctx // use context from middleware stack in children | ||||
| 		return obj.TotalUsers, 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.(int) | ||||
| 	fc.Result = res | ||||
| 	return ec.marshalNInt2int(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_JobsStatistics_totalUsers(_ 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) { | ||||
| 			return nil, errors.New("field of type Int does not have child fields") | ||||
| 		}, | ||||
| 	} | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _JobsStatistics_totalJobs(ctx context.Context, field graphql.CollectedField, obj *model.JobsStatistics) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_JobsStatistics_totalJobs(ctx, field) | ||||
| 	if err != nil { | ||||
| @@ -9788,6 +9856,50 @@ func (ec *executionContext) fieldContext_Node_subCluster(_ context.Context, fiel | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _Node_runningJobs(ctx context.Context, field graphql.CollectedField, obj *schema.Node) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Node_runningJobs(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) (any, error) { | ||||
| 		ctx = rctx // use context from middleware stack in children | ||||
| 		return ec.resolvers.Node().RunningJobs(rctx, obj) | ||||
| 	}) | ||||
| 	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.(int) | ||||
| 	fc.Result = res | ||||
| 	return ec.marshalNInt2int(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_Node_runningJobs(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| 	fc = &graphql.FieldContext{ | ||||
| 		Object:     "Node", | ||||
| 		Field:      field, | ||||
| 		IsMethod:   true, | ||||
| 		IsResolver: true, | ||||
| 		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { | ||||
| 			return nil, errors.New("field of type Int does not have child fields") | ||||
| 		}, | ||||
| 	} | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _Node_nodeState(ctx context.Context, field graphql.CollectedField, obj *schema.Node) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Node_nodeState(ctx, field) | ||||
| 	if err != nil { | ||||
| @@ -9832,8 +9944,8 @@ func (ec *executionContext) fieldContext_Node_nodeState(_ context.Context, field | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _Node_HealthState(ctx context.Context, field graphql.CollectedField, obj *schema.Node) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Node_HealthState(ctx, field) | ||||
| func (ec *executionContext) _Node_healthState(ctx context.Context, field graphql.CollectedField, obj *schema.Node) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Node_healthState(ctx, field) | ||||
| 	if err != nil { | ||||
| 		return graphql.Null | ||||
| 	} | ||||
| @@ -9863,7 +9975,7 @@ func (ec *executionContext) _Node_HealthState(ctx context.Context, field graphql | ||||
| 	return ec.marshalNMonitoringState2githubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐNodeState(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_Node_HealthState(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| func (ec *executionContext) fieldContext_Node_healthState(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| 	fc = &graphql.FieldContext{ | ||||
| 		Object:     "Node", | ||||
| 		Field:      field, | ||||
| @@ -10104,10 +10216,12 @@ func (ec *executionContext) fieldContext_NodeStateResultList_items(_ context.Con | ||||
| 				return ec.fieldContext_Node_cluster(ctx, field) | ||||
| 			case "subCluster": | ||||
| 				return ec.fieldContext_Node_subCluster(ctx, field) | ||||
| 			case "runningJobs": | ||||
| 				return ec.fieldContext_Node_runningJobs(ctx, field) | ||||
| 			case "nodeState": | ||||
| 				return ec.fieldContext_Node_nodeState(ctx, field) | ||||
| 			case "HealthState": | ||||
| 				return ec.fieldContext_Node_HealthState(ctx, field) | ||||
| 			case "healthState": | ||||
| 				return ec.fieldContext_Node_healthState(ctx, field) | ||||
| 			case "metaData": | ||||
| 				return ec.fieldContext_Node_metaData(ctx, field) | ||||
| 			} | ||||
| @@ -10158,8 +10272,8 @@ func (ec *executionContext) fieldContext_NodeStateResultList_count(_ context.Con | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _NodeStats_state(ctx context.Context, field graphql.CollectedField, obj *model.NodeStats) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_NodeStats_state(ctx, field) | ||||
| func (ec *executionContext) _NodeStates_state(ctx context.Context, field graphql.CollectedField, obj *model.NodeStates) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_NodeStates_state(ctx, field) | ||||
| 	if err != nil { | ||||
| 		return graphql.Null | ||||
| 	} | ||||
| @@ -10189,9 +10303,9 @@ func (ec *executionContext) _NodeStats_state(ctx context.Context, field graphql. | ||||
| 	return ec.marshalNString2string(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_NodeStats_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| func (ec *executionContext) fieldContext_NodeStates_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| 	fc = &graphql.FieldContext{ | ||||
| 		Object:     "NodeStats", | ||||
| 		Object:     "NodeStates", | ||||
| 		Field:      field, | ||||
| 		IsMethod:   false, | ||||
| 		IsResolver: false, | ||||
| @@ -10202,8 +10316,8 @@ func (ec *executionContext) fieldContext_NodeStats_state(_ context.Context, fiel | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _NodeStats_count(ctx context.Context, field graphql.CollectedField, obj *model.NodeStats) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_NodeStats_count(ctx, field) | ||||
| func (ec *executionContext) _NodeStates_count(ctx context.Context, field graphql.CollectedField, obj *model.NodeStates) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_NodeStates_count(ctx, field) | ||||
| 	if err != nil { | ||||
| 		return graphql.Null | ||||
| 	} | ||||
| @@ -10233,9 +10347,9 @@ func (ec *executionContext) _NodeStats_count(ctx context.Context, field graphql. | ||||
| 	return ec.marshalNInt2int(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_NodeStats_count(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| func (ec *executionContext) fieldContext_NodeStates_count(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| 	fc = &graphql.FieldContext{ | ||||
| 		Object:     "NodeStats", | ||||
| 		Object:     "NodeStates", | ||||
| 		Field:      field, | ||||
| 		IsMethod:   false, | ||||
| 		IsResolver: false, | ||||
| @@ -10830,10 +10944,12 @@ func (ec *executionContext) fieldContext_Query_node(ctx context.Context, field g | ||||
| 				return ec.fieldContext_Node_cluster(ctx, field) | ||||
| 			case "subCluster": | ||||
| 				return ec.fieldContext_Node_subCluster(ctx, field) | ||||
| 			case "runningJobs": | ||||
| 				return ec.fieldContext_Node_runningJobs(ctx, field) | ||||
| 			case "nodeState": | ||||
| 				return ec.fieldContext_Node_nodeState(ctx, field) | ||||
| 			case "HealthState": | ||||
| 				return ec.fieldContext_Node_HealthState(ctx, field) | ||||
| 			case "healthState": | ||||
| 				return ec.fieldContext_Node_healthState(ctx, field) | ||||
| 			case "metaData": | ||||
| 				return ec.fieldContext_Node_metaData(ctx, field) | ||||
| 			} | ||||
| @@ -10915,8 +11031,8 @@ func (ec *executionContext) fieldContext_Query_nodes(ctx context.Context, field | ||||
| 	return fc, nil | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) _Query_nodeStats(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Query_nodeStats(ctx, field) | ||||
| func (ec *executionContext) _Query_nodeStates(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { | ||||
| 	fc, err := ec.fieldContext_Query_nodeStates(ctx, field) | ||||
| 	if err != nil { | ||||
| 		return graphql.Null | ||||
| 	} | ||||
| @@ -10929,7 +11045,7 @@ func (ec *executionContext) _Query_nodeStats(ctx context.Context, field graphql. | ||||
| 	}() | ||||
| 	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { | ||||
| 		ctx = rctx // use context from middleware stack in children | ||||
| 		return ec.resolvers.Query().NodeStats(rctx, fc.Args["filter"].([]*model.NodeFilter)) | ||||
| 		return ec.resolvers.Query().NodeStates(rctx, fc.Args["filter"].([]*model.NodeFilter)) | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ec.Error(ctx, err) | ||||
| @@ -10941,12 +11057,12 @@ func (ec *executionContext) _Query_nodeStats(ctx context.Context, field graphql. | ||||
| 		} | ||||
| 		return graphql.Null | ||||
| 	} | ||||
| 	res := resTmp.([]*model.NodeStats) | ||||
| 	res := resTmp.([]*model.NodeStates) | ||||
| 	fc.Result = res | ||||
| 	return ec.marshalNNodeStats2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStatsᚄ(ctx, field.Selections, res) | ||||
| 	return ec.marshalNNodeStates2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStatesᚄ(ctx, field.Selections, res) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) fieldContext_Query_nodeStats(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| func (ec *executionContext) fieldContext_Query_nodeStates(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { | ||||
| 	fc = &graphql.FieldContext{ | ||||
| 		Object:     "Query", | ||||
| 		Field:      field, | ||||
| @@ -10955,11 +11071,11 @@ func (ec *executionContext) fieldContext_Query_nodeStats(ctx context.Context, fi | ||||
| 		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { | ||||
| 			switch field.Name { | ||||
| 			case "state": | ||||
| 				return ec.fieldContext_NodeStats_state(ctx, field) | ||||
| 				return ec.fieldContext_NodeStates_state(ctx, field) | ||||
| 			case "count": | ||||
| 				return ec.fieldContext_NodeStats_count(ctx, field) | ||||
| 				return ec.fieldContext_NodeStates_count(ctx, field) | ||||
| 			} | ||||
| 			return nil, fmt.Errorf("no field named %q was found under type NodeStats", field.Name) | ||||
| 			return nil, fmt.Errorf("no field named %q was found under type NodeStates", field.Name) | ||||
| 		}, | ||||
| 	} | ||||
| 	defer func() { | ||||
| @@ -10969,7 +11085,7 @@ func (ec *executionContext) fieldContext_Query_nodeStats(ctx context.Context, fi | ||||
| 		} | ||||
| 	}() | ||||
| 	ctx = graphql.WithFieldContext(ctx, fc) | ||||
| 	if fc.Args, err = ec.field_Query_nodeStats_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { | ||||
| 	if fc.Args, err = ec.field_Query_nodeStates_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { | ||||
| 		ec.Error(ctx, err) | ||||
| 		return fc, err | ||||
| 	} | ||||
| @@ -11379,6 +11495,8 @@ func (ec *executionContext) fieldContext_Query_jobsStatistics(ctx context.Contex | ||||
| 				return ec.fieldContext_JobsStatistics_id(ctx, field) | ||||
| 			case "name": | ||||
| 				return ec.fieldContext_JobsStatistics_name(ctx, field) | ||||
| 			case "totalUsers": | ||||
| 				return ec.fieldContext_JobsStatistics_totalUsers(ctx, field) | ||||
| 			case "totalJobs": | ||||
| 				return ec.fieldContext_JobsStatistics_totalJobs(ctx, field) | ||||
| 			case "runningJobs": | ||||
| @@ -16549,7 +16667,7 @@ func (ec *executionContext) unmarshalInputNodeFilter(ctx context.Context, obj an | ||||
| 		asMap[k] = v | ||||
| 	} | ||||
|  | ||||
| 	fieldsInOrder := [...]string{"hostname", "cluster", "nodeState", "healthState"} | ||||
| 	fieldsInOrder := [...]string{"hostname", "cluster", "subcluster", "nodeState", "healthState"} | ||||
| 	for _, k := range fieldsInOrder { | ||||
| 		v, ok := asMap[k] | ||||
| 		if !ok { | ||||
| @@ -16570,6 +16688,13 @@ func (ec *executionContext) unmarshalInputNodeFilter(ctx context.Context, obj an | ||||
| 				return it, err | ||||
| 			} | ||||
| 			it.Cluster = data | ||||
| 		case "subcluster": | ||||
| 			ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("subcluster")) | ||||
| 			data, err := ec.unmarshalOStringInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐStringInput(ctx, v) | ||||
| 			if err != nil { | ||||
| 				return it, err | ||||
| 			} | ||||
| 			it.Subcluster = data | ||||
| 		case "nodeState": | ||||
| 			ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nodeState")) | ||||
| 			data, err := ec.unmarshalONodeState2ᚖstring(ctx, v) | ||||
| @@ -17976,6 +18101,11 @@ func (ec *executionContext) _JobsStatistics(ctx context.Context, sel ast.Selecti | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| 				out.Invalids++ | ||||
| 			} | ||||
| 		case "totalUsers": | ||||
| 			out.Values[i] = ec._JobsStatistics_totalUsers(ctx, field, obj) | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| 				out.Invalids++ | ||||
| 			} | ||||
| 		case "totalJobs": | ||||
| 			out.Values[i] = ec._JobsStatistics_totalJobs(ctx, field, obj) | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| @@ -18625,6 +18755,42 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| 				atomic.AddUint32(&out.Invalids, 1) | ||||
| 			} | ||||
| 		case "runningJobs": | ||||
| 			field := field | ||||
|  | ||||
| 			innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { | ||||
| 				defer func() { | ||||
| 					if r := recover(); r != nil { | ||||
| 						ec.Error(ctx, ec.Recover(ctx, r)) | ||||
| 					} | ||||
| 				}() | ||||
| 				res = ec._Node_runningJobs(ctx, field, obj) | ||||
| 				if res == graphql.Null { | ||||
| 					atomic.AddUint32(&fs.Invalids, 1) | ||||
| 				} | ||||
| 				return res | ||||
| 			} | ||||
|  | ||||
| 			if field.Deferrable != nil { | ||||
| 				dfs, ok := deferred[field.Deferrable.Label] | ||||
| 				di := 0 | ||||
| 				if ok { | ||||
| 					dfs.AddField(field) | ||||
| 					di = len(dfs.Values) - 1 | ||||
| 				} else { | ||||
| 					dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) | ||||
| 					deferred[field.Deferrable.Label] = dfs | ||||
| 				} | ||||
| 				dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { | ||||
| 					return innerFunc(ctx, dfs) | ||||
| 				}) | ||||
|  | ||||
| 				// don't run the out.Concurrently() call below | ||||
| 				out.Values[i] = graphql.Null | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) | ||||
| 		case "nodeState": | ||||
| 			field := field | ||||
|  | ||||
| @@ -18661,7 +18827,7 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj | ||||
| 			} | ||||
|  | ||||
| 			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) | ||||
| 		case "HealthState": | ||||
| 		case "healthState": | ||||
| 			field := field | ||||
|  | ||||
| 			innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { | ||||
| @@ -18670,7 +18836,7 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj | ||||
| 						ec.Error(ctx, ec.Recover(ctx, r)) | ||||
| 					} | ||||
| 				}() | ||||
| 				res = ec._Node_HealthState(ctx, field, obj) | ||||
| 				res = ec._Node_healthState(ctx, field, obj) | ||||
| 				if res == graphql.Null { | ||||
| 					atomic.AddUint32(&fs.Invalids, 1) | ||||
| 				} | ||||
| @@ -18843,24 +19009,24 @@ func (ec *executionContext) _NodeStateResultList(ctx context.Context, sel ast.Se | ||||
| 	return out | ||||
| } | ||||
|  | ||||
| var nodeStatsImplementors = []string{"NodeStats"} | ||||
| var nodeStatesImplementors = []string{"NodeStates"} | ||||
|  | ||||
| func (ec *executionContext) _NodeStats(ctx context.Context, sel ast.SelectionSet, obj *model.NodeStats) graphql.Marshaler { | ||||
| 	fields := graphql.CollectFields(ec.OperationContext, sel, nodeStatsImplementors) | ||||
| func (ec *executionContext) _NodeStates(ctx context.Context, sel ast.SelectionSet, obj *model.NodeStates) graphql.Marshaler { | ||||
| 	fields := graphql.CollectFields(ec.OperationContext, sel, nodeStatesImplementors) | ||||
|  | ||||
| 	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("NodeStats") | ||||
| 			out.Values[i] = graphql.MarshalString("NodeStates") | ||||
| 		case "state": | ||||
| 			out.Values[i] = ec._NodeStats_state(ctx, field, obj) | ||||
| 			out.Values[i] = ec._NodeStates_state(ctx, field, obj) | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| 				out.Invalids++ | ||||
| 			} | ||||
| 		case "count": | ||||
| 			out.Values[i] = ec._NodeStats_count(ctx, field, obj) | ||||
| 			out.Values[i] = ec._NodeStates_count(ctx, field, obj) | ||||
| 			if out.Values[i] == graphql.Null { | ||||
| 				out.Invalids++ | ||||
| 			} | ||||
| @@ -19103,7 +19269,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr | ||||
| 			} | ||||
|  | ||||
| 			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) | ||||
| 		case "nodeStats": | ||||
| 		case "nodeStates": | ||||
| 			field := field | ||||
|  | ||||
| 			innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { | ||||
| @@ -19112,7 +19278,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr | ||||
| 						ec.Error(ctx, ec.Recover(ctx, r)) | ||||
| 					} | ||||
| 				}() | ||||
| 				res = ec._Query_nodeStats(ctx, field) | ||||
| 				res = ec._Query_nodeStates(ctx, field) | ||||
| 				if res == graphql.Null { | ||||
| 					atomic.AddUint32(&fs.Invalids, 1) | ||||
| 				} | ||||
| @@ -21756,7 +21922,7 @@ func (ec *executionContext) marshalNNodeStateResultList2ᚖgithubᚗcomᚋCluste | ||||
| 	return ec._NodeStateResultList(ctx, sel, v) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) marshalNNodeStats2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStatsᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NodeStats) graphql.Marshaler { | ||||
| func (ec *executionContext) marshalNNodeStates2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStatesᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NodeStates) graphql.Marshaler { | ||||
| 	ret := make(graphql.Array, len(v)) | ||||
| 	var wg sync.WaitGroup | ||||
| 	isLen1 := len(v) == 1 | ||||
| @@ -21780,7 +21946,7 @@ func (ec *executionContext) marshalNNodeStats2ᚕᚖgithubᚗcomᚋClusterCockpi | ||||
| 			if !isLen1 { | ||||
| 				defer wg.Done() | ||||
| 			} | ||||
| 			ret[i] = ec.marshalNNodeStats2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStats(ctx, sel, v[i]) | ||||
| 			ret[i] = ec.marshalNNodeStates2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStates(ctx, sel, v[i]) | ||||
| 		} | ||||
| 		if isLen1 { | ||||
| 			f(i) | ||||
| @@ -21800,14 +21966,14 @@ func (ec *executionContext) marshalNNodeStats2ᚕᚖgithubᚗcomᚋClusterCockpi | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) marshalNNodeStats2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStats(ctx context.Context, sel ast.SelectionSet, v *model.NodeStats) graphql.Marshaler { | ||||
| func (ec *executionContext) marshalNNodeStates2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeStates(ctx context.Context, sel ast.SelectionSet, v *model.NodeStates) 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._NodeStats(ctx, sel, v) | ||||
| 	return ec._NodeStates(ctx, sel, v) | ||||
| } | ||||
|  | ||||
| func (ec *executionContext) marshalNNodesResultList2githubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodesResultList(ctx context.Context, sel ast.SelectionSet, v model.NodesResultList) graphql.Marshaler { | ||||
|   | ||||
| @@ -114,6 +114,7 @@ type JobStats struct { | ||||
| type JobsStatistics struct { | ||||
| 	ID             string               `json:"id"` | ||||
| 	Name           string               `json:"name"` | ||||
| 	TotalUsers     int                  `json:"totalUsers"` | ||||
| 	TotalJobs      int                  `json:"totalJobs"` | ||||
| 	RunningJobs    int                  `json:"runningJobs"` | ||||
| 	ShortJobs      int                  `json:"shortJobs"` | ||||
| @@ -172,6 +173,7 @@ type NamedStatsWithScope struct { | ||||
| type NodeFilter struct { | ||||
| 	Hostname    *StringInput      `json:"hostname,omitempty"` | ||||
| 	Cluster     *StringInput      `json:"cluster,omitempty"` | ||||
| 	Subcluster  *StringInput      `json:"subcluster,omitempty"` | ||||
| 	NodeState   *string           `json:"nodeState,omitempty"` | ||||
| 	HealthState *schema.NodeState `json:"healthState,omitempty"` | ||||
| } | ||||
| @@ -187,7 +189,7 @@ type NodeStateResultList struct { | ||||
| 	Count *int           `json:"count,omitempty"` | ||||
| } | ||||
|  | ||||
| type NodeStats struct { | ||||
| type NodeStates struct { | ||||
| 	State string `json:"state"` | ||||
| 	Count int    `json:"count"` | ||||
| } | ||||
| @@ -248,20 +250,22 @@ type User struct { | ||||
| type Aggregate string | ||||
|  | ||||
| const ( | ||||
| 	AggregateUser    Aggregate = "USER" | ||||
| 	AggregateProject Aggregate = "PROJECT" | ||||
| 	AggregateCluster Aggregate = "CLUSTER" | ||||
| 	AggregateUser       Aggregate = "USER" | ||||
| 	AggregateProject    Aggregate = "PROJECT" | ||||
| 	AggregateCluster    Aggregate = "CLUSTER" | ||||
| 	AggregateSubcluster Aggregate = "SUBCLUSTER" | ||||
| ) | ||||
|  | ||||
| var AllAggregate = []Aggregate{ | ||||
| 	AggregateUser, | ||||
| 	AggregateProject, | ||||
| 	AggregateCluster, | ||||
| 	AggregateSubcluster, | ||||
| } | ||||
|  | ||||
| func (e Aggregate) IsValid() bool { | ||||
| 	switch e { | ||||
| 	case AggregateUser, AggregateProject, AggregateCluster: | ||||
| 	case AggregateUser, AggregateProject, AggregateCluster, AggregateSubcluster: | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| @@ -307,6 +311,7 @@ type SortByAggregate string | ||||
| const ( | ||||
| 	SortByAggregateTotalwalltime  SortByAggregate = "TOTALWALLTIME" | ||||
| 	SortByAggregateTotaljobs      SortByAggregate = "TOTALJOBS" | ||||
| 	SortByAggregateTotalusers     SortByAggregate = "TOTALUSERS" | ||||
| 	SortByAggregateTotalnodes     SortByAggregate = "TOTALNODES" | ||||
| 	SortByAggregateTotalnodehours SortByAggregate = "TOTALNODEHOURS" | ||||
| 	SortByAggregateTotalcores     SortByAggregate = "TOTALCORES" | ||||
| @@ -318,6 +323,7 @@ const ( | ||||
| var AllSortByAggregate = []SortByAggregate{ | ||||
| 	SortByAggregateTotalwalltime, | ||||
| 	SortByAggregateTotaljobs, | ||||
| 	SortByAggregateTotalusers, | ||||
| 	SortByAggregateTotalnodes, | ||||
| 	SortByAggregateTotalnodehours, | ||||
| 	SortByAggregateTotalcores, | ||||
| @@ -328,7 +334,7 @@ var AllSortByAggregate = []SortByAggregate{ | ||||
|  | ||||
| func (e SortByAggregate) IsValid() bool { | ||||
| 	switch e { | ||||
| 	case SortByAggregateTotalwalltime, SortByAggregateTotaljobs, SortByAggregateTotalnodes, SortByAggregateTotalnodehours, SortByAggregateTotalcores, SortByAggregateTotalcorehours, SortByAggregateTotalaccs, SortByAggregateTotalacchours: | ||||
| 	case SortByAggregateTotalwalltime, SortByAggregateTotaljobs, SortByAggregateTotalusers, SortByAggregateTotalnodes, SortByAggregateTotalnodehours, SortByAggregateTotalcores, SortByAggregateTotalcorehours, SortByAggregateTotalaccs, SortByAggregateTotalacchours: | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
|   | ||||
| @@ -305,14 +305,20 @@ func (r *mutationResolver) UpdateConfiguration(ctx context.Context, name string, | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // NodeState is the resolver for the nodeState field. | ||||
| func (r *nodeResolver) NodeState(ctx context.Context, obj *schema.Node) (string, error) { | ||||
| 	panic(fmt.Errorf("not implemented: NodeState - nodeState")) | ||||
| // RunningJobs is the resolver for the runningJobs field. | ||||
| func (r *nodeResolver) RunningJobs(ctx context.Context, obj *schema.Node) (int, error) { | ||||
| 	panic(fmt.Errorf("not implemented: RunningJobs - runningJobs")) | ||||
| } | ||||
|  | ||||
| // HealthState is the resolver for the HealthState field. | ||||
| // NodeState is the resolver for the nodeState field. | ||||
| func (r *nodeResolver) NodeState(ctx context.Context, obj *schema.Node) (string, error) { | ||||
| 	return string(obj.NodeState), nil | ||||
| } | ||||
|  | ||||
| // HealthState is the resolver for the healthState field. | ||||
| func (r *nodeResolver) HealthState(ctx context.Context, obj *schema.Node) (schema.NodeState, error) { | ||||
| 	panic(fmt.Errorf("not implemented: HealthState - HealthState")) | ||||
| 	// FIXME: Why is Output of schema.NodeState Type? | ||||
| 	panic(fmt.Errorf("not implemented: HealthState - healthState")) | ||||
| } | ||||
|  | ||||
| // MetaData is the resolver for the metaData field. | ||||
| @@ -378,9 +384,26 @@ func (r *queryResolver) Nodes(ctx context.Context, filter []*model.NodeFilter, o | ||||
| 	return &model.NodeStateResultList{Items: nodes, Count: &count}, err | ||||
| } | ||||
|  | ||||
| // NodeStats is the resolver for the nodeStats field. | ||||
| func (r *queryResolver) NodeStats(ctx context.Context, filter []*model.NodeFilter) ([]*model.NodeStats, error) { | ||||
| 	panic(fmt.Errorf("not implemented: NodeStats - nodeStats")) | ||||
| // NodeStates is the resolver for the nodeStates field. | ||||
| func (r *queryResolver) NodeStates(ctx context.Context, filter []*model.NodeFilter) ([]*model.NodeStates, error) { | ||||
| 	repo := repository.GetNodeRepository() | ||||
|  | ||||
| 	stateCounts, serr := repo.CountNodeStates(ctx, filter) | ||||
| 	if serr != nil { | ||||
| 		cclog.Warnf("Error while counting nodeStates: %s", serr.Error()) | ||||
| 		return nil, serr | ||||
| 	} | ||||
|  | ||||
| 	healthCounts, herr := repo.CountHealthStates(ctx, filter) | ||||
| 	if herr != nil { | ||||
| 		cclog.Warnf("Error while counting healthStates: %s", herr.Error()) | ||||
| 		return nil, herr | ||||
| 	} | ||||
|  | ||||
| 	allCounts := make([]*model.NodeStates, 0) | ||||
| 	allCounts = append(stateCounts, healthCounts...) | ||||
|  | ||||
| 	return allCounts, nil | ||||
| } | ||||
|  | ||||
| // Job is the resolver for the job field. | ||||
| @@ -558,7 +581,7 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF | ||||
| 	defaultDurationBins := "1h" | ||||
| 	defaultMetricBins := 10 | ||||
|  | ||||
| 	if requireField(ctx, "totalJobs") || requireField(ctx, "totalWalltime") || requireField(ctx, "totalNodes") || requireField(ctx, "totalCores") || | ||||
| 	if requireField(ctx, "totalJobs") || requireField(ctx, "totalUsers") || requireField(ctx, "totalWalltime") || requireField(ctx, "totalNodes") || requireField(ctx, "totalCores") || | ||||
| 		requireField(ctx, "totalAccs") || requireField(ctx, "totalNodeHours") || requireField(ctx, "totalCoreHours") || requireField(ctx, "totalAccHours") { | ||||
| 		if groupBy == nil { | ||||
| 			stats, err = r.Repo.JobsStats(ctx, filter) | ||||
|   | ||||
| @@ -40,7 +40,7 @@ func InitDB() error { | ||||
| 	} | ||||
| 	tags := make(map[string]int64) | ||||
|  | ||||
| 	// Not using log.Print because we want the line to end with `\r` and | ||||
| 	// Not using cclog.Print because we want the line to end with `\r` and | ||||
| 	// this function is only ever called when a special command line flag | ||||
| 	// is passed anyways. | ||||
| 	fmt.Printf("%d jobs inserted...\r", 0) | ||||
|   | ||||
| @@ -337,10 +337,10 @@ func (r *JobRepository) FindColumnValue(user *schema.User, searchterm string, ta | ||||
|  | ||||
| 		// theSql, args, theErr := theQuery.ToSql() | ||||
| 		// if theErr != nil { | ||||
| 		// 	log.Warn("Error while converting query to sql") | ||||
| 		// 	cclog.Warn("Error while converting query to sql") | ||||
| 		// 	return "", err | ||||
| 		// } | ||||
| 		// log.Debugf("SQL query (FindColumnValue): `%s`, args: %#v", theSql, args) | ||||
| 		// cclog.Debugf("SQL query (FindColumnValue): `%s`, args: %#v", theSql, args) | ||||
|  | ||||
| 		err := theQuery.RunWith(r.stmtCache).QueryRow().Scan(&result) | ||||
|  | ||||
|   | ||||
| @@ -4,12 +4,13 @@ CREATE TABLE "node" ( | ||||
|     hostname VARCHAR(255) NOT NULL, | ||||
|     cluster VARCHAR(255) NOT NULL, | ||||
|     subcluster VARCHAR(255) NOT NULL, | ||||
|     cpus_allocated INTEGER NOT NULL, | ||||
|     cpus_total INTEGER NOT NULL, | ||||
|     memory_allocated INTEGER NOT NULL, | ||||
|     memory_total INTEGER NOT NULL, | ||||
|     gpus_allocated INTEGER NOT NULL, | ||||
|     gpus_total INTEGER NOT NULL, | ||||
|     jobs_running INTEGER DEFAULT 0 NOT NULL, | ||||
|     cpus_allocated INTEGER DEFAULT 0 NOT NULL, | ||||
|     cpus_total INTEGER DEFAULT 0 NOT NULL, | ||||
|     memory_allocated INTEGER DEFAULT 0 NOT NULL, | ||||
|     memory_total INTEGER DEFAULT 0 NOT NULL, | ||||
|     gpus_allocated INTEGER DEFAULT 0 NOT NULL, | ||||
|     gpus_total INTEGER DEFAULT 0 NOT NULL, | ||||
|     node_state VARCHAR(255) NOT NULL | ||||
|     CHECK (node_state IN ( | ||||
|         'allocated', 'reserved', 'idle', 'mixed', | ||||
|   | ||||
| @@ -49,6 +49,12 @@ func GetNodeRepository() *NodeRepository { | ||||
| 	return nodeRepoInstance | ||||
| } | ||||
|  | ||||
| var nodeColumns []string = []string{ | ||||
| 	// "node.id," | ||||
| 	"node.hostname", "node.cluster", "node.subcluster", | ||||
| 	"node.node_state", "node.health_state", // "node.meta_data", | ||||
| } | ||||
|  | ||||
| func (r *NodeRepository) FetchMetadata(node *schema.Node) (map[string]string, error) { | ||||
| 	start := time.Now() | ||||
| 	cachekey := fmt.Sprintf("metadata:%d", node.ID) | ||||
| @@ -218,9 +224,9 @@ func (r *NodeRepository) DeleteNode(id int64) error { | ||||
| func (r *NodeRepository) QueryNodes( | ||||
| 	ctx context.Context, | ||||
| 	filters []*model.NodeFilter, | ||||
| 	order *model.OrderByInput, | ||||
| 	order *model.OrderByInput, // Currently unused! | ||||
| ) ([]*schema.Node, error) { | ||||
| 	query, qerr := SecurityCheck(ctx, sq.Select(jobColumns...).From("node")) | ||||
| 	query, qerr := AccessCheck(ctx, sq.Select(nodeColumns...).From("node")) | ||||
| 	if qerr != nil { | ||||
| 		return nil, qerr | ||||
| 	} | ||||
| @@ -232,6 +238,9 @@ func (r *NodeRepository) QueryNodes( | ||||
| 		if f.Cluster != nil { | ||||
| 			query = buildStringCondition("node.cluster", f.Cluster, query) | ||||
| 		} | ||||
| 		if f.Subcluster != nil { | ||||
| 			query = buildStringCondition("node.subcluster", f.Subcluster, query) | ||||
| 		} | ||||
| 		if f.NodeState != nil { | ||||
| 			query = query.Where("node.node_state = ?", f.NodeState) | ||||
| 		} | ||||
| @@ -287,3 +296,123 @@ func (r *NodeRepository) ListNodes(cluster string) ([]*schema.Node, error) { | ||||
|  | ||||
| 	return nodeList, nil | ||||
| } | ||||
|  | ||||
| func (r *NodeRepository) CountNodeStates(ctx context.Context, filters []*model.NodeFilter) ([]*model.NodeStates, error) { | ||||
| 	query, qerr := AccessCheck(ctx, sq.Select("node_state AS state", "count(*) AS count").From("node")) | ||||
| 	if qerr != nil { | ||||
| 		return nil, qerr | ||||
| 	} | ||||
|  | ||||
| 	for _, f := range filters { | ||||
| 		if f.Hostname != nil { | ||||
| 			query = buildStringCondition("node.hostname", f.Hostname, query) | ||||
| 		} | ||||
| 		if f.Cluster != nil { | ||||
| 			query = buildStringCondition("node.cluster", f.Cluster, query) | ||||
| 		} | ||||
| 		if f.Subcluster != nil { | ||||
| 			query = buildStringCondition("node.subcluster", f.Subcluster, query) | ||||
| 		} | ||||
| 		if f.NodeState != nil { | ||||
| 			query = query.Where("node.node_state = ?", f.NodeState) | ||||
| 		} | ||||
| 		if f.HealthState != nil { | ||||
| 			query = query.Where("node.health_state = ?", f.HealthState) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Add Group and Order | ||||
| 	query = query.GroupBy("state").OrderBy("count DESC") | ||||
|  | ||||
| 	rows, err := query.RunWith(r.stmtCache).Query() | ||||
| 	if err != nil { | ||||
| 		queryString, queryVars, _ := query.ToSql() | ||||
| 		cclog.Errorf("Error while running query '%s' %v: %v", queryString, queryVars, err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	nodes := make([]*model.NodeStates, 0) | ||||
| 	for rows.Next() { | ||||
| 		node := model.NodeStates{} | ||||
|  | ||||
| 		if err := rows.Scan(&node.State, &node.Count); err != nil { | ||||
| 			rows.Close() | ||||
| 			cclog.Warn("Error while scanning rows (NodeStates)") | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		nodes = append(nodes, &node) | ||||
| 	} | ||||
|  | ||||
| 	return nodes, nil | ||||
| } | ||||
|  | ||||
| func (r *NodeRepository) CountHealthStates(ctx context.Context, filters []*model.NodeFilter) ([]*model.NodeStates, error) { | ||||
| 	query, qerr := AccessCheck(ctx, sq.Select("health_state AS state", "count(*) AS count").From("node")) | ||||
| 	if qerr != nil { | ||||
| 		return nil, qerr | ||||
| 	} | ||||
|  | ||||
| 	for _, f := range filters { | ||||
| 		if f.Hostname != nil { | ||||
| 			query = buildStringCondition("node.hostname", f.Hostname, query) | ||||
| 		} | ||||
| 		if f.Cluster != nil { | ||||
| 			query = buildStringCondition("node.cluster", f.Cluster, query) | ||||
| 		} | ||||
| 		if f.Subcluster != nil { | ||||
| 			query = buildStringCondition("node.subcluster", f.Subcluster, query) | ||||
| 		} | ||||
| 		if f.NodeState != nil { | ||||
| 			query = query.Where("node.node_state = ?", f.NodeState) | ||||
| 		} | ||||
| 		if f.HealthState != nil { | ||||
| 			query = query.Where("node.health_state = ?", f.HealthState) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Add Group and Order | ||||
| 	query = query.GroupBy("state").OrderBy("count DESC") | ||||
|  | ||||
| 	rows, err := query.RunWith(r.stmtCache).Query() | ||||
| 	if err != nil { | ||||
| 		queryString, queryVars, _ := query.ToSql() | ||||
| 		cclog.Errorf("Error while running query '%s' %v: %v", queryString, queryVars, err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	nodes := make([]*model.NodeStates, 0) | ||||
| 	for rows.Next() { | ||||
| 		node := model.NodeStates{} | ||||
|  | ||||
| 		if err := rows.Scan(&node.State, &node.Count); err != nil { | ||||
| 			rows.Close() | ||||
| 			cclog.Warn("Error while scanning rows (NodeStates)") | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		nodes = append(nodes, &node) | ||||
| 	} | ||||
|  | ||||
| 	return nodes, nil | ||||
| } | ||||
|  | ||||
| func AccessCheck(ctx context.Context, query sq.SelectBuilder) (sq.SelectBuilder, error) { | ||||
| 	user := GetUserFromContext(ctx) | ||||
| 	return AccessCheckWithUser(user, query) | ||||
| } | ||||
|  | ||||
| func AccessCheckWithUser(user *schema.User, query sq.SelectBuilder) (sq.SelectBuilder, error) { | ||||
| 	if user == nil { | ||||
| 		var qnil sq.SelectBuilder | ||||
| 		return qnil, fmt.Errorf("user context is nil") | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	// case len(user.Roles) == 1 && user.HasRole(schema.RoleApi): // API-User : Access NodeInfos | ||||
| 	// 	return query, nil | ||||
| 	case user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}): // Admin & Support : Access NodeInfos | ||||
| 		return query, nil | ||||
| 	default: // No known Role: No Access, return error | ||||
| 		var qnil sq.SelectBuilder | ||||
| 		return qnil, fmt.Errorf("user has no or unknown roles") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ var groupBy2column = map[model.Aggregate]string{ | ||||
|  | ||||
| var sortBy2column = map[model.SortByAggregate]string{ | ||||
| 	model.SortByAggregateTotaljobs:      "totalJobs", | ||||
| 	model.SortByAggregateTotalusers:     "totalUsers", | ||||
| 	model.SortByAggregateTotalwalltime:  "totalWalltime", | ||||
| 	model.SortByAggregateTotalnodes:     "totalNodes", | ||||
| 	model.SortByAggregateTotalnodehours: "totalNodeHours", | ||||
| @@ -76,8 +77,12 @@ func (r *JobRepository) buildStatsQuery( | ||||
| 	// fmt.Sprintf(`CAST(ROUND((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / 3600) as %s) as value`, time.Now().Unix(), castType) | ||||
|  | ||||
| 	if col != "" { | ||||
| 		// Scan columns: id, totalJobs, name, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours | ||||
| 		query = sq.Select(col, "COUNT(job.id) as totalJobs", "name", | ||||
| 		// Scan columns: id, name, totalJobs, totalUsers, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours | ||||
| 		query = sq.Select( | ||||
| 			col, | ||||
| 			"name", | ||||
| 			"COUNT(job.id) as totalJobs", | ||||
| 			"COUNT(DISTINCT job.hpc_user) AS totalUsers", | ||||
| 			fmt.Sprintf(`CAST(ROUND(SUM((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END)) / 3600) as %s) as totalWalltime`, time.Now().Unix(), castType), | ||||
| 			fmt.Sprintf(`CAST(SUM(job.num_nodes) as %s) as totalNodes`, castType), | ||||
| 			fmt.Sprintf(`CAST(ROUND(SUM((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) * job.num_nodes) / 3600) as %s) as totalNodeHours`, time.Now().Unix(), castType), | ||||
| @@ -87,8 +92,10 @@ func (r *JobRepository) buildStatsQuery( | ||||
| 			fmt.Sprintf(`CAST(ROUND(SUM((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) * job.num_acc) / 3600) as %s) as totalAccHours`, time.Now().Unix(), castType), | ||||
| 		).From("job").LeftJoin("hpc_user ON hpc_user.username = job.hpc_user").GroupBy(col) | ||||
| 	} else { | ||||
| 		// Scan columns: totalJobs, name, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours | ||||
| 		query = sq.Select("COUNT(job.id)", | ||||
| 		// Scan columns: totalJobs, totalUsers, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours | ||||
| 		query = sq.Select( | ||||
| 			"COUNT(job.id) as totalJobs", | ||||
| 			"COUNT(DISTINCT job.hpc_user) AS totalUsers", | ||||
| 			fmt.Sprintf(`CAST(ROUND(SUM((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END)) / 3600) as %s)`, time.Now().Unix(), castType), | ||||
| 			fmt.Sprintf(`CAST(SUM(job.num_nodes) as %s)`, castType), | ||||
| 			fmt.Sprintf(`CAST(ROUND(SUM((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) * job.num_nodes) / 3600) as %s)`, time.Now().Unix(), castType), | ||||
| @@ -167,14 +174,14 @@ func (r *JobRepository) JobsStatsGrouped( | ||||
| 	for rows.Next() { | ||||
| 		var id sql.NullString | ||||
| 		var name sql.NullString | ||||
| 		var jobs, walltime, nodes, nodeHours, cores, coreHours, accs, accHours sql.NullInt64 | ||||
| 		if err := rows.Scan(&id, &jobs, &name, &walltime, &nodes, &nodeHours, &cores, &coreHours, &accs, &accHours); err != nil { | ||||
| 		var jobs, users, walltime, nodes, nodeHours, cores, coreHours, accs, accHours sql.NullInt64 | ||||
| 		if err := rows.Scan(&id, &name, &jobs, &users, &walltime, &nodes, &nodeHours, &cores, &coreHours, &accs, &accHours); err != nil { | ||||
| 			cclog.Warn("Error while scanning rows") | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		if id.Valid { | ||||
| 			var totalJobs, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours int | ||||
| 			var totalJobs, totalUsers, totalWalltime, totalNodes, totalNodeHours, totalCores, totalCoreHours, totalAccs, totalAccHours int | ||||
| 			var personName string | ||||
|  | ||||
| 			if name.Valid { | ||||
| @@ -185,6 +192,10 @@ func (r *JobRepository) JobsStatsGrouped( | ||||
| 				totalJobs = int(jobs.Int64) | ||||
| 			} | ||||
|  | ||||
| 			if users.Valid { | ||||
| 				totalUsers = int(users.Int64) | ||||
| 			} | ||||
|  | ||||
| 			if walltime.Valid { | ||||
| 				totalWalltime = int(walltime.Int64) | ||||
| 			} | ||||
| @@ -228,8 +239,9 @@ func (r *JobRepository) JobsStatsGrouped( | ||||
| 				stats = append(stats, | ||||
| 					&model.JobsStatistics{ | ||||
| 						ID:             id.String, | ||||
| 						TotalJobs:      int(jobs.Int64), | ||||
| 						TotalWalltime:  int(walltime.Int64), | ||||
| 						TotalJobs:      totalJobs, | ||||
| 						TotalUsers:     totalUsers, | ||||
| 						TotalWalltime:  totalWalltime, | ||||
| 						TotalNodes:     totalNodes, | ||||
| 						TotalNodeHours: totalNodeHours, | ||||
| 						TotalCores:     totalCores, | ||||
| @@ -259,8 +271,8 @@ func (r *JobRepository) JobsStats( | ||||
| 	row := query.RunWith(r.DB).QueryRow() | ||||
| 	stats := make([]*model.JobsStatistics, 0, 1) | ||||
|  | ||||
| 	var jobs, walltime, nodes, nodeHours, cores, coreHours, accs, accHours sql.NullInt64 | ||||
| 	if err := row.Scan(&jobs, &walltime, &nodes, &nodeHours, &cores, &coreHours, &accs, &accHours); err != nil { | ||||
| 	var jobs, users, walltime, nodes, nodeHours, cores, coreHours, accs, accHours sql.NullInt64 | ||||
| 	if err := row.Scan(&jobs, &users, &walltime, &nodes, &nodeHours, &cores, &coreHours, &accs, &accHours); err != nil { | ||||
| 		cclog.Warn("Error while scanning rows") | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -280,6 +292,7 @@ func (r *JobRepository) JobsStats( | ||||
| 		stats = append(stats, | ||||
| 			&model.JobsStatistics{ | ||||
| 				TotalJobs:      int(jobs.Int64), | ||||
| 				TotalUsers:     int(users.Int64), | ||||
| 				TotalWalltime:  int(walltime.Int64), | ||||
| 				TotalNodeHours: totalNodeHours, | ||||
| 				TotalCoreHours: totalCoreHours, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user