mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-30 23:45:06 +01:00 
			
		
		
		
	Merge branch 'dev' of github.com:ClusterCockpit/cc-backend into dev
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| /cc-backend | ||||
| /.env | ||||
| /config.json | ||||
| /uiConfig.json | ||||
|  | ||||
| /var/job-archive | ||||
| /var/machine-state | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import "flag" | ||||
| var ( | ||||
| 	flagReinitDB, flagInit, flagServer, flagSyncLDAP, flagGops, flagMigrateDB, flagRevertDB, | ||||
| 	flagForceDB, flagDev, flagVersion, flagLogDateTime, flagApplyTags bool | ||||
| 	flagNewUser, flagDelUser, flagGenJWT, flagConfigFile, flagImportJob, flagLogLevel string | ||||
| 	flagNewUser, flagDelUser, flagGenJWT, flagConfigFile, flagUiConfigFile, flagImportJob, flagLogLevel string | ||||
| ) | ||||
|  | ||||
| func cliInit() { | ||||
| @@ -26,6 +26,7 @@ func cliInit() { | ||||
| 	flag.BoolVar(&flagForceDB, "force-db", false, "Force database version, clear dirty flag and exit") | ||||
| 	flag.BoolVar(&flagLogDateTime, "logdate", false, "Set this flag to add date and time to log messages") | ||||
| 	flag.StringVar(&flagConfigFile, "config", "./config.json", "Specify alternative path to `config.json`") | ||||
| 	flag.StringVar(&flagUiConfigFile, "ui-config", "./uiConfig.json", "Specify alternative path to `uiConfig.json`") | ||||
| 	flag.StringVar(&flagNewUser, "add-user", "", "Add a new user. Argument format: <username>:[admin,support,manager,api,user]:<password>") | ||||
| 	flag.StringVar(&flagDelUser, "del-user", "", "Remove a existing user. Argument format: <username>") | ||||
| 	flag.StringVar(&flagGenJWT, "jwt", "", "Generate and print a JWT for the user specified by its `username`") | ||||
|   | ||||
| @@ -52,6 +52,8 @@ func onFailureResponse(rw http.ResponseWriter, r *http.Request, err error) { | ||||
| } | ||||
|  | ||||
| func serverInit() { | ||||
| 	// Init Web Package (Primarily: uiDefaults) | ||||
| 	web.Init(flagUiConfigFile) | ||||
| 	// Setup the http.Handler/Router used by the server | ||||
| 	graph.Init() | ||||
| 	resolver := graph.GetResolverInstance() | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| { | ||||
|   "clusters": [ | ||||
|     { | ||||
|       "name": "fritz", | ||||
|       "default_metrics": "cpu_load, flops_any, core_power, lustre_open, mem_used, mem_bw, net_bytes_in" | ||||
|     }, | ||||
|     { | ||||
|       "name": "alex", | ||||
|       "default_metrics": "flops_any, mem_bw, mem_used, vectorization_ratio" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										45
									
								
								configs/uiConfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								configs/uiConfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| { | ||||
|   "jobList": { | ||||
|     "usePaging": false, | ||||
|     "showFootprint":false | ||||
|   }, | ||||
|   "jobView": { | ||||
|     "showPolarPlot": true, | ||||
|     "showFootprint": true, | ||||
|     "showRoofline": true, | ||||
|     "showStatTable": true | ||||
|   }, | ||||
|   "metricConfig": { | ||||
|     "jobListMetrics": ["mem_bw", "flops_dp"], | ||||
|     "jobViewPlotMetrics": ["mem_bw", "flops_dp"], | ||||
|     "jobViewTableMetrics": ["mem_bw", "flops_dp"], | ||||
|     "clusters": [ | ||||
|       { | ||||
|         "name": "test", | ||||
|         "subClusters": [ | ||||
|           { | ||||
|             "name": "one", | ||||
|             "jobListMetrics": ["mem_used", "flops_sp"] | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "nodeList": { | ||||
|     "usePaging": true | ||||
|   }, | ||||
|   "plotConfiguration": { | ||||
|     "plotsPerRow": 3, | ||||
|     "colorBackground": true, | ||||
|     "lineWidth": 3, | ||||
|     "colorScheme": [ | ||||
| 	"#00bfff", | ||||
| 	"#0000ff", | ||||
| 	"#ff00ff", | ||||
| 	"#ff0000", | ||||
| 	"#ff8000", | ||||
| 	"#ffff00", | ||||
| 	"#80ff00" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -11,6 +11,8 @@ import ( | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // DEPRECATED: SUPERSEDED BY NEW USER CONFIG - userConfig.go / web.go | ||||
|  | ||||
| type DefaultMetricsCluster struct { | ||||
| 	Name           string `json:"name"` | ||||
| 	DefaultMetrics string `json:"default_metrics"` | ||||
|   | ||||
| @@ -129,6 +129,7 @@ func (r *UserRepository) AddUser(user *schema.User) error { | ||||
|  | ||||
| 	cclog.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson) | ||||
|  | ||||
| 	// DEPRECATED: SUPERSEDED BY NEW USER CONFIG - userConfig.go / web.go | ||||
| 	defaultMetricsCfg, err := config.LoadDefaultMetricsConfig() | ||||
| 	if err != nil { | ||||
| 		cclog.Errorf("Error loading default metrics config: %v", err) | ||||
| @@ -140,7 +141,8 @@ func (r *UserRepository) AddUser(user *schema.User) error { | ||||
| 				cclog.Errorf("Error marshaling default metrics for cluster %s: %v", cluster.Name, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			confKey := "job_view_selectedMetrics:" + cluster.Name | ||||
| 			// Note: StatisticsTable now has different key (metricConfig_jobViewTableMetrics): Not updated here. | ||||
| 			confKey := "metricConfig_jobViewPlotMetrics:" + cluster.Name | ||||
| 			if _, err := sq.Insert("configuration"). | ||||
| 				Columns("username", "confkey", "value"). | ||||
| 				Values(user.Username, confKey, string(metricsJSON)). | ||||
| @@ -151,6 +153,7 @@ func (r *UserRepository) AddUser(user *schema.User) error { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// END DEPRECATION | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -91,8 +91,8 @@ func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]any, error) | ||||
| 			uiconfig[key] = val | ||||
| 		} | ||||
|  | ||||
| 		// Add global ShortRunningJobsDuration setting as plot_list_hideShortRunningJobs | ||||
| 		uiconfig["plot_list_hideShortRunningJobs"] = config.Keys.ShortRunningJobsDuration | ||||
| 		// Add global ShortRunningJobsDuration setting as jobList_hideShortRunningJobs | ||||
| 		uiconfig["jobList_hideShortRunningJobs"] = config.Keys.ShortRunningJobsDuration | ||||
|  | ||||
| 		return uiconfig, 24 * time.Hour, size | ||||
| 	}) | ||||
|   | ||||
| @@ -82,27 +82,21 @@ | ||||
|   let colWidth1 = $state(0); | ||||
|   let colWidth2 = $state(0); | ||||
|   let jobFilters = $state([]); | ||||
|   let metricsInHistograms = $state(ccconfig.analysis_view_histogramMetrics) | ||||
|   let metricsInScatterplots = $state(ccconfig.analysis_view_scatterPlotMetrics) | ||||
|   let metricsInHistograms = $state(ccconfig?.analysisView_histogramMetrics || []) | ||||
|   let metricsInScatterplots = $state(ccconfig?.analysisView_scatterPlotMetrics || []) | ||||
|   let sortSelection = $state( | ||||
|     sortOptions.find( | ||||
|       (option) => | ||||
|         option.key == | ||||
|         ccconfig[`analysis_view_selectedTopCategory:${filterPresets.cluster}`], | ||||
|     ) || | ||||
|     sortOptions.find( | ||||
|       (option) => option.key == ccconfig.analysis_view_selectedTopCategory, | ||||
|     ) | ||||
|         ccconfig[`analysisView_selectedTopCategory:${filterPresets.cluster}`], | ||||
|     ) || sortOptions[0] | ||||
|   ); | ||||
|   let groupSelection = $state( | ||||
|       groupOptions.find( | ||||
|         (option) => | ||||
|           option.key == | ||||
|           ccconfig[`analysis_view_selectedTopEntity:${filterPresets.cluster}`], | ||||
|       ) || | ||||
|       groupOptions.find( | ||||
|         (option) => option.key == ccconfig.analysis_view_selectedTopEntity, | ||||
|       ) | ||||
|           ccconfig[`analysisView_selectedTopEntity:${filterPresets.cluster}`], | ||||
|       ) || groupOptions[0] | ||||
|   ); | ||||
|  | ||||
|   /* Init Function */ | ||||
| @@ -275,15 +269,15 @@ | ||||
|  | ||||
|   function updateEntityConfiguration(select) { | ||||
|     if ( | ||||
|       ccconfig[`analysis_view_selectedTopEntity:${filterPresets.cluster}`] != | ||||
|       ccconfig[`analysisView_selectedTopEntity:${filterPresets.cluster}`] != | ||||
|       select | ||||
|     ) { | ||||
|       updateConfigurationMutation({ | ||||
|         name: `analysis_view_selectedTopEntity:${filterPresets.cluster}`, | ||||
|         name: `analysisView_selectedTopEntity:${filterPresets.cluster}`, | ||||
|         value: JSON.stringify(select), | ||||
|       }).subscribe((res) => { | ||||
|         if (res.fetching === false && !res.error) { | ||||
|           // console.log(`analysis_view_selectedTopEntity:${filterPresets.cluster}` + ' -> Updated!') | ||||
|           // console.log(`analysisView_selectedTopEntity:${filterPresets.cluster}` + ' -> Updated!') | ||||
|         } else if (res.fetching === false && res.error) { | ||||
|           throw res.error; | ||||
|         } | ||||
| @@ -295,15 +289,15 @@ | ||||
|  | ||||
|   function updateCategoryConfiguration(select) { | ||||
|     if ( | ||||
|       ccconfig[`analysis_view_selectedTopCategory:${filterPresets.cluster}`] != | ||||
|       ccconfig[`analysisView_selectedTopCategory:${filterPresets.cluster}`] != | ||||
|       select | ||||
|     ) { | ||||
|       updateConfigurationMutation({ | ||||
|         name: `analysis_view_selectedTopCategory:${filterPresets.cluster}`, | ||||
|         name: `analysisView_selectedTopCategory:${filterPresets.cluster}`, | ||||
|         value: JSON.stringify(select), | ||||
|       }).subscribe((res) => { | ||||
|         if (res.fetching === false && !res.error) { | ||||
|           // console.log(`analysis_view_selectedTopCategory:${filterPresets.cluster}` + ' -> Updated!') | ||||
|           // console.log(`analysisView_selectedTopCategory:${filterPresets.cluster}` + ' -> Updated!') | ||||
|         } else if (res.fetching === false && res.error) { | ||||
|           throw res.error; | ||||
|         } | ||||
| @@ -591,7 +585,7 @@ | ||||
|             numBins, | ||||
|           ), | ||||
|         }))} | ||||
|         itemsPerRow={ccconfig.plot_view_plotsPerRow} | ||||
|         itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} | ||||
|         gridContent={histoGridContent} | ||||
|       /> | ||||
|     </Col> | ||||
| @@ -634,7 +628,7 @@ | ||||
|             (f) => f.metric == m2, | ||||
|           ).data, | ||||
|         }))} | ||||
|         itemsPerRow={ccconfig.plot_view_plotsPerRow} | ||||
|         itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} | ||||
|         gridContent={metricsGridContent} | ||||
|       /> | ||||
|     </Col> | ||||
|   | ||||
| @@ -69,6 +69,9 @@ | ||||
|   `); | ||||
|   const client = getContextClient(); | ||||
|   const ccconfig = getContext("cc-config"); | ||||
|   const showRoofline = !!ccconfig[`jobView_showRoofline`]; | ||||
|   const showStatsTable = !!ccconfig[`jobView_showStatTable`]; | ||||
|  | ||||
|   /* Note: Actual metric data queried in <Metric> Component, only require base infos here -> reduce backend load by requesting just stats */ | ||||
|   const query = gql` | ||||
|     query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) { | ||||
| @@ -168,8 +171,8 @@ | ||||
|     let job = $initq.data.job; | ||||
|     if (!job) return; | ||||
|     const pendingMetrics = ( | ||||
|       ccconfig[`job_view_selectedMetrics:${job.cluster}:${job.subCluster}`] || | ||||
|       ccconfig[`job_view_selectedMetrics:${job.cluster}`] | ||||
|       ccconfig[`metricConfig_jobViewPlotMetrics:${job.cluster}:${job.subCluster}`] || | ||||
|       ccconfig[`metricConfig_jobViewPlotMetrics:${job.cluster}`] | ||||
|     ) ||  | ||||
|     $initq.data.globalMetrics.reduce((names, gm) => { | ||||
|       if (gm.availability.find((av) => av.cluster === job.cluster && av.subClusters.includes(job.subCluster))) { | ||||
| @@ -232,7 +235,7 @@ | ||||
|           {/if} | ||||
|           <TabPane tabId="meta-info" tab="Job Info" active={$initq.data?.job?.metaData?.message?false:true}> | ||||
|             <CardBody class="pb-2"> | ||||
|               <JobInfo job={$initq.data.job} {username} {authlevel} {roles} showTagedit/> | ||||
|               <JobInfo job={$initq.data.job} {username} {authlevel} {roles} showTagEdit/> | ||||
|             </CardBody> | ||||
|           </TabPane> | ||||
|           {#if $initq.data.job.concurrentJobs != null && $initq.data.job.concurrentJobs.items.length != 0} | ||||
| @@ -268,7 +271,9 @@ | ||||
|     {#if $initq.error} | ||||
|       <Card body color="danger">{$initq.error.message}</Card> | ||||
|     {:else if $initq?.data} | ||||
|       <JobRoofline job={$initq.data.job} clusters={$initq.data.clusters}/> | ||||
|       {#if showRoofline} | ||||
|         <JobRoofline job={$initq.data.job} clusters={$initq.data.clusters}/> | ||||
|       {/if}     | ||||
|     {:else} | ||||
|       <Spinner secondary /> | ||||
|     {/if} | ||||
| @@ -354,14 +359,14 @@ | ||||
|           groupByScope($jobMetrics.data.scopedJobStats), | ||||
|           selectedMetrics, | ||||
|         )} | ||||
|         itemsPerRow={ccconfig.plot_view_plotsPerRow} | ||||
|         itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} | ||||
|         {gridContent} | ||||
|       /> | ||||
|     {/if} | ||||
|   </CardBody> | ||||
| </Card> | ||||
|  | ||||
| <!-- Statistcics Table --> | ||||
| <!-- Metadata && Statistcics Table --> | ||||
| <Row class="mb-3"> | ||||
|   <Col> | ||||
|     {#if $initq?.data} | ||||
| @@ -397,8 +402,10 @@ | ||||
|               </div> | ||||
|             </TabPane> | ||||
|           {/if} | ||||
|           <!-- Includes <TabPane> Statistics Table with Independent GQL Query --> | ||||
|           <StatsTab job={$initq.data.job} clusters={$initq.data.clusters} tabActive={!somethingMissing}/> | ||||
|           {#if showStatsTable} | ||||
|             <!-- Includes <TabPane> Statistics Table with Independent GQL Query --> | ||||
|             <StatsTab job={$initq.data.job} clusters={$initq.data.clusters} tabActive={!somethingMissing}/> | ||||
|           {/if} | ||||
|           <TabPane tabId="job-script" tab="Job Script"> | ||||
|             <div class="pre-wrapper"> | ||||
|               {#if $initq.data.job.metaData?.jobScript} | ||||
| @@ -432,7 +439,7 @@ | ||||
|     presetMetrics={selectedMetrics} | ||||
|     cluster={$initq.data.job.cluster} | ||||
|     subCluster={$initq.data.job.subCluster} | ||||
|     configName="job_view_selectedMetrics" | ||||
|     configName="metricConfig_jobViewPlotMetrics" | ||||
|     preInitialized | ||||
|     applyMetrics={(newMetrics) =>  | ||||
|       selectedMetrics = [...newMetrics] | ||||
|   | ||||
| @@ -53,13 +53,13 @@ | ||||
|   let sorting = $state({ field: "startTime", type: "col", order: "DESC" }); | ||||
|   let selectedCluster = $state(filterPresets?.cluster ? filterPresets.cluster : null); | ||||
|   let metrics = $state(filterPresets.cluster | ||||
|     ? ccconfig[`plot_list_selectedMetrics:${filterPresets.cluster}`] || | ||||
|       ccconfig.plot_list_selectedMetrics | ||||
|     : ccconfig.plot_list_selectedMetrics | ||||
|     ? ccconfig[`metricConfig_jobListMetrics:${filterPresets.cluster}`] || | ||||
|       ccconfig.metricConfig_jobListMetrics | ||||
|     : ccconfig.metricConfig_jobListMetrics | ||||
|   ); | ||||
|   let showFootprint = $state(filterPresets.cluster | ||||
|     ? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`] | ||||
|     : !!ccconfig.plot_list_showFootprint | ||||
|     ? !!ccconfig[`jobList_showFootprint:${filterPresets.cluster}`] | ||||
|     : !!ccconfig.jobList_showFootprint | ||||
|   ); | ||||
|  | ||||
|   /* Functions */ | ||||
| @@ -213,7 +213,7 @@ | ||||
|     bind:showFootprint | ||||
|     presetMetrics={metrics} | ||||
|     cluster={selectedCluster} | ||||
|     configName="plot_list_selectedMetrics" | ||||
|     configName="metricConfig_jobListMetrics" | ||||
|     footprintSelect | ||||
|     applyMetrics={(newMetrics) =>  | ||||
|       metrics = [...newMetrics] | ||||
|   | ||||
| @@ -136,7 +136,7 @@ | ||||
|       startTimeQuickSelect | ||||
|       bind:this={filterComponent} | ||||
|       {filterPresets} | ||||
|       menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up" | ||||
|       menuText="Only {type.toLowerCase()}s with matching jobs will be displayed." | ||||
|       applyFilters={(detail) => { | ||||
|         jobFilters = detail.filters; | ||||
|       }} | ||||
|   | ||||
| @@ -247,7 +247,7 @@ | ||||
|             ), | ||||
|           })) | ||||
|           .sort((a, b) => a.name.localeCompare(b.name))} | ||||
|         itemsPerRow={ccconfig.plot_view_plotsPerRow} | ||||
|         itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} | ||||
|         {gridContent} | ||||
|       /> | ||||
|     {/if} | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /*Const Init */ | ||||
|   const useCbColors = getContext("cc-config")?.plot_general_colorblindMode || false | ||||
|   const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false | ||||
|  | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,11 @@ | ||||
|     Icon, | ||||
|     Button, | ||||
|   } from "@sveltestrap/sveltestrap"; | ||||
|   import { | ||||
|     gql, | ||||
|     getContextClient, | ||||
|     mutationStore, | ||||
|   } from "@urql/svelte"; | ||||
|  | ||||
|   import { init } from "./generic/utils.js"; | ||||
|   import NodeOverview from "./systems/NodeOverview.svelte"; | ||||
| @@ -45,6 +50,7 @@ | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const { query: initq } = init(); | ||||
|   const client = getContextClient(); | ||||
|   const displayNodeOverview = (displayType === 'OVERVIEW'); | ||||
|   const ccconfig = getContext("cc-config"); | ||||
|   const initialized = getContext("initialized"); | ||||
| @@ -65,20 +71,38 @@ | ||||
|   let hostnameFilter = $state(""); | ||||
|   let pendingHostnameFilter = $state(""); | ||||
|   let isMetricsSelectionOpen = $state(false); | ||||
|   let selectedMetric = $state(ccconfig.system_view_selectedMetric || ""); | ||||
|   let selectedMetrics = $state(( | ||||
|     ccconfig[`node_list_selectedMetrics:${cluster}:${subCluster}`] || | ||||
|     ccconfig[`node_list_selectedMetrics:${cluster}`] | ||||
|   ) || [ccconfig.system_view_selectedMetric]); | ||||
|  | ||||
|   /* Derived States */ | ||||
|   const systemMetrics = $derived($initialized ? [...globalMetrics.filter((gm) => gm?.availability.find((av) => av.cluster == cluster))] : []); | ||||
|   const presetSystemUnits = $derived(loadUnits(systemMetrics)); | ||||
|   let selectedMetric = $derived.by(() => { | ||||
|     let configKey = `nodeOverview_selectedMetric`; | ||||
|     if (cluster) configKey += `:${cluster}`; | ||||
|     if (subCluster) configKey += `:${subCluster}`; | ||||
|  | ||||
|     if ($initialized) { | ||||
|       if (ccconfig[configKey]) return ccconfig[configKey] | ||||
|       else if (systemMetrics.length !== 0) return systemMetrics[0].name | ||||
|     } | ||||
|     return "" | ||||
|   }); | ||||
|   let selectedMetrics = $derived.by(() => { | ||||
|     let configKey = `nodeList_selectedMetrics`; | ||||
|     if (cluster) configKey += `:${cluster}`; | ||||
|     if (subCluster) configKey += `:${subCluster}`; | ||||
|  | ||||
|     if ($initialized) { | ||||
|       if (ccconfig[configKey]) return ccconfig[configKey] | ||||
|       else if (systemMetrics.length >= 3) return [systemMetrics[0].name, systemMetrics[1].name, systemMetrics[2].name] | ||||
|     } | ||||
|     return [] | ||||
|   }); | ||||
|  | ||||
|   /* Effects */ | ||||
|   $effect(() => { | ||||
|     // OnMount: Ping Var, without this, OVERVIEW metric select is empty (reason tbd)  | ||||
|     systemMetrics | ||||
|     if (displayNodeOverview) { | ||||
|       updateOverviewMetric(selectedMetric) | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   /* Functions */ | ||||
| @@ -99,6 +123,34 @@ | ||||
|       hostnameFilter = pendingHostnameFilter; | ||||
|     }, 500); | ||||
|   }; | ||||
|  | ||||
|   function updateOverviewMetric(newMetric) { | ||||
|     let configKey = `nodeOverview_selectedMetric`; | ||||
|     if (cluster) configKey += `:${cluster}`; | ||||
|     if (subCluster) configKey += `:${subCluster}`; | ||||
|  | ||||
|     updateConfigurationMutation({ | ||||
|       name: configKey, | ||||
|       value: JSON.stringify(newMetric), | ||||
|     }).subscribe((res) => { | ||||
|       if (res.fetching === false && res.error) { | ||||
|         throw res.error; | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   const updateConfigurationMutation = ({ name, value }) => { | ||||
|     return mutationStore({ | ||||
|       client: client, | ||||
|       query: gql` | ||||
|         mutation ($name: String!, $value: String!) { | ||||
|           updateConfiguration(name: $name, value: $value) | ||||
|         } | ||||
|       `, | ||||
|       variables: { name, value }, | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
| </script> | ||||
|  | ||||
| <!-- ROW1: Tools--> | ||||
| @@ -213,7 +265,7 @@ | ||||
|     presetMetrics={selectedMetrics} | ||||
|     {cluster} | ||||
|     {subCluster} | ||||
|     configName="node_list_selectedMetrics" | ||||
|     configName="nodeList_selectedMetrics" | ||||
|     applyMetrics={(newMetrics) =>  | ||||
|       selectedMetrics = [...newMetrics] | ||||
|     } | ||||
|   | ||||
| @@ -65,15 +65,15 @@ | ||||
|   let isMetricsSelectionOpen = $state(false); | ||||
|   let sorting = $state({ field: "startTime", type: "col", order: "DESC" }); | ||||
|   let selectedCluster = $state(filterPresets?.cluster ? filterPresets.cluster : null); | ||||
|   let selectedHistogramsBuffer = $state({ all: (ccconfig['user_view_histogramMetrics'] || []) }) | ||||
|   let selectedHistogramsBuffer = $state({ all: (ccconfig['userView_histogramMetrics'] || []) }) | ||||
|   let metrics = $state(filterPresets.cluster | ||||
|     ? ccconfig[`plot_list_selectedMetrics:${filterPresets.cluster}`] || | ||||
|       ccconfig.plot_list_selectedMetrics | ||||
|     : ccconfig.plot_list_selectedMetrics | ||||
|     ? ccconfig[`metricConfig_jobListMetrics:${filterPresets.cluster}`] || | ||||
|       ccconfig.metricConfig_jobListMetrics | ||||
|     : ccconfig.metricConfig_jobListMetrics | ||||
|   ); | ||||
|   let showFootprint = $state(filterPresets.cluster | ||||
|     ? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`] | ||||
|     : !!ccconfig.plot_list_showFootprint | ||||
|     ? !!ccconfig[`jobList_showFootprint:${filterPresets.cluster}`] | ||||
|     : !!ccconfig.jobList_showFootprint | ||||
|   ); | ||||
|  | ||||
|   // Histogram Vars | ||||
| @@ -129,7 +129,7 @@ | ||||
|   /* Effect */ | ||||
|   $effect(() => { | ||||
|     if (!selectedHistogramsBuffer[selectedCluster]) { | ||||
|       selectedHistogramsBuffer[selectedCluster] = ccconfig[`user_view_histogramMetrics:${selectedCluster}`]; | ||||
|       selectedHistogramsBuffer[selectedCluster] = ccconfig[`userView_histogramMetrics:${selectedCluster}`]; | ||||
|     }; | ||||
|   }); | ||||
|  | ||||
| @@ -138,7 +138,7 @@ | ||||
|     filterComponent.updateFilters(); | ||||
|     // Why? -> `$derived(ccconfig[$cluster])` only loads array from last Backend-Query if $cluster changed reactively (without reload) | ||||
|     if (filterPresets?.cluster) { | ||||
|       selectedHistogramsBuffer[filterPresets.cluster] = ccconfig[`user_view_histogramMetrics:${filterPresets.cluster}`]; | ||||
|       selectedHistogramsBuffer[filterPresets.cluster] = ccconfig[`userView_histogramMetrics:${filterPresets.cluster}`]; | ||||
|     }; | ||||
|   }); | ||||
| </script> | ||||
| @@ -393,7 +393,7 @@ | ||||
|     bind:showFootprint | ||||
|     presetMetrics={metrics} | ||||
|     cluster={selectedCluster} | ||||
|     configName="plot_list_selectedMetrics" | ||||
|     configName="metricConfig_jobListMetrics" | ||||
|     footprintSelect | ||||
|     applyMetrics={(newMetrics) =>  | ||||
|       metrics = [...newMetrics] | ||||
| @@ -404,7 +404,7 @@ | ||||
|   cluster={selectedCluster} | ||||
|   bind:isOpen={isHistogramSelectionOpen} | ||||
|   presetSelectedHistograms={selectedHistograms} | ||||
|   configName="user_view_histogramMetrics" | ||||
|   configName="userView_histogramMetrics" | ||||
|   applyChange={(newSelection) => { | ||||
|     selectedHistogramsBuffer[selectedCluster || 'all'] = [...newSelection]; | ||||
|   }} | ||||
|   | ||||
| @@ -92,7 +92,7 @@ | ||||
|             value={metric} | ||||
|             onchange={() => { | ||||
|               updateConfiguration({ | ||||
|                 name: "analysis_view_histogramMetrics", | ||||
|                 name: "analysisView_histogramMetrics", | ||||
|                 value: metricsInHistograms, | ||||
|               }); | ||||
|               applyHistograms(metricsInHistograms); | ||||
| @@ -131,7 +131,7 @@ | ||||
|                 (p) => pair != p, | ||||
|               ); | ||||
|               updateConfiguration({ | ||||
|                 name: "analysis_view_scatterPlotMetrics", | ||||
|                 name: "analysisView_scatterPlotMetrics", | ||||
|                 value: metricsInScatterplots, | ||||
|               }); | ||||
|               applyScatter(metricsInScatterplots); | ||||
| @@ -169,7 +169,7 @@ | ||||
|           selectedMetric1 = null; | ||||
|           selectedMetric2 = null; | ||||
|           updateConfiguration({ | ||||
|             name: "analysis_view_scatterPlotMetrics", | ||||
|             name: "analysisView_scatterPlotMetrics", | ||||
|             value: metricsInScatterplots, | ||||
|           }); | ||||
|           applyScatter(metricsInScatterplots); | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|   /* State Init */ | ||||
|   let message = $state({ msg: "", target: "", color: "#d63384" }); | ||||
|   let displayMessage = $state(false); | ||||
|   let cbmode = $state(ccconfig?.plot_general_colorblindMode || false); | ||||
|   let cbmode = $state(ccconfig?.plotConfiguration_colorblindMode || false); | ||||
|  | ||||
|   /* Functions */ | ||||
|   async function handleSettingSubmit(event, setting) { | ||||
| @@ -38,7 +38,7 @@ | ||||
|       const res = await fetch(form.action, { method: "POST", body: formData }); | ||||
|       if (res.ok) { | ||||
|         let text = await res.text(); | ||||
|         if (formData.get("key") === "plot_general_colorblindMode") { | ||||
|         if (formData.get("key") === "plotConfiguration_colorblindMode") { | ||||
|           cbmode = JSON.parse(formData.get("value")); | ||||
|         } | ||||
|         popMessage(text, target, "#048109"); | ||||
|   | ||||
| @@ -69,10 +69,10 @@ | ||||
|           method="post" | ||||
|           action="/frontend/configuration/" | ||||
|           onsubmit={(e) => handleSettingSubmit(e, "#node-paging-form", "npag")}> | ||||
|           <input type="hidden" name="key" value="node_list_usePaging" /> | ||||
|           <input type="hidden" name="key" value="nodeList_usePaging" /> | ||||
|           <div class="mb-3"> | ||||
|             <div> | ||||
|               {#if config?.node_list_usePaging} | ||||
|               {#if config?.nodeList_usePaging} | ||||
|                 <input type="radio" id="nodes-true-checked" name="value" value="true" checked /> | ||||
|               {:else} | ||||
|                 <input type="radio" id="nodes-true" name="value" value="true" /> | ||||
| @@ -80,7 +80,7 @@ | ||||
|               <label for="nodes-true">Paging with selectable count of nodes.</label> | ||||
|             </div> | ||||
|             <div> | ||||
|               {#if config?.node_list_usePaging} | ||||
|               {#if config?.nodeList_usePaging} | ||||
|                 <input type="radio" id="nodes-false" name="value" value="false" /> | ||||
|               {:else} | ||||
|                 <input type="radio" id="nodes-false-checked" name="value" value="false" checked /> | ||||
|   | ||||
| @@ -29,10 +29,10 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /* State Init */ | ||||
|   let activeRow = $state(JSON.stringify(config?.plot_general_colorscheme)); | ||||
|   let activeRow = $state(JSON.stringify(config?.plotConfiguration_colorScheme)); | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const colorschemes = { | ||||
|   const colorSchemes = { | ||||
|     Default: [ | ||||
|       "#00bfff", | ||||
|       "#0000ff", | ||||
| @@ -266,7 +266,7 @@ | ||||
|  | ||||
|   // https://personal.sron.nl/~pault/ | ||||
|   // https://tsitsul.in/blog/coloropt/ | ||||
|   const cvdschemes = { | ||||
|   const cvdSchemes = { | ||||
|     HighContrast: [ | ||||
|       "rgb(221,170,51)", | ||||
|       "rgb(187,85,102)", | ||||
| @@ -344,11 +344,11 @@ | ||||
|             </div> | ||||
|           {/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="plot_general_colorscheme" /> | ||||
|         <input type="hidden" name="key" value="plotConfiguration_colorScheme" /> | ||||
|         <Table hover> | ||||
|           <tbody> | ||||
|             {#key activeRow} | ||||
|               {#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]} | ||||
|               {#each Object.entries(cbmode ? cvdSchemes : colorSchemes) as [name, rgbrow]} | ||||
|                 <tr> | ||||
|                   <th scope="col">{name}</th> | ||||
|                   <td> | ||||
|   | ||||
| @@ -55,7 +55,7 @@ | ||||
|             </div> | ||||
|           {/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="plot_general_lineWidth" /> | ||||
|         <input type="hidden" name="key" value="plotConfiguration_lineWidth" /> | ||||
|         <div class="mb-3"> | ||||
|           <label for="value" class="form-label">Line Width</label> | ||||
|           <input | ||||
| @@ -64,7 +64,7 @@ | ||||
|             id="lwvalue" | ||||
|             name="value" | ||||
|             aria-describedby="lineWidthHelp" | ||||
|             value={config.plot_general_lineWidth} | ||||
|             value={config.plotConfiguration_lineWidth} | ||||
|             min="1" | ||||
|           /> | ||||
|           <div id="lineWidthHelp" class="form-text"> | ||||
| @@ -102,7 +102,7 @@ | ||||
|             </div> | ||||
|           {/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="plot_view_plotsPerRow" /> | ||||
|         <input type="hidden" name="key" value="plotConfiguration_plotsPerRow" /> | ||||
|         <div class="mb-3"> | ||||
|           <label for="value" class="form-label">Plots per Row</label> | ||||
|           <input | ||||
| @@ -111,7 +111,7 @@ | ||||
|             id="pprvalue" | ||||
|             name="value" | ||||
|             aria-describedby="plotsperrowHelp" | ||||
|             value={config.plot_view_plotsPerRow} | ||||
|             value={config.plotConfiguration_plotsPerRow} | ||||
|             min="1" | ||||
|           /> | ||||
|           <div id="plotsperrowHelp" class="form-text"> | ||||
| @@ -150,10 +150,10 @@ | ||||
|             </div> | ||||
|           {/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="plot_general_colorBackground" /> | ||||
|         <input type="hidden" name="key" value="plotConfiguration_colorBackground" /> | ||||
|         <div class="mb-3"> | ||||
|           <div> | ||||
|             {#if config.plot_general_colorBackground} | ||||
|             {#if config.plotConfiguration_colorBackground} | ||||
|               <input type="radio" id="colb-true-checked" name="value" value="true" checked /> | ||||
|             {:else} | ||||
|               <input type="radio" id="colb-true" name="value" value="true" /> | ||||
| @@ -161,7 +161,7 @@ | ||||
|             <label for="true">Yes</label> | ||||
|           </div> | ||||
|           <div> | ||||
|             {#if config.plot_general_colorBackground} | ||||
|             {#if config.plotConfiguration_colorBackground} | ||||
|               <input type="radio" id="colb-false" name="value" value="false" /> | ||||
|             {:else} | ||||
|               <input type="radio" id="colb-false-checked" name="value" value="false" checked /> | ||||
| @@ -196,10 +196,10 @@ | ||||
|             </div> | ||||
|           {/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="plot_general_colorblindMode" /> | ||||
|         <input type="hidden" name="key" value="plotConfiguration_colorblindMode" /> | ||||
|         <div class="mb-3"> | ||||
|           <div> | ||||
|             {#if config?.plot_general_colorblindMode} | ||||
|             {#if config?.plotConfiguration_colorblindMode} | ||||
|               <input type="radio" id="cbm-true-checked" name="value" value="true" checked /> | ||||
|             {:else} | ||||
|               <input type="radio" id="cbm-true" name="value" value="true" /> | ||||
| @@ -207,7 +207,7 @@ | ||||
|             <label for="true">Yes</label> | ||||
|           </div> | ||||
|           <div> | ||||
|             {#if config?.plot_general_colorblindMode} | ||||
|             {#if config?.plotConfiguration_colorblindMode} | ||||
|               <input type="radio" id="cbm-false" name="value" value="false" /> | ||||
|             {:else} | ||||
|               <input type="radio" id="cbm-false-checked" name="value" value="false" checked /> | ||||
|   | ||||
| @@ -104,10 +104,10 @@ | ||||
|               > | ||||
|             </div>{/if} | ||||
|         </CardTitle> | ||||
|         <input type="hidden" name="key" value="job_list_usePaging" /> | ||||
|         <input type="hidden" name="key" value="jobList_usePaging" /> | ||||
|         <div class="mb-3"> | ||||
|           <div> | ||||
|             {#if config?.job_list_usePaging} | ||||
|             {#if config?.jobList_usePaging} | ||||
|               <input type="radio" id="true-checked" name="value" value="true" checked /> | ||||
|             {:else} | ||||
|               <input type="radio" id="true" name="value" value="true" /> | ||||
| @@ -115,7 +115,7 @@ | ||||
|             <label for="true">Paging with selectable count of jobs.</label> | ||||
|           </div> | ||||
|           <div> | ||||
|             {#if config?.job_list_usePaging} | ||||
|             {#if config?.jobList_usePaging} | ||||
|               <input type="radio" id="false" name="value" value="false" /> | ||||
|             {:else} | ||||
|               <input type="radio" id="false-checked" name="value" value="false" checked /> | ||||
|   | ||||
| @@ -294,11 +294,12 @@ | ||||
|         Filters | ||||
|       </DropdownToggle> | ||||
|       <DropdownMenu> | ||||
|         <DropdownItem header>Manage Filters</DropdownItem> | ||||
|         {#if menuText} | ||||
|           <DropdownItem header>Note</DropdownItem> | ||||
|           <DropdownItem disabled>{menuText}</DropdownItem> | ||||
|           <DropdownItem divider /> | ||||
|         {/if} | ||||
|         <DropdownItem header>Manage Filters</DropdownItem> | ||||
|         <DropdownItem onclick={() => (isClusterOpen = true)}> | ||||
|           <Icon name="cpu" /> Cluster/Partition | ||||
|         </DropdownItem> | ||||
| @@ -325,7 +326,7 @@ | ||||
|         </DropdownItem> | ||||
|         {#if startTimeQuickSelect} | ||||
|           <DropdownItem divider /> | ||||
|           <DropdownItem disabled>Start Time Quick Selection</DropdownItem> | ||||
|           <DropdownItem header>Start Time Quick Selection</DropdownItem> | ||||
|           {#each startTimeSelectOptions.filter((stso) => stso.range !== "") as { rangeLabel, range }} | ||||
|             <DropdownItem | ||||
|               onclick={() => { | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
|   /* Svelte 5 Props */ | ||||
|   let { | ||||
|     matchedCompareJobs = $bindable(0), | ||||
|     metrics = getContext("cc-config")?.plot_list_selectedMetrics, | ||||
|     metrics = getContext("cc-config")?.metricConfig_jobListMetrics, | ||||
|     filterBuffer = [], | ||||
|   } = $props(); | ||||
|  | ||||
| @@ -112,7 +112,7 @@ | ||||
|   // (Re-)query and optionally set new filters; Query will be started reactively. | ||||
|   export function queryJobs(filters) { | ||||
|     if (filters != null) { | ||||
|       let minRunningFor = ccconfig.plot_list_hideShortRunningJobs; | ||||
|       let minRunningFor = ccconfig.jobList_hideShortRunningJobs; | ||||
|       if (minRunningFor && minRunningFor > 0) { | ||||
|         filters.push({ minRunningFor }); | ||||
|       } | ||||
| @@ -229,7 +229,7 @@ | ||||
|  | ||||
|   // function updateConfiguration(value, page) { | ||||
|   //   updateConfigurationMutation({ | ||||
|   //     name: "plot_list_jobsPerPage", | ||||
|   //     name: "jobList_jobsPerPage", | ||||
|   //     value: value, | ||||
|   //   }).subscribe((res) => { | ||||
|   //     if (res.fetching === false && !res.error) { | ||||
|   | ||||
| @@ -32,7 +32,7 @@ | ||||
|   let { | ||||
|     matchedListJobs = $bindable(0), | ||||
|     selectedJobs = $bindable([]), | ||||
|     metrics = getContext("cc-config").plot_list_selectedMetrics, | ||||
|     metrics = getContext("cc-config").metricConfig_jobListMetrics, | ||||
|     sorting = { field: "startTime", type: "col", order: "DESC" }, | ||||
|     showFootprint = false, | ||||
|     filterBuffer = [], | ||||
| @@ -42,7 +42,7 @@ | ||||
|   const ccconfig = getContext("cc-config"); | ||||
|   const initialized = getContext("initialized"); | ||||
|   const globalMetrics = getContext("globalMetrics"); | ||||
|   const usePaging = ccconfig?.job_list_usePaging || false; | ||||
|   const usePaging = ccconfig?.jobList_usePaging || false; | ||||
|   const jobInfoColumnWidth = 250; | ||||
|   const client = getContextClient(); | ||||
|   const query = gql` | ||||
| @@ -101,7 +101,7 @@ | ||||
|   let jobs = $state([]); | ||||
|   let filter = $state([...filterBuffer]); | ||||
|   let page = $state(1); | ||||
|   let itemsPerPage = $state(usePaging ? (ccconfig?.plot_list_jobsPerPage || 10) : 10); | ||||
|   let itemsPerPage = $state(usePaging ? (ccconfig?.jobList_jobsPerPage || 10) : 10); | ||||
|   let triggerMetricRefresh = $state(false); | ||||
|   let tableWidth = $state(0); | ||||
|  | ||||
| @@ -177,7 +177,7 @@ | ||||
|   // (Re-)query and optionally set new filters; Query will be started reactively. | ||||
|   export function queryJobs(filters) { | ||||
|     if (filters != null) { | ||||
|       let minRunningFor = ccconfig.plot_list_hideShortRunningJobs; | ||||
|       let minRunningFor = ccconfig.jobList_hideShortRunningJobs; | ||||
|       if (minRunningFor && minRunningFor > 0) { | ||||
|         filters.push({ minRunningFor }); | ||||
|       } | ||||
| @@ -207,7 +207,7 @@ | ||||
|  | ||||
|   function updateConfiguration(value, newPage) { | ||||
|     updateConfigurationMutation({ | ||||
|       name: "plot_list_jobsPerPage", | ||||
|       name: "jobList_jobsPerPage", | ||||
|       value: value.toString(), | ||||
|     }).subscribe((res) => { | ||||
|       if (res.fetching === false && !res.error) { | ||||
| @@ -236,10 +236,6 @@ | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   const equalsCheck = (a, b) => { | ||||
|     return JSON.stringify(a) === JSON.stringify(b); | ||||
|   } | ||||
|  | ||||
|   /* Init Header */ | ||||
|   stickyHeader( | ||||
|     ".cc-table-wrapper > table.table >thead > tr > th.position-sticky:nth-child(1)", | ||||
|   | ||||
| @@ -170,7 +170,7 @@ | ||||
|  | ||||
| <tr> | ||||
|   <td> | ||||
|     <JobInfo {job} bind:isSelected showSelect/> | ||||
|     <JobInfo {job} bind:isSelected showJobSelect/> | ||||
|   </td> | ||||
|   {#if job.monitoringStatus == 0 || job.monitoringStatus == 2} | ||||
|     <td colspan={metrics.length}> | ||||
|   | ||||
| @@ -44,8 +44,8 @@ | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const clusterCockpitConfig = getContext("cc-config"); | ||||
|   const lineWidth = clusterCockpitConfig?.plot_general_lineWidth / window.devicePixelRatio || 2; | ||||
|   const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false; | ||||
|   const lineWidth = clusterCockpitConfig?.plotConfiguration_lineWidth / window.devicePixelRatio || 2; | ||||
|   const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; | ||||
|  | ||||
|   // UPLOT SERIES INIT // | ||||
|   const plotSeries = [ | ||||
|   | ||||
| @@ -56,9 +56,9 @@ | ||||
|   const resampleConfig = getContext("resampling"); | ||||
|   const subClusterTopology = getContext("getHardwareTopology")(cluster, subCluster); | ||||
|   const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric); | ||||
|   const lineWidth = clusterCockpitConfig?.plot_general_lineWidth / window.devicePixelRatio || 2; | ||||
|   const lineColors = clusterCockpitConfig?.plot_general_colorscheme || ["#00bfff","#0000ff","#ff00ff","#ff0000","#ff8000","#ffff00","#80ff00"]; | ||||
|   const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false; | ||||
|   const lineColors = clusterCockpitConfig.plotConfiguration_colorScheme; | ||||
|   const lineWidth = clusterCockpitConfig.plotConfiguration_lineWidth / window.devicePixelRatio; | ||||
|   const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; | ||||
|   const renderSleepTime = 200; | ||||
|   const normalLineColor = "#000000"; | ||||
|   const backgroundColors = { | ||||
| @@ -416,7 +416,7 @@ | ||||
|   // RETURN BG COLOR FROM THRESHOLD | ||||
|   function backgroundColor() { | ||||
|     if ( | ||||
|       clusterCockpitConfig.plot_general_colorBackground == false || | ||||
|       clusterCockpitConfig.plotConfiguration_colorBackground == false || | ||||
|       !thresholds || | ||||
|       !(series && series.every((s) => s.statistics != null)) | ||||
|     ) | ||||
|   | ||||
| @@ -80,7 +80,7 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const useCbColors = getContext("cc-config")?.plot_general_colorblindMode || false | ||||
|   const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false | ||||
|   const options = {  | ||||
|     maintainAspectRatio: false, | ||||
|     animation: false, | ||||
|   | ||||
| @@ -41,8 +41,8 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const lineWidth = clusterCockpitConfig?.plot_general_lineWidth || 2; | ||||
|   const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false; | ||||
|   const lineWidth = clusterCockpitConfig.plotConfiguration_lineWidth; | ||||
|   const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; | ||||
|  | ||||
|   /* Var Init */ | ||||
|   let timeoutId = null; | ||||
|   | ||||
| @@ -39,8 +39,8 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const lineWidth = clusterCockpitConfig?.plot_general_lineWidth || 2; | ||||
|   const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false; | ||||
|   const lineWidth = clusterCockpitConfig.plotConfiguration_lineWidth; | ||||
|   const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; | ||||
|  | ||||
|   /* Var Init */ | ||||
|   let timeoutId = null; | ||||
|   | ||||
| @@ -57,12 +57,12 @@ | ||||
|   }; | ||||
|  | ||||
|   /* State Init */ | ||||
|   let pendingMetrics = $state(presetMetrics); | ||||
|   let pendingShowFootprint = $state(!!showFootprint); | ||||
|   let listedMetrics = $state([]); | ||||
|   let columnHovering = $state(null); | ||||
|  | ||||
|   /* Derives States */ | ||||
|   let pendingMetrics = $derived(presetMetrics); | ||||
|   const allMetrics = $derived(loadAvailable(preInitialized || $initialized)); | ||||
|  | ||||
|   /* Reactive Effects */ | ||||
| @@ -151,8 +151,8 @@ | ||||
|       updateConfigurationMutation({ | ||||
|         name: | ||||
|           !cluster | ||||
|             ? "plot_list_showFootprint" | ||||
|             : `plot_list_showFootprint:${cluster}`, | ||||
|             ? "jobList_showFootprint" | ||||
|             : `jobList_showFootprint:${cluster}`, | ||||
|         value: JSON.stringify(showFootprint), | ||||
|       }).subscribe((res) => { | ||||
|         if (res.fetching === false && res.error) { | ||||
|   | ||||
| @@ -51,4 +51,8 @@ export function formatTime(t, forNode = false) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| // const equalsCheck = (a, b) => { | ||||
| //   return JSON.stringify(a) === JSON.stringify(b); | ||||
| // } | ||||
|  | ||||
| // export const dateToUnixEpoch = (rfc3339) => Math.floor(Date.parse(rfc3339) / 1000); | ||||
|   | ||||
| @@ -25,20 +25,23 @@ | ||||
|   } = $props(); | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const showFootprintTab = !!getContext("cc-config")[`job_view_showFootprint`]; | ||||
|   const showFootprintTab = !!getContext("cc-config")[`jobView_showFootprint`]; | ||||
|   const showPolarTab = !!getContext("cc-config")[`jobView_showPolarPlot`]; | ||||
| </script> | ||||
|  | ||||
| <Card class="overflow-auto" style="width: {width}; height: {height}"> | ||||
|   <TabContent> | ||||
|     {#if showFootprintTab} | ||||
|       <TabPane tabId="foot" tab="Footprint" active> | ||||
|       <TabPane tabId="foot" tab="Footprint" active={showFootprintTab}> | ||||
|         <!-- Bars CardBody Here--> | ||||
|         <JobFootprintBars {job} /> | ||||
|       </TabPane> | ||||
|     {/if} | ||||
|     <TabPane tabId="polar" tab="Polar" active={!showFootprintTab}> | ||||
|       <!-- Polar Plot CardBody Here --> | ||||
|        <JobFootprintPolar {job} /> | ||||
|     </TabPane> | ||||
|     {#if showPolarTab} | ||||
|       <TabPane tabId="polar" tab="Polar" active={showPolarTab && !showFootprintTab}> | ||||
|         <!-- Polar Plot CardBody Here --> | ||||
|         <JobFootprintPolar {job} /> | ||||
|       </TabPane> | ||||
|     {/if} | ||||
|   </TabContent> | ||||
| </Card> | ||||
|   | ||||
| @@ -81,9 +81,9 @@ | ||||
|     if (!job) return; | ||||
|  | ||||
|     const pendingMetrics = ( | ||||
|       getContext("cc-config")[`job_view_nodestats_selectedMetrics:${job.cluster}:${job.subCluster}`] || | ||||
|       getContext("cc-config")[`job_view_nodestats_selectedMetrics:${job.cluster}`] | ||||
|     ) || getContext("cc-config")["job_view_nodestats_selectedMetrics"]; | ||||
|       getContext("cc-config")[`metricConfig_jobViewTableMetrics:${job.cluster}:${job.subCluster}`] || | ||||
|       getContext("cc-config")[`metricConfig_jobViewTableMetrics:${job.cluster}`] | ||||
|     ) || getContext("cc-config")["metricConfig_jobViewTableMetrics"]; | ||||
|  | ||||
|     // Select default Scopes to load: Check before if any metric has accelerator scope by default | ||||
|     const accScopeDefault = [...pendingMetrics].some(function (m) { | ||||
| @@ -152,7 +152,7 @@ | ||||
|   presetMetrics={selectedMetrics} | ||||
|   cluster={job.cluster} | ||||
|   subCluster={job.subCluster} | ||||
|   configName="job_view_nodestats_selectedMetrics" | ||||
|   configName="metricConfig_jobViewTableMetrics" | ||||
|   preInitialized | ||||
|   applyMetrics={(newMetrics) =>  | ||||
|     selectedMetrics = [...newMetrics] | ||||
|   | ||||
| @@ -48,8 +48,8 @@ | ||||
|  | ||||
|   /* Derived */ | ||||
|   let selectedHistograms = $derived(cluster | ||||
|     ? ccconfig[`status_view_selectedHistograms:${cluster}`] || ( ccconfig['status_view_selectedHistograms'] || [] ) | ||||
|     : ccconfig['status_view_selectedHistograms'] || []); | ||||
|     ? ccconfig[`statusView_selectedHistograms:${cluster}`] || ( ccconfig['statusView_selectedHistograms'] || [] ) | ||||
|     : ccconfig['statusView_selectedHistograms'] || []); | ||||
|  | ||||
|   // Note: nodeMetrics are requested on configured $timestep resolution | ||||
|   const metricStatusQuery = $derived(queryStore({ | ||||
| @@ -152,7 +152,7 @@ | ||||
|   {cluster} | ||||
|   bind:isOpen={isHistogramSelectionOpen} | ||||
|   presetSelectedHistograms={selectedHistograms} | ||||
|   configName="status_view_selectedHistograms" | ||||
|   configName="statusView_selectedHistograms" | ||||
|   applyChange={(newSelection) => { | ||||
|     selectedHistograms = [...newSelection]; | ||||
|   }} | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|  | ||||
|   /* Const Init */ | ||||
|   const client = getContextClient(); | ||||
|   const usePaging = ccconfig?.node_list_usePaging || false; | ||||
|   const usePaging = ccconfig?.nodeList_usePaging || false; | ||||
|   const nodeListQuery = gql` | ||||
|     query ($cluster: String!, $subCluster: String!, $nodeFilter: String!, $metrics: [String!], $scopes: [MetricScope!]!, $from: Time!, $to: Time!, $paging: PageRequest!, $selectedResolution: Int) { | ||||
|       nodeMetricsList( | ||||
| @@ -88,7 +88,7 @@ | ||||
|   /* State Init */ | ||||
|   let nodes = $state([]); | ||||
|   let page = $state(1); | ||||
|   let itemsPerPage = $state(usePaging ? (ccconfig?.plot_list_nodesPerPage || 10) : 10); | ||||
|   let itemsPerPage = $state(usePaging ? (ccconfig?.nodeList_nodesPerPage || 10) : 10); | ||||
|   let headerPaddingTop = $state(0); | ||||
|   let matchedNodes = $state(0); | ||||
|  | ||||
| @@ -165,7 +165,7 @@ | ||||
|   // Decouple from Job List Paging Params? | ||||
|   function updateConfiguration(newItems, newPage) { | ||||
|     updateConfigurationMutation({ | ||||
|       name: "plot_list_nodesPerPage", | ||||
|       name: "nodeList_nodesPerPage", | ||||
|       value: newItems.toString(), | ||||
|     }).subscribe((res) => { | ||||
|       if (res.fetching === false && !res.error) { | ||||
|   | ||||
| @@ -121,7 +121,7 @@ | ||||
|   </Row> | ||||
| {:else if filteredData?.length > 0} | ||||
|   <!-- PlotGrid flattened into this component --> | ||||
|   <Row cols={{ xs: 1, sm: 2, md: 3, lg: ccconfig.plot_view_plotsPerRow}}> | ||||
|   <Row cols={{ xs: 1, sm: 2, md: 3, lg: ccconfig.plotConfiguration_plotsPerRow}}> | ||||
|     {#key selectedMetric} | ||||
|       {#each filteredData as item (item.host)} | ||||
|         <Col class="px-1"> | ||||
|   | ||||
							
								
								
									
										30
									
								
								web/web.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								web/web.go
									
									
									
									
									
								
							| @@ -11,7 +11,9 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"html/template" | ||||
| 	"io/fs" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" | ||||
| @@ -109,16 +111,24 @@ var UIDefaultsMap map[string]any | ||||
| // 	"analysis_view_scatterPlotMetrics":       [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}}, | ||||
| // 	"job_view_nodestats_selectedMetrics":     []string{"flops_any", "mem_bw", "mem_used"}, | ||||
| // 	"plot_list_jobsPerPage":                  50, | ||||
| // 	"system_view_selectedMetric":             "cpu_load", | ||||
| // 	"analysis_view_selectedTopEntity":        "user", | ||||
| // 	"analysis_view_selectedTopCategory":      "totalWalltime", | ||||
| // 	"status_view_selectedTopUserCategory":    "totalJobs", | ||||
| // 	"status_view_selectedTopProjectCategory": "totalJobs", | ||||
| // } | ||||
|  | ||||
| func Init(rawConfig json.RawMessage) error { | ||||
| 	var err error | ||||
| func Init(configFilePath string) error { | ||||
| 	var rawConfig json.RawMessage = nil | ||||
| 	raw, rerr := os.ReadFile(configFilePath) | ||||
| 	if rerr != nil { | ||||
| 		if !os.IsNotExist(rerr) { | ||||
| 			log.Fatalf("UI-CONFIG ERROR: %v", rerr) | ||||
| 		} | ||||
| 	} else { | ||||
| 		rawConfig = json.RawMessage(raw) | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
| 	if rawConfig != nil { | ||||
| 		config.Validate(configSchema, rawConfig) | ||||
| 		if err = json.Unmarshal(rawConfig, &UIDefaults); err != nil { | ||||
| @@ -129,13 +139,13 @@ func Init(rawConfig json.RawMessage) error { | ||||
|  | ||||
| 	UIDefaultsMap = make(map[string]any) | ||||
|  | ||||
| 	UIDefaultsMap["joblist_usePaging"] = UIDefaults.JobList.UsePaging | ||||
| 	UIDefaultsMap["joblist_showFootprint"] = UIDefaults.JobList.ShowFootprint | ||||
| 	UIDefaultsMap["nodelist_usePaging"] = UIDefaults.NodeList.UsePaging | ||||
| 	UIDefaultsMap["jobview_showPolarPlot"] = UIDefaults.JobView.ShowPolarPlot | ||||
| 	UIDefaultsMap["jobview_showFootprint"] = UIDefaults.JobView.ShowFootprint | ||||
| 	UIDefaultsMap["jobview_showRoofline"] = UIDefaults.JobView.ShowRoofline | ||||
| 	UIDefaultsMap["jobview_showStatTable"] = UIDefaults.JobView.ShowStatTable | ||||
| 	UIDefaultsMap["jobList_usePaging"] = UIDefaults.JobList.UsePaging | ||||
| 	UIDefaultsMap["jobList_showFootprint"] = UIDefaults.JobList.ShowFootprint | ||||
| 	UIDefaultsMap["nodeList_usePaging"] = UIDefaults.NodeList.UsePaging | ||||
| 	UIDefaultsMap["jobView_showPolarPlot"] = UIDefaults.JobView.ShowPolarPlot | ||||
| 	UIDefaultsMap["jobView_showFootprint"] = UIDefaults.JobView.ShowFootprint | ||||
| 	UIDefaultsMap["jobView_showRoofline"] = UIDefaults.JobView.ShowRoofline | ||||
| 	UIDefaultsMap["jobView_showStatTable"] = UIDefaults.JobView.ShowStatTable | ||||
|  | ||||
| 	UIDefaultsMap["metricConfig_jobListMetrics"] = UIDefaults.MetricConfig.JobListMetrics | ||||
| 	UIDefaultsMap["metricConfig_jobViewPlotMetrics"] = UIDefaults.MetricConfig.JobViewPlotMetrics | ||||
|   | ||||
		Reference in New Issue
	
	Block a user