|
68 | 68 | energyFootprint { hardware, metric, value } |
69 | 69 | } |
70 | 70 | `); |
71 | | - const client = getContextClient(); |
72 | | - const ccconfig = getContext("cc-config"); |
73 | | - const showRoofline = !!ccconfig[`jobView_showRoofline`]; |
74 | | - const showStatsTable = !!ccconfig[`jobView_showStatTable`]; |
75 | | -
|
76 | 71 | /* Note: Actual metric data queried in <Metric> Component, only require base infos here -> reduce backend load by requesting just stats */ |
| 72 | + const client = getContextClient(); |
77 | 73 | const query = gql` |
78 | 74 | query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) { |
79 | 75 | scopedJobStats(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes) { |
|
89 | 85 | /* State Init */ |
90 | 86 | let plots = $state({}); |
91 | 87 | let isMetricsSelectionOpen = $state(false); |
92 | | - let selectedMetrics = $state([]); |
93 | | - let selectedScopes = $state([]); |
94 | 88 | let totalMetrics = $state(0); |
95 | 89 |
|
96 | | - /* Derived */ |
97 | | - const showSummary = $derived((!!ccconfig[`jobView_showFootprint`] || !!ccconfig[`jobView_showPolarPlot`])) |
| 90 | + /* Derived Init Return */ |
| 91 | + const thisJob = $derived($initq?.data ? $initq.data.job : null); |
| 92 | +
|
| 93 | + /* Derived Settings */ |
| 94 | + const globalMetrics = $derived(thisJob ? getContext("globalMetrics") : null); |
| 95 | + const clusterInfo = $derived(thisJob ? getContext("clusters") : null); |
| 96 | + const ccconfig = $derived(thisJob ? getContext("cc-config") : null); |
| 97 | + const showRoofline = $derived(ccconfig ? !!ccconfig[`jobView_showRoofline`] : false); |
| 98 | + const showStatsTable = $derived(ccconfig ? !!ccconfig[`jobView_showStatTable`] : false); |
| 99 | + const showSummary = $derived(ccconfig ? (!!ccconfig[`jobView_showFootprint`] || !!ccconfig[`jobView_showPolarPlot`]) : false) |
| 100 | +
|
| 101 | + /* Derived Var Preprocessing*/ |
| 102 | + let selectedMetrics = $derived.by(() => { |
| 103 | + if(thisJob && ccconfig) { |
| 104 | + if (thisJob.cluster) { |
| 105 | + if (thisJob.subCluster) { |
| 106 | + return ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}:${thisJob.subCluster}`] || |
| 107 | + ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}`] || |
| 108 | + ccconfig.metricConfig_jobViewPlotMetrics |
| 109 | + } |
| 110 | + return ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}`] || |
| 111 | + ccconfig.metricConfig_jobViewPlotMetrics |
| 112 | + } |
| 113 | + return ccconfig.metricConfig_jobViewPlotMetrics |
| 114 | + } |
| 115 | + return []; |
| 116 | + }); |
| 117 | +
|
| 118 | + let selectedScopes = $derived.by(() => { |
| 119 | + const pendingScopes = ["node"] |
| 120 | + if (thisJob) { |
| 121 | + const accScopeDefault = [...selectedMetrics].some(function (m) { |
| 122 | + const thisCluster = clusterInfo.find((c) => c.name == thisJob.cluster); |
| 123 | + const subCluster = thisCluster.subClusters.find((sc) => sc.name == thisJob.subCluster); |
| 124 | + return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator"; |
| 125 | + }); |
| 126 | +
|
| 127 | + |
| 128 | + if (accScopeDefault) pendingScopes.push("accelerator") |
| 129 | + if (thisJob.numNodes === 1) { |
| 130 | + pendingScopes.push("socket") |
| 131 | + pendingScopes.push("core") |
| 132 | + } |
| 133 | + } |
| 134 | + return[...new Set(pendingScopes)]; |
| 135 | + }); |
| 136 | +
|
| 137 | + /* Derived Query and Postprocessing*/ |
98 | 138 | const jobMetrics = $derived(queryStore({ |
99 | 139 | client: client, |
100 | 140 | query: query, |
101 | 141 | variables: { dbid, selectedMetrics, selectedScopes }, |
102 | 142 | }) |
103 | 143 | ); |
104 | | - |
| 144 | +
|
105 | 145 | const missingMetrics = $derived.by(() => { |
106 | | - if ($initq?.data && $jobMetrics?.data) { |
107 | | - let job = $initq.data.job; |
| 146 | + if (thisJob && $jobMetrics?.data) { |
108 | 147 | let metrics = $jobMetrics.data.scopedJobStats; |
109 | | - let metricNames = $initq.data.globalMetrics.reduce((names, gm) => { |
110 | | - if (gm.availability.find((av) => av.cluster === job.cluster)) { |
| 148 | + let metricNames = globalMetrics.reduce((names, gm) => { |
| 149 | + if (gm.availability.find((av) => av.cluster === thisJob.cluster)) { |
111 | 150 | names.push(gm.name); |
112 | 151 | } |
113 | 152 | return names; |
|
118 | 157 | !metrics.some((jm) => jm.name == metric) && |
119 | 158 | selectedMetrics.includes(metric) && |
120 | 159 | !checkMetricDisabled( |
| 160 | + globalMetrics, |
121 | 161 | metric, |
122 | | - $initq.data.job.cluster, |
123 | | - $initq.data.job.subCluster, |
| 162 | + thisJob.cluster, |
| 163 | + thisJob.subCluster, |
124 | 164 | ), |
125 | 165 | ); |
126 | 166 | } else { |
|
129 | 169 | }); |
130 | 170 |
|
131 | 171 | const missingHosts = $derived.by(() => { |
132 | | - if ($initq?.data && $jobMetrics?.data) { |
133 | | - let job = $initq.data.job; |
| 172 | + if (thisJob && $jobMetrics?.data) { |
134 | 173 | let metrics = $jobMetrics.data.scopedJobStats; |
135 | | - let metricNames = $initq.data.globalMetrics.reduce((names, gm) => { |
136 | | - if (gm.availability.find((av) => av.cluster === job.cluster)) { |
| 174 | + let metricNames = globalMetrics.reduce((names, gm) => { |
| 175 | + if (gm.availability.find((av) => av.cluster === thisJob.cluster)) { |
137 | 176 | names.push(gm.name); |
138 | 177 | } |
139 | 178 | return names; |
140 | 179 | }, []); |
141 | 180 |
|
142 | | - return job.resources |
| 181 | + return thisJob.resources |
143 | 182 | .map(({ hostname }) => ({ |
144 | 183 | hostname: hostname, |
145 | 184 | metrics: metricNames.filter( |
|
165 | 204 | ? "Loading..." |
166 | 205 | : $initq?.error |
167 | 206 | ? "Error" |
168 | | - : `Job ${$initq.data.job.jobId} - ClusterCockpit`; |
169 | | - }); |
170 | | -
|
171 | | - /* On Init */ |
172 | | - getContext("on-init")(() => { |
173 | | - let job = $initq.data.job; |
174 | | - if (!job) return; |
175 | | - const pendingMetrics = ( |
176 | | - ccconfig[`metricConfig_jobViewPlotMetrics:${job.cluster}:${job.subCluster}`] || |
177 | | - ccconfig[`metricConfig_jobViewPlotMetrics:${job.cluster}`] |
178 | | - ) || |
179 | | - $initq.data.globalMetrics.reduce((names, gm) => { |
180 | | - if (gm.availability.find((av) => av.cluster === job.cluster && av.subClusters.includes(job.subCluster))) { |
181 | | - names.push(gm.name); |
182 | | - } |
183 | | - return names; |
184 | | - }, []) |
185 | | -
|
186 | | - // Select default Scopes to load: Check before if any metric has accelerator scope by default |
187 | | - const accScopeDefault = [...pendingMetrics].some(function (m) { |
188 | | - const cluster = $initq.data.clusters.find((c) => c.name == job.cluster); |
189 | | - const subCluster = cluster.subClusters.find((sc) => sc.name == job.subCluster); |
190 | | - return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator"; |
191 | | - }); |
192 | | -
|
193 | | - const pendingScopes = ["node"] |
194 | | - if (accScopeDefault) pendingScopes.push("accelerator") |
195 | | - if (job.numNodes === 1) { |
196 | | - pendingScopes.push("socket") |
197 | | - pendingScopes.push("core") |
198 | | - } |
199 | | -
|
200 | | - selectedMetrics = [...new Set(pendingMetrics)]; |
201 | | - selectedScopes = [...new Set(pendingScopes)]; |
| 207 | + : `Job ${thisJob.jobId} - ClusterCockpit`; |
202 | 208 | }); |
203 | 209 |
|
204 | 210 | /* Functions */ |
205 | | - const orderAndMap = (grouped, selectedMetrics) => |
206 | | - selectedMetrics.map((metric) => ({ |
| 211 | + const orderAndMap = (grouped, inputMetrics) => |
| 212 | + inputMetrics.map((metric) => ({ |
207 | 213 | metric: metric, |
208 | 214 | data: grouped.find((group) => group[0].name == metric), |
209 | 215 | disabled: checkMetricDisabled( |
| 216 | + globalMetrics, |
210 | 217 | metric, |
211 | | - $initq.data.job.cluster, |
212 | | - $initq.data.job.subCluster, |
| 218 | + thisJob.cluster, |
| 219 | + thisJob.subCluster, |
213 | 220 | ), |
214 | 221 | })); |
215 | 222 | </script> |
|
219 | 226 | <Col xs={12} md={6} xl={3} class="mb-3 mb-xxl-0"> |
220 | 227 | {#if $initq.error} |
221 | 228 | <Card body color="danger">{$initq.error.message}</Card> |
222 | | - {:else if $initq?.data} |
| 229 | + {:else if thisJob} |
223 | 230 | <Card class="overflow-auto" style="height: auto;"> |
224 | 231 | <TabContent> <!-- on:tab={(e) => (status = e.detail)} --> |
225 | | - {#if $initq.data?.job?.metaData?.message} |
| 232 | + {#if thisJob?.metaData?.message} |
226 | 233 | <TabPane tabId="admin-msg" tab="Admin Note" active> |
227 | 234 | <CardBody> |
228 | 235 | <Card body class="mb-2" color="warning"> |
229 | | - <h5>Job {$initq.data?.job?.jobId} ({$initq.data?.job?.cluster})</h5> |
| 236 | + <h5>Job {thisJob?.jobId} ({thisJob?.cluster})</h5> |
230 | 237 | The following note was added by administrators: |
231 | 238 | </Card> |
232 | 239 | <Card body> |
233 | | - {@html $initq.data.job.metaData.message} |
| 240 | + {@html thisJob.metaData.message} |
234 | 241 | </Card> |
235 | 242 | </CardBody> |
236 | 243 | </TabPane> |
237 | 244 | {/if} |
238 | | - <TabPane tabId="meta-info" tab="Job Info" active={$initq.data?.job?.metaData?.message?false:true}> |
| 245 | + <TabPane tabId="meta-info" tab="Job Info" active={thisJob?.metaData?.message?false:true}> |
239 | 246 | <CardBody class="pb-2"> |
240 | | - <JobInfo job={$initq.data.job} {username} {authlevel} {roles} showTagEdit/> |
| 247 | + <JobInfo job={thisJob} {username} {authlevel} {roles} showTagEdit/> |
241 | 248 | </CardBody> |
242 | 249 | </TabPane> |
243 | | - {#if $initq.data.job.concurrentJobs != null && $initq.data.job.concurrentJobs.items.length != 0} |
| 250 | + {#if thisJob.concurrentJobs != null && thisJob.concurrentJobs.items.length != 0} |
244 | 251 | <TabPane tabId="shared-jobs"> |
245 | 252 | <span slot="tab"> |
246 | | - {$initq.data.job.concurrentJobs.items.length} Concurrent Jobs |
| 253 | + {thisJob.concurrentJobs.items.length} Concurrent Jobs |
247 | 254 | </span> |
248 | 255 | <CardBody> |
249 | | - <ConcurrentJobs cJobs={$initq.data.job.concurrentJobs} showLinks={(authlevel > roles.manager)}/> |
| 256 | + <ConcurrentJobs cJobs={thisJob.concurrentJobs} showLinks={(authlevel > roles.manager)}/> |
250 | 257 | </CardBody> |
251 | 258 | </TabPane> |
252 | 259 | {/if} |
|
261 | 268 | <Col xs={12} md={6} xl={4} xxl={3} class="mb-3 mb-xxl-0"> |
262 | 269 | {#if $initq.error} |
263 | 270 | <Card body color="danger">{$initq.error.message}</Card> |
264 | | - {:else if $initq?.data} |
| 271 | + {:else if thisJob} |
265 | 272 | {#if showSummary} |
266 | | - <JobSummary job={$initq.data.job}/> |
| 273 | + <JobSummary job={thisJob}/> |
267 | 274 | {/if} |
268 | 275 | {:else} |
269 | 276 | <Spinner secondary /> |
|
274 | 281 | <Col xs={12} md={12} xl={5} xxl={6}> |
275 | 282 | {#if $initq.error} |
276 | 283 | <Card body color="danger">{$initq.error.message}</Card> |
277 | | - {:else if $initq?.data} |
| 284 | + {:else if thisJob} |
278 | 285 | {#if showRoofline} |
279 | | - <JobRoofline job={$initq.data.job} clusters={$initq.data.clusters}/> |
| 286 | + <JobRoofline job={thisJob} {clusterInfo}/> |
280 | 287 | {/if} |
281 | 288 | {:else} |
282 | 289 | <Spinner secondary /> |
|
285 | 292 | </Row> |
286 | 293 |
|
287 | 294 | <!-- Row 2: Energy Information if available --> |
288 | | -{#if $initq?.data && $initq.data.job.energyFootprint.length != 0} |
| 295 | +{#if thisJob && thisJob?.energyFootprint?.length != 0} |
289 | 296 | <Row class="mb-3"> |
290 | 297 | <Col> |
291 | | - <EnergySummary jobId={$initq.data.job.jobId} jobEnergy={$initq.data.job.energy} jobEnergyFootprint={$initq.data.job.energyFootprint}/> |
| 298 | + <EnergySummary jobId={thisJob.jobId} jobEnergy={thisJob.energy} jobEnergyFootprint={thisJob.energyFootprint}/> |
292 | 299 | </Col> |
293 | 300 | </Row> |
294 | 301 | {/if} |
|
297 | 304 | <Card class="mb-3"> |
298 | 305 | <CardBody> |
299 | 306 | <Row class="mb-2"> |
300 | | - {#if $initq?.data} |
| 307 | + {#if thisJob} |
301 | 308 | <Col xs="auto"> |
302 | 309 | <Button outline onclick={() => (isMetricsSelectionOpen = true)} color="primary"> |
303 | 310 | Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available) |
|
310 | 317 | {#if $jobMetrics.error} |
311 | 318 | <Row class="mt-2"> |
312 | 319 | <Col> |
313 | | - {#if $initq?.data && ($initq.data.job?.monitoringStatus == 0 || $initq.data.job?.monitoringStatus == 2)} |
| 320 | + {#if thisJob && (thisJob?.monitoringStatus == 0 || thisJob?.monitoringStatus == 2)} |
314 | 321 | <Card body color="warning">Not monitored or archiving failed</Card> |
315 | 322 | <br /> |
316 | 323 | {/if} |
|
323 | 330 | <Spinner secondary /> |
324 | 331 | </Col> |
325 | 332 | </Row> |
326 | | - {:else if $initq?.data && $jobMetrics?.data?.scopedJobStats} |
| 333 | + {:else if thisJob && $jobMetrics?.data?.scopedJobStats} |
327 | 334 | <!-- Note: Ignore '#snippet' Error in IDE --> |
328 | 335 | {#snippet gridContent(item)} |
329 | 336 | {#if item.data} |
330 | 337 | <Metric |
331 | 338 | bind:this={plots[item.metric]} |
332 | | - job={$initq.data.job} |
| 339 | + job={thisJob} |
333 | 340 | metricName={item.metric} |
334 | | - metricUnit={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.unit} |
335 | | - nativeScope={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.scope} |
| 341 | + metricUnit={globalMetrics.find((gm) => gm.name == item.metric)?.unit} |
| 342 | + nativeScope={globalMetrics.find((gm) => gm.name == item.metric)?.scope} |
336 | 343 | presetScopes={item.data.map((x) => x.scope)} |
337 | | - isShared={$initq.data.job.shared != "none"} |
| 344 | + isShared={thisJob.shared != "none"} |
338 | 345 | /> |
339 | 346 | {:else if item.disabled == true} |
340 | 347 | <Card color="info"> |
341 | 348 | <CardHeader class="mb-0"> |
342 | 349 | <b>Disabled Metric</b> |
343 | 350 | </CardHeader> |
344 | 351 | <CardBody> |
345 | | - <p>Metric <b>{item.metric}</b> is disabled for cluster <b>{$initq.data.job.cluster}:{$initq.data.job.subCluster}</b>.</p> |
| 352 | + <p>Metric <b>{item.metric}</b> is disabled for cluster <b>{thisJob.cluster}:{thisJob.subCluster}</b>.</p> |
346 | 353 | <p class="mb-1">To remove this card, open metric selection and press "Close and Apply".</p> |
347 | 354 | </CardBody> |
348 | 355 | </Card> |
|
353 | 360 | </CardHeader> |
354 | 361 | <CardBody> |
355 | 362 | <p>No dataset(s) returned for <b>{item.metric}</b>.</p> |
356 | | - <p class="mb-1">Metric was not found in metric store for cluster <b>{$initq.data.job.cluster}</b>.</p> |
| 363 | + <p class="mb-1">Metric was not found in metric store for cluster <b>{thisJob.cluster}</b>.</p> |
357 | 364 | </CardBody> |
358 | 365 | </Card> |
359 | 366 | {/if} |
|
374 | 381 | <!-- Metadata && Statistcics Table --> |
375 | 382 | <Row class="mb-3"> |
376 | 383 | <Col> |
377 | | - {#if $initq?.data} |
| 384 | + {#if thisJob} |
378 | 385 | <Card> |
379 | 386 | <TabContent> |
380 | 387 | {#if somethingMissing} |
|
409 | 416 | {/if} |
410 | 417 | {#if showStatsTable} |
411 | 418 | <!-- Includes <TabPane> Statistics Table with Independent GQL Query --> |
412 | | - <StatsTab job={$initq.data.job} clusters={$initq.data.clusters} tabActive={!somethingMissing}/> |
| 419 | + <StatsTab job={thisJob} {clusterInfo} {globalMetrics} {ccconfig} tabActive={!somethingMissing}/> |
413 | 420 | {/if} |
414 | 421 | <TabPane tabId="job-script" tab="Job Script"> |
415 | 422 | <div class="pre-wrapper"> |
416 | | - {#if $initq.data.job.metaData?.jobScript} |
417 | | - <pre><code>{$initq.data.job.metaData?.jobScript}</code></pre> |
| 423 | + {#if thisJob.metaData?.jobScript} |
| 424 | + <pre><code>{thisJob.metaData?.jobScript}</code></pre> |
418 | 425 | {:else} |
419 | 426 | <Card body color="warning">No job script available</Card> |
420 | 427 | {/if} |
421 | 428 | </div> |
422 | 429 | </TabPane> |
423 | 430 | <TabPane tabId="slurm-info" tab="Slurm Info"> |
424 | 431 | <div class="pre-wrapper"> |
425 | | - {#if $initq.data.job.metaData?.slurmInfo} |
426 | | - <pre><code>{$initq.data.job.metaData?.slurmInfo}</code></pre> |
| 432 | + {#if thisJob.metaData?.slurmInfo} |
| 433 | + <pre><code>{thisJob.metaData?.slurmInfo}</code></pre> |
427 | 434 | {:else} |
428 | 435 | <Card body color="warning" |
429 | 436 | >No additional slurm information available</Card |
|
437 | 444 | </Col> |
438 | 445 | </Row> |
439 | 446 |
|
440 | | -{#if $initq?.data} |
| 447 | +{#if thisJob} |
441 | 448 | <MetricSelection |
442 | 449 | bind:isOpen={isMetricsSelectionOpen} |
443 | 450 | bind:totalMetrics |
444 | 451 | presetMetrics={selectedMetrics} |
445 | | - cluster={$initq.data.job.cluster} |
446 | | - subCluster={$initq.data.job.subCluster} |
| 452 | + cluster={thisJob.cluster} |
| 453 | + subCluster={thisJob.subCluster} |
447 | 454 | configName="metricConfig_jobViewPlotMetrics" |
448 | | - preInitialized |
| 455 | + {globalMetrics} |
449 | 456 | applyMetrics={(newMetrics) => |
450 | 457 | selectedMetrics = [...newMetrics] |
451 | 458 | } |
|
0 commit comments