diff --git a/.gitignore b/.gitignore index 963073d..840f607 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /cc-backend /.env /config.json +/uiConfig.json /var/job-archive /var/machine-state diff --git a/cmd/cc-backend/cli.go b/cmd/cc-backend/cli.go index 235a12c..72a0a6d 100644 --- a/cmd/cc-backend/cli.go +++ b/cmd/cc-backend/cli.go @@ -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: :[admin,support,manager,api,user]:") flag.StringVar(&flagDelUser, "del-user", "", "Remove a existing user. Argument format: ") flag.StringVar(&flagGenJWT, "jwt", "", "Generate and print a JWT for the user specified by its `username`") diff --git a/cmd/cc-backend/server.go b/cmd/cc-backend/server.go index deffb48..da14e1e 100644 --- a/cmd/cc-backend/server.go +++ b/cmd/cc-backend/server.go @@ -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() diff --git a/configs/default_metrics.json b/configs/default_metrics.json deleted file mode 100644 index 7c392cc..0000000 --- a/configs/default_metrics.json +++ /dev/null @@ -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" - } - ] -} diff --git a/configs/uiConfig.json b/configs/uiConfig.json new file mode 100644 index 0000000..2a3ed30 --- /dev/null +++ b/configs/uiConfig.json @@ -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" + ] + } +} diff --git a/internal/config/default_metrics.go b/internal/config/default_metrics.go index f8a0e34..d710aa0 100644 --- a/internal/config/default_metrics.go +++ b/internal/config/default_metrics.go @@ -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"` diff --git a/internal/repository/user.go b/internal/repository/user.go index 1dca7f4..5cab2b0 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -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 } diff --git a/internal/repository/userConfig.go b/internal/repository/userConfig.go index 744c2ea..beeffbf 100644 --- a/internal/repository/userConfig.go +++ b/internal/repository/userConfig.go @@ -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 }) diff --git a/web/frontend/src/Analysis.root.svelte b/web/frontend/src/Analysis.root.svelte index 122a67b..8d9972a 100644 --- a/web/frontend/src/Analysis.root.svelte +++ b/web/frontend/src/Analysis.root.svelte @@ -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} /> @@ -634,7 +628,7 @@ (f) => f.metric == m2, ).data, }))} - itemsPerRow={ccconfig.plot_view_plotsPerRow} + itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} gridContent={metricsGridContent} /> diff --git a/web/frontend/src/Job.root.svelte b/web/frontend/src/Job.root.svelte index b003434..4ee1957 100644 --- a/web/frontend/src/Job.root.svelte +++ b/web/frontend/src/Job.root.svelte @@ -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 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} - + {#if $initq.data.job.concurrentJobs != null && $initq.data.job.concurrentJobs.items.length != 0} @@ -268,7 +271,9 @@ {#if $initq.error} {$initq.error.message} {:else if $initq?.data} - + {#if showRoofline} + + {/if} {:else} {/if} @@ -354,14 +359,14 @@ groupByScope($jobMetrics.data.scopedJobStats), selectedMetrics, )} - itemsPerRow={ccconfig.plot_view_plotsPerRow} + itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} {gridContent} /> {/if} - + {#if $initq?.data} @@ -397,8 +402,10 @@ {/if} - - + {#if showStatsTable} + + + {/if}
{#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] diff --git a/web/frontend/src/Jobs.root.svelte b/web/frontend/src/Jobs.root.svelte index 28a8bb4..8bbc5ad 100644 --- a/web/frontend/src/Jobs.root.svelte +++ b/web/frontend/src/Jobs.root.svelte @@ -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] diff --git a/web/frontend/src/List.root.svelte b/web/frontend/src/List.root.svelte index 9dc14ad..ec8f654 100644 --- a/web/frontend/src/List.root.svelte +++ b/web/frontend/src/List.root.svelte @@ -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; }} diff --git a/web/frontend/src/Node.root.svelte b/web/frontend/src/Node.root.svelte index 8082c5a..5c37ad1 100644 --- a/web/frontend/src/Node.root.svelte +++ b/web/frontend/src/Node.root.svelte @@ -247,7 +247,7 @@ ), })) .sort((a, b) => a.name.localeCompare(b.name))} - itemsPerRow={ccconfig.plot_view_plotsPerRow} + itemsPerRow={ccconfig.plotConfiguration_plotsPerRow} {gridContent} /> {/if} diff --git a/web/frontend/src/Status.root.svelte b/web/frontend/src/Status.root.svelte index e28af8e..d5ae0f7 100644 --- a/web/frontend/src/Status.root.svelte +++ b/web/frontend/src/Status.root.svelte @@ -28,7 +28,7 @@ } = $props(); /*Const Init */ - const useCbColors = getContext("cc-config")?.plot_general_colorblindMode || false + const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false diff --git a/web/frontend/src/Systems.root.svelte b/web/frontend/src/Systems.root.svelte index f46dde4..3c46adc 100644 --- a/web/frontend/src/Systems.root.svelte +++ b/web/frontend/src/Systems.root.svelte @@ -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 }, + }); + }; + @@ -213,7 +265,7 @@ presetMetrics={selectedMetrics} {cluster} {subCluster} - configName="node_list_selectedMetrics" + configName="nodeList_selectedMetrics" applyMetrics={(newMetrics) => selectedMetrics = [...newMetrics] } diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte index f675a0d..f435846 100644 --- a/web/frontend/src/User.root.svelte +++ b/web/frontend/src/User.root.svelte @@ -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}`]; }; }); @@ -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]; }} diff --git a/web/frontend/src/analysis/PlotSelection.svelte b/web/frontend/src/analysis/PlotSelection.svelte index f4818b6..ab4eda8 100644 --- a/web/frontend/src/analysis/PlotSelection.svelte +++ b/web/frontend/src/analysis/PlotSelection.svelte @@ -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); diff --git a/web/frontend/src/config/UserSettings.svelte b/web/frontend/src/config/UserSettings.svelte index dade973..34b81dc 100644 --- a/web/frontend/src/config/UserSettings.svelte +++ b/web/frontend/src/config/UserSettings.svelte @@ -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"); diff --git a/web/frontend/src/config/support/SupportOptions.svelte b/web/frontend/src/config/support/SupportOptions.svelte index 0aa8d13..101a88f 100644 --- a/web/frontend/src/config/support/SupportOptions.svelte +++ b/web/frontend/src/config/support/SupportOptions.svelte @@ -69,10 +69,10 @@ method="post" action="/frontend/configuration/" onsubmit={(e) => handleSettingSubmit(e, "#node-paging-form", "npag")}> - +
- {#if config?.node_list_usePaging} + {#if config?.nodeList_usePaging} {:else} @@ -80,7 +80,7 @@
- {#if config?.node_list_usePaging} + {#if config?.nodeList_usePaging} {:else} diff --git a/web/frontend/src/config/user/PlotColorScheme.svelte b/web/frontend/src/config/user/PlotColorScheme.svelte index 613d951..6b24fd4 100644 --- a/web/frontend/src/config/user/PlotColorScheme.svelte +++ b/web/frontend/src/config/user/PlotColorScheme.svelte @@ -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 @@
{/if} - + {#key activeRow} - {#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]} + {#each Object.entries(cbmode ? cvdSchemes : colorSchemes) as [name, rgbrow]} {#if job.monitoringStatus == 0 || job.monitoringStatus == 2} diff --git a/web/web.go b/web/web.go index 8c0af8b..da64fcd 100644 --- a/web/web.go +++ b/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
{name} diff --git a/web/frontend/src/config/user/PlotRenderOptions.svelte b/web/frontend/src/config/user/PlotRenderOptions.svelte index 2f6af91..de58585 100644 --- a/web/frontend/src/config/user/PlotRenderOptions.svelte +++ b/web/frontend/src/config/user/PlotRenderOptions.svelte @@ -55,7 +55,7 @@ {/if} - +
@@ -102,7 +102,7 @@
{/if} - +
@@ -150,10 +150,10 @@
{/if} - +
- {#if config.plot_general_colorBackground} + {#if config.plotConfiguration_colorBackground} {:else} @@ -161,7 +161,7 @@
- {#if config.plot_general_colorBackground} + {#if config.plotConfiguration_colorBackground} {:else} @@ -196,10 +196,10 @@
{/if} - +
- {#if config?.plot_general_colorblindMode} + {#if config?.plotConfiguration_colorblindMode} {:else} @@ -207,7 +207,7 @@
- {#if config?.plot_general_colorblindMode} + {#if config?.plotConfiguration_colorblindMode} {:else} diff --git a/web/frontend/src/config/user/UserOptions.svelte b/web/frontend/src/config/user/UserOptions.svelte index 49f3c17..ad03d71 100644 --- a/web/frontend/src/config/user/UserOptions.svelte +++ b/web/frontend/src/config/user/UserOptions.svelte @@ -104,10 +104,10 @@ >
{/if} - +
- {#if config?.job_list_usePaging} + {#if config?.jobList_usePaging} {:else} @@ -115,7 +115,7 @@
- {#if config?.job_list_usePaging} + {#if config?.jobList_usePaging} {:else} diff --git a/web/frontend/src/generic/Filters.svelte b/web/frontend/src/generic/Filters.svelte index 44fc408..7bc877f 100644 --- a/web/frontend/src/generic/Filters.svelte +++ b/web/frontend/src/generic/Filters.svelte @@ -294,11 +294,12 @@ Filters - Manage Filters {#if menuText} + Note {menuText} {/if} + Manage Filters (isClusterOpen = true)}> Cluster/Partition @@ -325,7 +326,7 @@ {#if startTimeQuickSelect} - Start Time Quick Selection + Start Time Quick Selection {#each startTimeSelectOptions.filter((stso) => stso.range !== "") as { rangeLabel, range }} { diff --git a/web/frontend/src/generic/JobCompare.svelte b/web/frontend/src/generic/JobCompare.svelte index 55966ac..a504e98 100644 --- a/web/frontend/src/generic/JobCompare.svelte +++ b/web/frontend/src/generic/JobCompare.svelte @@ -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) { diff --git a/web/frontend/src/generic/JobList.svelte b/web/frontend/src/generic/JobList.svelte index 5ca8981..b636e6e 100644 --- a/web/frontend/src/generic/JobList.svelte +++ b/web/frontend/src/generic/JobList.svelte @@ -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)", diff --git a/web/frontend/src/generic/joblist/JobListRow.svelte b/web/frontend/src/generic/joblist/JobListRow.svelte index 28574d9..8019b96 100644 --- a/web/frontend/src/generic/joblist/JobListRow.svelte +++ b/web/frontend/src/generic/joblist/JobListRow.svelte @@ -170,7 +170,7 @@
- + diff --git a/web/frontend/src/generic/plots/Comparogram.svelte b/web/frontend/src/generic/plots/Comparogram.svelte index 2051088..5f50475 100644 --- a/web/frontend/src/generic/plots/Comparogram.svelte +++ b/web/frontend/src/generic/plots/Comparogram.svelte @@ -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 = [ diff --git a/web/frontend/src/generic/plots/MetricPlot.svelte b/web/frontend/src/generic/plots/MetricPlot.svelte index 25bc3b7..5870678 100644 --- a/web/frontend/src/generic/plots/MetricPlot.svelte +++ b/web/frontend/src/generic/plots/MetricPlot.svelte @@ -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)) ) diff --git a/web/frontend/src/generic/plots/Pie.svelte b/web/frontend/src/generic/plots/Pie.svelte index 48031e4..45be1f5 100644 --- a/web/frontend/src/generic/plots/Pie.svelte +++ b/web/frontend/src/generic/plots/Pie.svelte @@ -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, diff --git a/web/frontend/src/generic/plots/Roofline.svelte b/web/frontend/src/generic/plots/Roofline.svelte index 3a0e332..4f6dcb9 100644 --- a/web/frontend/src/generic/plots/Roofline.svelte +++ b/web/frontend/src/generic/plots/Roofline.svelte @@ -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; diff --git a/web/frontend/src/generic/plots/RooflineLegacy.svelte b/web/frontend/src/generic/plots/RooflineLegacy.svelte index 8c6e9de..624253b 100644 --- a/web/frontend/src/generic/plots/RooflineLegacy.svelte +++ b/web/frontend/src/generic/plots/RooflineLegacy.svelte @@ -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; diff --git a/web/frontend/src/generic/select/MetricSelection.svelte b/web/frontend/src/generic/select/MetricSelection.svelte index d6da4df..ba5a289 100644 --- a/web/frontend/src/generic/select/MetricSelection.svelte +++ b/web/frontend/src/generic/select/MetricSelection.svelte @@ -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) { diff --git a/web/frontend/src/generic/units.js b/web/frontend/src/generic/units.js index bb527ab..9193ff8 100644 --- a/web/frontend/src/generic/units.js +++ b/web/frontend/src/generic/units.js @@ -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); diff --git a/web/frontend/src/job/JobSummary.svelte b/web/frontend/src/job/JobSummary.svelte index 992b99c..c2cabb5 100644 --- a/web/frontend/src/job/JobSummary.svelte +++ b/web/frontend/src/job/JobSummary.svelte @@ -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`]; {#if showFootprintTab} - + {/if} - - - - + {#if showPolarTab} + + + + + {/if} diff --git a/web/frontend/src/job/StatsTab.svelte b/web/frontend/src/job/StatsTab.svelte index 1bcc4f1..eb4fd55 100644 --- a/web/frontend/src/job/StatsTab.svelte +++ b/web/frontend/src/job/StatsTab.svelte @@ -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] diff --git a/web/frontend/src/status/StatisticsDash.svelte b/web/frontend/src/status/StatisticsDash.svelte index 8523c80..f299439 100644 --- a/web/frontend/src/status/StatisticsDash.svelte +++ b/web/frontend/src/status/StatisticsDash.svelte @@ -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]; }} diff --git a/web/frontend/src/systems/NodeList.svelte b/web/frontend/src/systems/NodeList.svelte index b898109..a4cec02 100644 --- a/web/frontend/src/systems/NodeList.svelte +++ b/web/frontend/src/systems/NodeList.svelte @@ -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) { diff --git a/web/frontend/src/systems/NodeOverview.svelte b/web/frontend/src/systems/NodeOverview.svelte index 2fc7bde..366f705 100644 --- a/web/frontend/src/systems/NodeOverview.svelte +++ b/web/frontend/src/systems/NodeOverview.svelte @@ -121,7 +121,7 @@ {:else if filteredData?.length > 0} - + {#key selectedMetric} {#each filteredData as item (item.host)}