From f416be77f789fb3a8f0ec037d452a4104f2538b4 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Tue, 20 Jan 2026 16:26:00 +0100 Subject: [PATCH 1/4] add publicmode to doublemetric plot, --- .../src/generic/plots/DoubleMetricPlot.svelte | 45 ++++++++++--------- .../src/generic/plots/MetricPlot.svelte | 10 ++--- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/web/frontend/src/generic/plots/DoubleMetricPlot.svelte b/web/frontend/src/generic/plots/DoubleMetricPlot.svelte index 4f76fc7a..afc4f9a3 100644 --- a/web/frontend/src/generic/plots/DoubleMetricPlot.svelte +++ b/web/frontend/src/generic/plots/DoubleMetricPlot.svelte @@ -44,18 +44,18 @@ // zoomState = null, // thresholdState = null, enableFlip = false, + publicMode = false, // onZoom } = $props(); /* Const Init */ const clusterCockpitConfig = getContext("cc-config"); + const fixedLineColors = ["#0000ff", "#ff0000"]; // Plot only uses 2 Datasets: High Contrast // const resampleConfig = getContext("resampling"); // const subClusterTopology = getContext("getHardwareTopology")(cluster, subCluster); // const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric); - const lineColors = clusterCockpitConfig.plotConfiguration_colorScheme; // const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; const renderSleepTime = 200; - // const normalLineColor = "#000000"; // const backgroundColors = { // normal: "rgba(255, 255, 255, 1.0)", // caution: cbmode ? "rgba(239, 230, 69, 0.3)" : "rgba(255, 128, 0, 0.3)", @@ -186,10 +186,10 @@ // Default // if (!extendedLegendData) { pendingSeries.push({ - label: `${metricData[i]?.name} (${metricData[i]?.unit?.prefix}${metricData[i]?.unit?.base})`, + label: publicMode ? null : `${metricData[i]?.name} (${metricData[i]?.unit?.prefix}${metricData[i]?.unit?.base})`, scale: `y${i+1}`, width: lineWidth, - stroke: lineColor(i, metricData.length), + stroke: fixedLineColors[i], }); // } // Extended Legend For NodeList @@ -435,38 +435,42 @@ // return backgroundColors.normal; // } - function lineColor(i, n) { - if (n && n >= lineColors.length) return lineColors[i % lineColors.length]; - else return lineColors[Math.floor((i / n) * lineColors.length)]; - } - function render(ren_width, ren_height) { // Set Options const opts = { - width, - height, - title: 'Cluster Utilization', + width: ren_width, + height: ren_height, + title: publicMode ? null : 'Cluster Utilization', plugins: [legendAsTooltipPlugin()], series: plotSeries, axes: [ { scale: "x", - space: 35, + space: publicMode ? 60 : null, incrs: timeIncrs(timestep, maxX, forNode), - label: "Time", + font: publicMode ? '16px Arial' : CanvasRenderingContext2D['font'], + label: publicMode ? null : "Time", values: (_, vals) => vals.map((v) => formatDurationTime(v, forNode)), }, { scale: "y1", grid: { show: true }, - label: `${metricData[0]?.name} (${metricData[0]?.unit?.prefix}${metricData[0]?.unit?.base})`, + stroke: publicMode ? fixedLineColors[0] : "#000000", + size: publicMode ? 60 : null, + space: publicMode ? 50 : null, + font: publicMode ? '16px Arial' : CanvasRenderingContext2D['font'], + label: publicMode ? null : `${metricData[0]?.name} (${metricData[0]?.unit?.prefix}${metricData[0]?.unit?.base})`, values: (u, vals) => vals.map((v) => formatNumber(v)), }, { side: 1, scale: "y2", grid: { show: false }, - label: `${metricData[1]?.name} (${metricData[1]?.unit?.prefix}${metricData[1]?.unit?.base})`, + stroke: publicMode ? fixedLineColors[1] : "#000000", + size: publicMode ? 60 : null, + space: publicMode ? 40 : null, + font: publicMode ? '16px Arial' : CanvasRenderingContext2D['font'], + label: publicMode ? null : `${metricData[1]?.name} (${metricData[1]?.unit?.prefix}${metricData[1]?.unit?.base})`, values: (u, vals) => vals.map((v) => formatNumber(v)), }, ], @@ -489,7 +493,7 @@ // }; // }, // ], - draw: [ + draw: publicMode ? null : [ (u) => { // Draw plot type label: let textl = `Cluster ${cluster}` @@ -523,7 +527,7 @@ // let y = u.valToPos(thresholds.normal, "y", true); // u.ctx.save(); // u.ctx.lineWidth = lineWidth; - // u.ctx.strokeStyle = normalLineColor; + // u.ctx.strokeStyle = "#000000"; // u.ctx.setLineDash([5, 5]); // u.ctx.beginPath(); // u.ctx.moveTo(u.bbox.left, y); @@ -567,7 +571,7 @@ scales: { x: { time: false }, y1: { auto: true }, - y1: { auto: true }, + y2: { auto: true }, }, legend: { // Display legend until max 12 Y-dataseries @@ -581,9 +585,6 @@ // Handle Render if (!uplot) { - opts.width = ren_width; - opts.height = ren_height; - // if (plotSync) { // opts.cursor.sync = { // key: plotSync.key, diff --git a/web/frontend/src/generic/plots/MetricPlot.svelte b/web/frontend/src/generic/plots/MetricPlot.svelte index 81fbd3c0..7e48e8e1 100644 --- a/web/frontend/src/generic/plots/MetricPlot.svelte +++ b/web/frontend/src/generic/plots/MetricPlot.svelte @@ -56,7 +56,6 @@ /* Const Init */ const clusterCockpitConfig = getContext("cc-config"); const resampleConfig = getContext("resampling"); - const lineColors = clusterCockpitConfig.plotConfiguration_colorScheme; const lineWidth = clusterCockpitConfig.plotConfiguration_lineWidth / window.devicePixelRatio; const cbmode = clusterCockpitConfig?.plotConfiguration_colorblindMode || false; const renderSleepTime = 200; @@ -200,7 +199,7 @@ : scope + " #" + (i + 1), scale: "y", width: lineWidth, - stroke: lineColor(i, series?.length), + stroke: lineColor(i, clusterCockpitConfig.plotConfiguration_colorScheme), }); } // Extended Legend For NodeList @@ -214,7 +213,7 @@ : scope + " #" + (i + 1), scale: "y", width: lineWidth, - stroke: lineColor(i, series?.length), + stroke: lineColor(i, clusterCockpitConfig.plotConfiguration_colorScheme), values: (u, sidx, idx) => { // "i" = "sidx - 1" : sidx contains x-axis-data if (idx == null) @@ -446,9 +445,8 @@ return backgroundColors.normal; } - function lineColor(i, n) { - if (n && n >= lineColors.length) return lineColors[i % lineColors.length]; - else return lineColors[Math.floor((i / n) * lineColors.length)]; + function lineColor(index, colors) { + return colors[index % colors.length]; } function render(ren_width, ren_height) { From f18ae350300bcd3b57a0d85073a1564e08cc77d6 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 21 Jan 2026 12:21:52 +0100 Subject: [PATCH 2/4] review plot rendering and doublemetric opts --- web/frontend/src/DashPublic.root.svelte | 44 +- web/frontend/src/Header.svelte | 2 +- .../src/generic/plots/DoubleMetricPlot.svelte | 587 ++++-------------- .../src/generic/plots/Roofline.svelte | 12 +- web/frontend/src/generic/plots/Stacked.svelte | 55 +- web/frontend/src/generic/units.js | 2 +- web/frontend/src/status/DashInternal.svelte | 32 +- .../src/status/dashdetails/StatusDash.svelte | 17 +- 8 files changed, 205 insertions(+), 546 deletions(-) diff --git a/web/frontend/src/DashPublic.root.svelte b/web/frontend/src/DashPublic.root.svelte index 0c66d867..5706b705 100644 --- a/web/frontend/src/DashPublic.root.svelte +++ b/web/frontend/src/DashPublic.root.svelte @@ -6,9 +6,6 @@ --> {#if roofData != null} -
+
{:else} Cannot render roofline: No data! {/if} diff --git a/web/frontend/src/generic/plots/Stacked.svelte b/web/frontend/src/generic/plots/Stacked.svelte index 3ee1ce1c..1dc57945 100644 --- a/web/frontend/src/generic/plots/Stacked.svelte +++ b/web/frontend/src/generic/plots/Stacked.svelte @@ -2,7 +2,6 @@ @component Node State/Health Data Stacked Plot Component, based on uPlot; states by timestamp Properties: - - `width Number?`: The plot width [Default: 0] - `height Number?`: The plot height [Default: 300] - `data [Array]`: The data object [Default: null] - `xlabel String?`: Plot X axis label [Default: ""] @@ -15,12 +14,11 @@ - - - + + + { + from = new Date(Date.now() - 5 * 60 * 1000); + to = new Date(Date.now()); + clusterFrom = new Date(Date.now() - (8 * 60 * 60 * 1000)) + + if (interval) stackedFrom += Math.floor(interval / 1000); + else stackedFrom += 1 // Workaround: TimeSelection not linked, just trigger new data on manual refresh + }} + /> + + + +{#if $statusQuery.fetching || $statesTimed.fetching} + + + + + + +{:else if $statusQuery.error || $statesTimed.error} + + + + + + + {#if $statusQuery.error} - { - from = new Date(Date.now() - 5 * 60 * 1000); - to = new Date(Date.now()); - clusterFrom = new Date(Date.now() - (8 * 60 * 60 * 1000)) - - if (interval) stackedFrom += Math.floor(interval / 1000); - else stackedFrom += 1 // Workaround: TimeSelection not linked, just trigger new data on manual refresh - }} - /> + Error Requesting Status Data: {$statusQuery.error.message} - - {#if $statusQuery.fetching || $statesTimed.fetching} - - - - - + {/if} + {#if $statesTimed.error} + + Error Requesting Node Scheduler States: {$statesTimed.error.message} + + {/if} + - {:else if $statusQuery.error || $statesTimed.error} - - - - - - - {#if $statusQuery.error} - - Error Requesting Status Data: {$statusQuery.error.message} - - {/if} - {#if $statesTimed.error} - - Error Requesting Node Scheduler States: {$statesTimed.error.message} - - {/if} - - - {:else} - - - - - - -

Cluster {presetCluster.charAt(0).toUpperCase() + presetCluster.slice(1)}

- - - - -
-
- -

CPU(s)

{[...clusterInfo?.processorTypes].join(', ')}

-
-
- - - +{:else} + +
+ + - - - - - {clusterInfo?.runningJobs} - -
- Running Jobs -
- - - - {clusterInfo?.activeUsers} - -
- Active Users -
- - - - {clusterInfo?.allocatedNodes} - -
- Active Nodes -
- -
- + + + +

Cluster {presetCluster.charAt(0).toUpperCase() + presetCluster.slice(1)}

+ + + + +
+
+ +

CPU(s)

{[...clusterInfo?.processorTypes].join(', ')}

+
+
+ + + + + + + + + {clusterInfo?.runningJobs} + +
+ Running Jobs +
+ + + + {clusterInfo?.activeUsers} + +
+ Active Users +
+ + + + {clusterInfo?.allocatedNodes} + +
+ Active Nodes +
+ +
+ + + + {clusterInfo?.flopRate} {clusterInfo?.flopRateUnit} + +
+ Total Flop Rate +
+ + + + {clusterInfo?.memBwRate} {clusterInfo?.memBwRateUnit} + +
+ Total Memory Bandwidth +
+ + {#if clusterInfo?.totalAccs !== 0} - {clusterInfo?.flopRate} {clusterInfo?.flopRateUnit} + {clusterInfo?.gpuPwr} {clusterInfo?.gpuPwrUnit}
- Total Flop Rate + Total GPU Power
+ {:else} - {clusterInfo?.memBwRate} {clusterInfo?.memBwRateUnit} + {clusterInfo?.cpuPwr} {clusterInfo?.cpuPwrUnit}
- Total Memory Bandwidth + Total CPU Power
- {#if clusterInfo?.totalAccs !== 0} - - - {clusterInfo?.gpuPwr} {clusterInfo?.gpuPwrUnit} - -
- Total GPU Power -
- - {:else} - - - {clusterInfo?.cpuPwr} {clusterInfo?.cpuPwrUnit} - -
- Total CPU Power -
- - {/if} -
+ {/if} +
+ + + Active Cores + + + + {formatNumber(clusterInfo?.allocatedCores)} + {formatNumber(clusterInfo?.idleCores)} + + + + Idle Cores + + + {#if clusterInfo?.totalAccs !== 0} - Active Cores + Active GPU - - {formatNumber(clusterInfo?.allocatedCores)} - {formatNumber(clusterInfo?.idleCores)} + + {formatNumber(clusterInfo?.allocatedAccs)} + {formatNumber(clusterInfo?.idleAccs)} - Idle Cores + Idle GPU - {#if clusterInfo?.totalAccs !== 0} - - - Active GPU - - - - {formatNumber(clusterInfo?.allocatedAccs)} - {formatNumber(clusterInfo?.idleAccs)} - - - - Idle GPU - - - {/if} - - - - - - -
- Cluster Utilization ( - - {`${$statusQuery?.data?.clusterMetrics?.metrics[0]?.name} (${$statusQuery?.data?.clusterMetrics?.metrics[0]?.unit?.prefix}${$statusQuery?.data?.clusterMetrics?.metrics[0]?.unit?.base})`} - , - - {`${$statusQuery?.data?.clusterMetrics?.metrics[1]?.name} (${$statusQuery?.data?.clusterMetrics?.metrics[1]?.unit?.prefix}${$statusQuery?.data?.clusterMetrics?.metrics[1]?.unit?.base})`} - - ) -
-
- {#key $statusQuery?.data?.clusterMetrics} - - {/key} -
- - - -
- {#key $statusQuery?.data?.nodeMetrics} - - {/key} -
- - - - {#if refinedStateData.length > 0} - -
- {#key refinedStateData} - sd.count, - )} - entities={refinedStateData.map( - (sd) => sd.state, - )} - fixColors={refinedStateData.map( - (sd) => colors['nodeStates'][sd.state], - )} - /> - {/key} -
- - - {#key refinedStateData} - - - - - - - {#each refinedStateData as sd, i} - - - - - - {/each} -
StateCount
{sd.state.charAt(0).toUpperCase() + sd.state.slice(1)}{sd.count}
- {/key} - - {:else} - - Cannot render state status: No state data returned for Pie Chart - {/if} -
- + + + + - -
- {#key $statesTimed?.data?.nodeStatesTimed} - - {/key} -
- - - {/if} - - + + + +
+ Cluster Utilization ( + + {`${$statusQuery?.data?.clusterMetrics?.metrics[0]?.name} (${$statusQuery?.data?.clusterMetrics?.metrics[0]?.unit?.prefix}${$statusQuery?.data?.clusterMetrics?.metrics[0]?.unit?.base})`} + , + + {`${$statusQuery?.data?.clusterMetrics?.metrics[1]?.name} (${$statusQuery?.data?.clusterMetrics?.metrics[1]?.unit?.prefix}${$statusQuery?.data?.clusterMetrics?.metrics[1]?.unit?.base})`} + + ) +
+
+ {#key $statusQuery?.data?.clusterMetrics} + + {/key} +
+ + + +
+ {#key $statusQuery?.data?.nodeMetrics} + + {/key} +
+ +
+ + + + + {#if refinedStateData.length > 0} + +
+ {#key refinedStateData} + sd.count, + )} + entities={refinedStateData.map( + (sd) => sd.state, + )} + fixColors={refinedStateData.map( + (sd) => colors['nodeStates'][sd.state], + )} + /> + {/key} +
+ + + {#key refinedStateData} + + + + + + + {#each refinedStateData as sd, i} + + + + + + {/each} +
StateCount
{sd.state.charAt(0).toUpperCase() + sd.state.slice(1)}{sd.count}
+ {/key} + + {:else} + + Cannot render state status: No state data returned for Pie Chart + + {/if} +
+ + + +
+ {#key $statesTimed?.data?.nodeStatesTimed} + + {/key} +
+ +
+
+{/if} From 05abea87e71576f56585da37447fb708ee9738e7 Mon Sep 17 00:00:00 2001 From: Michael Panzlaff Date: Thu, 22 Jan 2026 14:01:58 +0100 Subject: [PATCH 4/4] Do not warn about unencrypted auth when encrypted revsere proxy is used --- internal/auth/auth.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index cd89369c..df618a3f 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -305,8 +305,13 @@ func (auth *Authentication) SaveSession(rw http.ResponseWriter, r *http.Request, if auth.SessionMaxAge != 0 { session.Options.MaxAge = int(auth.SessionMaxAge.Seconds()) } - if config.Keys.HTTPSCertFile == "" && config.Keys.HTTPSKeyFile == "" { - cclog.Warn("HTTPS not configured - session cookies will not have Secure flag set (insecure for production)") + if r.TLS == nil && r.Header.Get("X-Forwarded-Proto") != "https" { + // If neither TLS or an encrypted reverse proxy are used, do not mark cookies as secure. + cclog.Warn("Authenticating with unencrypted request. Session cookies will not have Secure flag set (insecure for production)") + if r.Header.Get("X-Forwarded-Proto") == "" { + // This warning will not be printed if e.g. X-Forwarded-Proto == http + cclog.Warn("If you are using a reverse proxy, make sure X-Forwarded-Proto is set") + } session.Options.Secure = false } session.Options.SameSite = http.SameSiteStrictMode