Migrate Job View

This commit is contained in:
Christoph Kluge 2025-06-25 17:41:11 +02:00
parent e2e67e3977
commit 79a6c9e90d

View File

@ -9,10 +9,11 @@
--> -->
<script> <script>
import { getContext } from "svelte";
import { import {
queryStore, queryStore,
gql, gql,
getContextClient getContextClient,
} from "@urql/svelte"; } from "@urql/svelte";
import { import {
Row, Row,
@ -26,7 +27,6 @@
CardTitle, CardTitle,
Button, Button,
} from "@sveltestrap/sveltestrap"; } from "@sveltestrap/sveltestrap";
import { getContext } from "svelte";
import { import {
init, init,
groupByScope, groupByScope,
@ -42,29 +42,16 @@
import PlotGrid from "./generic/PlotGrid.svelte"; import PlotGrid from "./generic/PlotGrid.svelte";
import StatsTab from "./job/StatsTab.svelte"; import StatsTab from "./job/StatsTab.svelte";
export let dbid; /* Svelte 5 Props */
export let username; let {
export let authlevel; dbid,
export let roles; username,
authlevel,
// Setup General roles
} = $props();
const ccconfig = getContext("cc-config")
let isMetricsSelectionOpen = false,
selectedMetrics = [],
selectedScopes = [],
plots = {};
let totalMetrics = 0;
let missingMetrics = [],
missingHosts = [],
somethingMissing = false;
// Setup GQL
// First: Add Job Query to init function -> Only requires DBID as argument, received via URL-ID
// Second: Trigger jobMetrics query with now received jobInfos (scopes: from job metadata, selectedMetrics: from config or all, job: from url-id)
/* Const Init */
// Important: init() needs to be first const declaration or contextclient will not be initialized before "const client = ..."
const { query: initq } = init(` const { query: initq } = init(`
job(id: "${dbid}") { job(id: "${dbid}") {
id, jobId, user, project, cluster, startTime, id, jobId, user, project, cluster, startTime,
@ -80,8 +67,8 @@
energyFootprint { hardware, metric, value } energyFootprint { hardware, metric, value }
} }
`); `);
const client = getContextClient(); const client = getContextClient();
const ccconfig = getContext("cc-config");
const query = gql` const query = gql`
query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) { query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) {
jobMetrics(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes) { jobMetrics(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes) {
@ -114,17 +101,91 @@
} }
`; `;
$: jobMetrics = queryStore({ /* State Init */
let plots = $state({});
let isMetricsSelectionOpen = $state(false);
let selectedMetrics = $state([]);
let selectedScopes = $state([]);
let totalMetrics = $state(0);
/* Derived */
const jobMetrics = $derived(queryStore({
client: client, client: client,
query: query, query: query,
variables: { dbid, selectedMetrics, selectedScopes }, variables: { dbid, selectedMetrics, selectedScopes },
})
);
const missingMetrics = $derived.by(() => {
if ($initq?.data && $jobMetrics?.data) {
let job = $initq.data.job;
let metrics = $jobMetrics.data.jobMetrics;
let metricNames = $initq.data.globalMetrics.reduce((names, gm) => {
if (gm.availability.find((av) => av.cluster === job.cluster)) {
names.push(gm.name);
}
return names;
}, []);
return metricNames.filter(
(metric) =>
!metrics.some((jm) => jm.name == metric) &&
selectedMetrics.includes(metric) &&
!checkMetricDisabled(
metric,
$initq.data.job.cluster,
$initq.data.job.subCluster,
),
);
} else {
return []
}
}); });
// Handle Job Query on Init -> is not executed anymore const missingHosts = $derived.by(() => {
if ($initq?.data && $jobMetrics?.data) {
let job = $initq.data.job;
let metrics = $jobMetrics.data.jobMetrics;
let metricNames = $initq.data.globalMetrics.reduce((names, gm) => {
if (gm.availability.find((av) => av.cluster === job.cluster)) {
names.push(gm.name);
}
return names;
}, []);
return job.resources
.map(({ hostname }) => ({
hostname: hostname,
metrics: metricNames.filter(
(metric) =>
!metrics.some(
(jm) =>
jm.scope == "node" &&
jm.metric.series.some((series) => series.hostname == hostname),
),
),
}))
.filter(({ metrics }) => metrics.length > 0);
} else {
return [];
}
});
const somethingMissing = $derived(missingMetrics?.length > 0 || missingHosts?.length > 0);
/* Effects */
$effect(() => {
document.title = $initq.fetching
? "Loading..."
: $initq.error
? "Error"
: `Job ${$initq.data.job.jobId} - ClusterCockpit`;
});
/* On Init */
getContext("on-init")(() => { getContext("on-init")(() => {
let job = $initq.data.job; let job = $initq.data.job;
if (!job) return; if (!job) return;
const pendingMetrics = ( const pendingMetrics = (
ccconfig[`job_view_selectedMetrics:${job.cluster}:${job.subCluster}`] || ccconfig[`job_view_selectedMetrics:${job.cluster}:${job.subCluster}`] ||
ccconfig[`job_view_selectedMetrics:${job.cluster}`] ccconfig[`job_view_selectedMetrics:${job.cluster}`]
@ -154,52 +215,7 @@
selectedScopes = [...new Set(pendingScopes)]; selectedScopes = [...new Set(pendingScopes)];
}); });
// Interactive Document Title /* Functions */
$: document.title = $initq.fetching
? "Loading..."
: $initq.error
? "Error"
: `Job ${$initq.data.job.jobId} - ClusterCockpit`;
// Find out what metrics or hosts are missing:
$: if ($initq?.data && $jobMetrics?.data?.jobMetrics) {
let job = $initq.data.job,
metrics = $jobMetrics.data.jobMetrics,
metricNames = $initq.data.globalMetrics.reduce((names, gm) => {
if (gm.availability.find((av) => av.cluster === job.cluster)) {
names.push(gm.name);
}
return names;
}, []);
// Metric not found in JobMetrics && Metric not explicitly disabled in config or deselected: Was expected, but is Missing
missingMetrics = metricNames.filter(
(metric) =>
!metrics.some((jm) => jm.name == metric) &&
selectedMetrics.includes(metric) &&
!checkMetricDisabled(
metric,
$initq.data.job.cluster,
$initq.data.job.subCluster,
),
);
missingHosts = job.resources
.map(({ hostname }) => ({
hostname: hostname,
metrics: metricNames.filter(
(metric) =>
!metrics.some(
(jm) =>
jm.scope == "node" &&
jm.metric.series.some((series) => series.hostname == hostname),
),
),
}))
.filter(({ metrics }) => metrics.length > 0);
somethingMissing = missingMetrics.length > 0 || missingHosts.length > 0;
}
// Helper
const orderAndMap = (grouped, selectedMetrics) => const orderAndMap = (grouped, selectedMetrics) =>
selectedMetrics.map((metric) => ({ selectedMetrics.map((metric) => ({
metric: metric, metric: metric,
@ -293,7 +309,7 @@
<Row class="mb-2"> <Row class="mb-2">
{#if $initq?.data} {#if $initq?.data}
<Col xs="auto"> <Col xs="auto">
<Button outline on:click={() => (isMetricsSelectionOpen = true)} color="primary"> <Button outline onclick={() => (isMetricsSelectionOpen = true)} color="primary">
Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available) Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available)
</Button> </Button>
</Col> </Col>