mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-03-30 20:47:31 +02:00
complete review of context initialization and access, streamlining
This commit is contained in:
@@ -68,12 +68,8 @@
|
|||||||
energyFootprint { hardware, metric, value }
|
energyFootprint { hardware, metric, value }
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
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 */
|
/* Note: Actual metric data queried in <Metric> Component, only require base infos here -> reduce backend load by requesting just stats */
|
||||||
|
const client = getContextClient();
|
||||||
const query = gql`
|
const query = gql`
|
||||||
query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) {
|
query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!) {
|
||||||
scopedJobStats(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes) {
|
scopedJobStats(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes) {
|
||||||
@@ -89,12 +85,56 @@
|
|||||||
/* State Init */
|
/* State Init */
|
||||||
let plots = $state({});
|
let plots = $state({});
|
||||||
let isMetricsSelectionOpen = $state(false);
|
let isMetricsSelectionOpen = $state(false);
|
||||||
let selectedMetrics = $state([]);
|
|
||||||
let selectedScopes = $state([]);
|
|
||||||
let totalMetrics = $state(0);
|
let totalMetrics = $state(0);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived Init Return */
|
||||||
const showSummary = $derived((!!ccconfig[`jobView_showFootprint`] || !!ccconfig[`jobView_showPolarPlot`]))
|
const thisJob = $derived($initq?.data ? $initq.data.job : null);
|
||||||
|
|
||||||
|
/* Derived Settings */
|
||||||
|
const globalMetrics = $derived(thisJob ? getContext("globalMetrics") : null);
|
||||||
|
const clusterInfo = $derived(thisJob ? getContext("clusters") : null);
|
||||||
|
const ccconfig = $derived(thisJob ? getContext("cc-config") : null);
|
||||||
|
const showRoofline = $derived(ccconfig ? !!ccconfig[`jobView_showRoofline`] : false);
|
||||||
|
const showStatsTable = $derived(ccconfig ? !!ccconfig[`jobView_showStatTable`] : false);
|
||||||
|
const showSummary = $derived(ccconfig ? (!!ccconfig[`jobView_showFootprint`] || !!ccconfig[`jobView_showPolarPlot`]) : false)
|
||||||
|
|
||||||
|
/* Derived Var Preprocessing*/
|
||||||
|
let selectedMetrics = $derived.by(() => {
|
||||||
|
if(thisJob && ccconfig) {
|
||||||
|
if (thisJob.cluster) {
|
||||||
|
if (thisJob.subCluster) {
|
||||||
|
return ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}:${thisJob.subCluster}`] ||
|
||||||
|
ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}`] ||
|
||||||
|
ccconfig.metricConfig_jobViewPlotMetrics
|
||||||
|
}
|
||||||
|
return ccconfig[`metricConfig_jobViewPlotMetrics:${thisJob.cluster}`] ||
|
||||||
|
ccconfig.metricConfig_jobViewPlotMetrics
|
||||||
|
}
|
||||||
|
return ccconfig.metricConfig_jobViewPlotMetrics
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
let selectedScopes = $derived.by(() => {
|
||||||
|
const pendingScopes = ["node"]
|
||||||
|
if (thisJob) {
|
||||||
|
const accScopeDefault = [...selectedMetrics].some(function (m) {
|
||||||
|
const thisCluster = clusterInfo.find((c) => c.name == thisJob.cluster);
|
||||||
|
const subCluster = thisCluster.subClusters.find((sc) => sc.name == thisJob.subCluster);
|
||||||
|
return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator";
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (accScopeDefault) pendingScopes.push("accelerator")
|
||||||
|
if (thisJob.numNodes === 1) {
|
||||||
|
pendingScopes.push("socket")
|
||||||
|
pendingScopes.push("core")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return[...new Set(pendingScopes)];
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Derived Query and Postprocessing*/
|
||||||
const jobMetrics = $derived(queryStore({
|
const jobMetrics = $derived(queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: query,
|
query: query,
|
||||||
@@ -103,11 +143,10 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const missingMetrics = $derived.by(() => {
|
const missingMetrics = $derived.by(() => {
|
||||||
if ($initq?.data && $jobMetrics?.data) {
|
if (thisJob && $jobMetrics?.data) {
|
||||||
let job = $initq.data.job;
|
|
||||||
let metrics = $jobMetrics.data.scopedJobStats;
|
let metrics = $jobMetrics.data.scopedJobStats;
|
||||||
let metricNames = $initq.data.globalMetrics.reduce((names, gm) => {
|
let metricNames = globalMetrics.reduce((names, gm) => {
|
||||||
if (gm.availability.find((av) => av.cluster === job.cluster)) {
|
if (gm.availability.find((av) => av.cluster === thisJob.cluster)) {
|
||||||
names.push(gm.name);
|
names.push(gm.name);
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
@@ -118,9 +157,10 @@
|
|||||||
!metrics.some((jm) => jm.name == metric) &&
|
!metrics.some((jm) => jm.name == metric) &&
|
||||||
selectedMetrics.includes(metric) &&
|
selectedMetrics.includes(metric) &&
|
||||||
!checkMetricDisabled(
|
!checkMetricDisabled(
|
||||||
|
globalMetrics,
|
||||||
metric,
|
metric,
|
||||||
$initq.data.job.cluster,
|
thisJob.cluster,
|
||||||
$initq.data.job.subCluster,
|
thisJob.subCluster,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -129,17 +169,16 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const missingHosts = $derived.by(() => {
|
const missingHosts = $derived.by(() => {
|
||||||
if ($initq?.data && $jobMetrics?.data) {
|
if (thisJob && $jobMetrics?.data) {
|
||||||
let job = $initq.data.job;
|
|
||||||
let metrics = $jobMetrics.data.scopedJobStats;
|
let metrics = $jobMetrics.data.scopedJobStats;
|
||||||
let metricNames = $initq.data.globalMetrics.reduce((names, gm) => {
|
let metricNames = globalMetrics.reduce((names, gm) => {
|
||||||
if (gm.availability.find((av) => av.cluster === job.cluster)) {
|
if (gm.availability.find((av) => av.cluster === thisJob.cluster)) {
|
||||||
names.push(gm.name);
|
names.push(gm.name);
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return job.resources
|
return thisJob.resources
|
||||||
.map(({ hostname }) => ({
|
.map(({ hostname }) => ({
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
metrics: metricNames.filter(
|
metrics: metricNames.filter(
|
||||||
@@ -165,51 +204,19 @@
|
|||||||
? "Loading..."
|
? "Loading..."
|
||||||
: $initq?.error
|
: $initq?.error
|
||||||
? "Error"
|
? "Error"
|
||||||
: `Job ${$initq.data.job.jobId} - ClusterCockpit`;
|
: `Job ${thisJob.jobId} - ClusterCockpit`;
|
||||||
});
|
|
||||||
|
|
||||||
/* On Init */
|
|
||||||
getContext("on-init")(() => {
|
|
||||||
let job = $initq.data.job;
|
|
||||||
if (!job) return;
|
|
||||||
const pendingMetrics = (
|
|
||||||
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))) {
|
|
||||||
names.push(gm.name);
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Select default Scopes to load: Check before if any metric has accelerator scope by default
|
|
||||||
const accScopeDefault = [...pendingMetrics].some(function (m) {
|
|
||||||
const cluster = $initq.data.clusters.find((c) => c.name == job.cluster);
|
|
||||||
const subCluster = cluster.subClusters.find((sc) => sc.name == job.subCluster);
|
|
||||||
return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator";
|
|
||||||
});
|
|
||||||
|
|
||||||
const pendingScopes = ["node"]
|
|
||||||
if (accScopeDefault) pendingScopes.push("accelerator")
|
|
||||||
if (job.numNodes === 1) {
|
|
||||||
pendingScopes.push("socket")
|
|
||||||
pendingScopes.push("core")
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedMetrics = [...new Set(pendingMetrics)];
|
|
||||||
selectedScopes = [...new Set(pendingScopes)];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
const orderAndMap = (grouped, selectedMetrics) =>
|
const orderAndMap = (grouped, inputMetrics) =>
|
||||||
selectedMetrics.map((metric) => ({
|
inputMetrics.map((metric) => ({
|
||||||
metric: metric,
|
metric: metric,
|
||||||
data: grouped.find((group) => group[0].name == metric),
|
data: grouped.find((group) => group[0].name == metric),
|
||||||
disabled: checkMetricDisabled(
|
disabled: checkMetricDisabled(
|
||||||
|
globalMetrics,
|
||||||
metric,
|
metric,
|
||||||
$initq.data.job.cluster,
|
thisJob.cluster,
|
||||||
$initq.data.job.subCluster,
|
thisJob.subCluster,
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
@@ -219,34 +226,34 @@
|
|||||||
<Col xs={12} md={6} xl={3} class="mb-3 mb-xxl-0">
|
<Col xs={12} md={6} xl={3} class="mb-3 mb-xxl-0">
|
||||||
{#if $initq.error}
|
{#if $initq.error}
|
||||||
<Card body color="danger">{$initq.error.message}</Card>
|
<Card body color="danger">{$initq.error.message}</Card>
|
||||||
{:else if $initq?.data}
|
{:else if thisJob}
|
||||||
<Card class="overflow-auto" style="height: auto;">
|
<Card class="overflow-auto" style="height: auto;">
|
||||||
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
||||||
{#if $initq.data?.job?.metaData?.message}
|
{#if thisJob?.metaData?.message}
|
||||||
<TabPane tabId="admin-msg" tab="Admin Note" active>
|
<TabPane tabId="admin-msg" tab="Admin Note" active>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Card body class="mb-2" color="warning">
|
<Card body class="mb-2" color="warning">
|
||||||
<h5>Job {$initq.data?.job?.jobId} ({$initq.data?.job?.cluster})</h5>
|
<h5>Job {thisJob?.jobId} ({thisJob?.cluster})</h5>
|
||||||
The following note was added by administrators:
|
The following note was added by administrators:
|
||||||
</Card>
|
</Card>
|
||||||
<Card body>
|
<Card body>
|
||||||
{@html $initq.data.job.metaData.message}
|
{@html thisJob.metaData.message}
|
||||||
</Card>
|
</Card>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{/if}
|
{/if}
|
||||||
<TabPane tabId="meta-info" tab="Job Info" active={$initq.data?.job?.metaData?.message?false:true}>
|
<TabPane tabId="meta-info" tab="Job Info" active={thisJob?.metaData?.message?false:true}>
|
||||||
<CardBody class="pb-2">
|
<CardBody class="pb-2">
|
||||||
<JobInfo job={$initq.data.job} {username} {authlevel} {roles} showTagEdit/>
|
<JobInfo job={thisJob} {username} {authlevel} {roles} showTagEdit/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{#if $initq.data.job.concurrentJobs != null && $initq.data.job.concurrentJobs.items.length != 0}
|
{#if thisJob.concurrentJobs != null && thisJob.concurrentJobs.items.length != 0}
|
||||||
<TabPane tabId="shared-jobs">
|
<TabPane tabId="shared-jobs">
|
||||||
<span slot="tab">
|
<span slot="tab">
|
||||||
{$initq.data.job.concurrentJobs.items.length} Concurrent Jobs
|
{thisJob.concurrentJobs.items.length} Concurrent Jobs
|
||||||
</span>
|
</span>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<ConcurrentJobs cJobs={$initq.data.job.concurrentJobs} showLinks={(authlevel > roles.manager)}/>
|
<ConcurrentJobs cJobs={thisJob.concurrentJobs} showLinks={(authlevel > roles.manager)}/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -261,9 +268,9 @@
|
|||||||
<Col xs={12} md={6} xl={4} xxl={3} class="mb-3 mb-xxl-0">
|
<Col xs={12} md={6} xl={4} xxl={3} class="mb-3 mb-xxl-0">
|
||||||
{#if $initq.error}
|
{#if $initq.error}
|
||||||
<Card body color="danger">{$initq.error.message}</Card>
|
<Card body color="danger">{$initq.error.message}</Card>
|
||||||
{:else if $initq?.data}
|
{:else if thisJob}
|
||||||
{#if showSummary}
|
{#if showSummary}
|
||||||
<JobSummary job={$initq.data.job}/>
|
<JobSummary job={thisJob}/>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
@@ -274,9 +281,9 @@
|
|||||||
<Col xs={12} md={12} xl={5} xxl={6}>
|
<Col xs={12} md={12} xl={5} xxl={6}>
|
||||||
{#if $initq.error}
|
{#if $initq.error}
|
||||||
<Card body color="danger">{$initq.error.message}</Card>
|
<Card body color="danger">{$initq.error.message}</Card>
|
||||||
{:else if $initq?.data}
|
{:else if thisJob}
|
||||||
{#if showRoofline}
|
{#if showRoofline}
|
||||||
<JobRoofline job={$initq.data.job} clusters={$initq.data.clusters}/>
|
<JobRoofline job={thisJob} {clusterInfo}/>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
@@ -285,10 +292,10 @@
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<!-- Row 2: Energy Information if available -->
|
<!-- Row 2: Energy Information if available -->
|
||||||
{#if $initq?.data && $initq.data.job.energyFootprint.length != 0}
|
{#if thisJob && thisJob?.energyFootprint?.length != 0}
|
||||||
<Row class="mb-3">
|
<Row class="mb-3">
|
||||||
<Col>
|
<Col>
|
||||||
<EnergySummary jobId={$initq.data.job.jobId} jobEnergy={$initq.data.job.energy} jobEnergyFootprint={$initq.data.job.energyFootprint}/>
|
<EnergySummary jobId={thisJob.jobId} jobEnergy={thisJob.energy} jobEnergyFootprint={thisJob.energyFootprint}/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -297,7 +304,7 @@
|
|||||||
<Card class="mb-3">
|
<Card class="mb-3">
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Row class="mb-2">
|
<Row class="mb-2">
|
||||||
{#if $initq?.data}
|
{#if thisJob}
|
||||||
<Col xs="auto">
|
<Col xs="auto">
|
||||||
<Button outline onclick={() => (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)
|
||||||
@@ -310,7 +317,7 @@
|
|||||||
{#if $jobMetrics.error}
|
{#if $jobMetrics.error}
|
||||||
<Row class="mt-2">
|
<Row class="mt-2">
|
||||||
<Col>
|
<Col>
|
||||||
{#if $initq?.data && ($initq.data.job?.monitoringStatus == 0 || $initq.data.job?.monitoringStatus == 2)}
|
{#if thisJob && (thisJob?.monitoringStatus == 0 || thisJob?.monitoringStatus == 2)}
|
||||||
<Card body color="warning">Not monitored or archiving failed</Card>
|
<Card body color="warning">Not monitored or archiving failed</Card>
|
||||||
<br />
|
<br />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -323,18 +330,18 @@
|
|||||||
<Spinner secondary />
|
<Spinner secondary />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{:else if $initq?.data && $jobMetrics?.data?.scopedJobStats}
|
{:else if thisJob && $jobMetrics?.data?.scopedJobStats}
|
||||||
<!-- Note: Ignore '#snippet' Error in IDE -->
|
<!-- Note: Ignore '#snippet' Error in IDE -->
|
||||||
{#snippet gridContent(item)}
|
{#snippet gridContent(item)}
|
||||||
{#if item.data}
|
{#if item.data}
|
||||||
<Metric
|
<Metric
|
||||||
bind:this={plots[item.metric]}
|
bind:this={plots[item.metric]}
|
||||||
job={$initq.data.job}
|
job={thisJob}
|
||||||
metricName={item.metric}
|
metricName={item.metric}
|
||||||
metricUnit={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.unit}
|
metricUnit={globalMetrics.find((gm) => gm.name == item.metric)?.unit}
|
||||||
nativeScope={$initq.data.globalMetrics.find((gm) => gm.name == item.metric)?.scope}
|
nativeScope={globalMetrics.find((gm) => gm.name == item.metric)?.scope}
|
||||||
presetScopes={item.data.map((x) => x.scope)}
|
presetScopes={item.data.map((x) => x.scope)}
|
||||||
isShared={$initq.data.job.shared != "none"}
|
isShared={thisJob.shared != "none"}
|
||||||
/>
|
/>
|
||||||
{:else if item.disabled == true}
|
{:else if item.disabled == true}
|
||||||
<Card color="info">
|
<Card color="info">
|
||||||
@@ -342,7 +349,7 @@
|
|||||||
<b>Disabled Metric</b>
|
<b>Disabled Metric</b>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<p>Metric <b>{item.metric}</b> is disabled for cluster <b>{$initq.data.job.cluster}:{$initq.data.job.subCluster}</b>.</p>
|
<p>Metric <b>{item.metric}</b> is disabled for cluster <b>{thisJob.cluster}:{thisJob.subCluster}</b>.</p>
|
||||||
<p class="mb-1">To remove this card, open metric selection and press "Close and Apply".</p>
|
<p class="mb-1">To remove this card, open metric selection and press "Close and Apply".</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -353,7 +360,7 @@
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<p>No dataset(s) returned for <b>{item.metric}</b>.</p>
|
<p>No dataset(s) returned for <b>{item.metric}</b>.</p>
|
||||||
<p class="mb-1">Metric was not found in metric store for cluster <b>{$initq.data.job.cluster}</b>.</p>
|
<p class="mb-1">Metric was not found in metric store for cluster <b>{thisJob.cluster}</b>.</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -374,7 +381,7 @@
|
|||||||
<!-- Metadata && Statistcics Table -->
|
<!-- Metadata && Statistcics Table -->
|
||||||
<Row class="mb-3">
|
<Row class="mb-3">
|
||||||
<Col>
|
<Col>
|
||||||
{#if $initq?.data}
|
{#if thisJob}
|
||||||
<Card>
|
<Card>
|
||||||
<TabContent>
|
<TabContent>
|
||||||
{#if somethingMissing}
|
{#if somethingMissing}
|
||||||
@@ -409,12 +416,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if showStatsTable}
|
{#if showStatsTable}
|
||||||
<!-- Includes <TabPane> Statistics Table with Independent GQL Query -->
|
<!-- Includes <TabPane> Statistics Table with Independent GQL Query -->
|
||||||
<StatsTab job={$initq.data.job} clusters={$initq.data.clusters} tabActive={!somethingMissing}/>
|
<StatsTab job={thisJob} {clusterInfo} {globalMetrics} {ccconfig} tabActive={!somethingMissing}/>
|
||||||
{/if}
|
{/if}
|
||||||
<TabPane tabId="job-script" tab="Job Script">
|
<TabPane tabId="job-script" tab="Job Script">
|
||||||
<div class="pre-wrapper">
|
<div class="pre-wrapper">
|
||||||
{#if $initq.data.job.metaData?.jobScript}
|
{#if thisJob.metaData?.jobScript}
|
||||||
<pre><code>{$initq.data.job.metaData?.jobScript}</code></pre>
|
<pre><code>{thisJob.metaData?.jobScript}</code></pre>
|
||||||
{:else}
|
{:else}
|
||||||
<Card body color="warning">No job script available</Card>
|
<Card body color="warning">No job script available</Card>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -422,8 +429,8 @@
|
|||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tabId="slurm-info" tab="Slurm Info">
|
<TabPane tabId="slurm-info" tab="Slurm Info">
|
||||||
<div class="pre-wrapper">
|
<div class="pre-wrapper">
|
||||||
{#if $initq.data.job.metaData?.slurmInfo}
|
{#if thisJob.metaData?.slurmInfo}
|
||||||
<pre><code>{$initq.data.job.metaData?.slurmInfo}</code></pre>
|
<pre><code>{thisJob.metaData?.slurmInfo}</code></pre>
|
||||||
{:else}
|
{:else}
|
||||||
<Card body color="warning"
|
<Card body color="warning"
|
||||||
>No additional slurm information available</Card
|
>No additional slurm information available</Card
|
||||||
@@ -437,15 +444,15 @@
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{#if $initq?.data}
|
{#if thisJob}
|
||||||
<MetricSelection
|
<MetricSelection
|
||||||
bind:isOpen={isMetricsSelectionOpen}
|
bind:isOpen={isMetricsSelectionOpen}
|
||||||
bind:totalMetrics
|
bind:totalMetrics
|
||||||
presetMetrics={selectedMetrics}
|
presetMetrics={selectedMetrics}
|
||||||
cluster={$initq.data.job.cluster}
|
cluster={thisJob.cluster}
|
||||||
subCluster={$initq.data.job.subCluster}
|
subCluster={thisJob.subCluster}
|
||||||
configName="metricConfig_jobViewPlotMetrics"
|
configName="metricConfig_jobViewPlotMetrics"
|
||||||
preInitialized
|
{globalMetrics}
|
||||||
applyMetrics={(newMetrics) =>
|
applyMetrics={(newMetrics) =>
|
||||||
selectedMetrics = [...newMetrics]
|
selectedMetrics = [...newMetrics]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const { query: initq } = init();
|
const { query: initq } = init();
|
||||||
const ccconfig = getContext("cc-config");
|
|
||||||
const matchedJobCompareLimit = 500;
|
const matchedJobCompareLimit = 500;
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
@@ -52,11 +51,17 @@
|
|||||||
let isMetricsSelectionOpen = $state(false);
|
let isMetricsSelectionOpen = $state(false);
|
||||||
let sorting = $state({ field: "startTime", type: "col", order: "DESC" });
|
let sorting = $state({ field: "startTime", type: "col", order: "DESC" });
|
||||||
|
|
||||||
|
/* Derived Init Return */
|
||||||
|
const thisInit = $derived($initq?.data ? true : false);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const ccconfig = $derived(thisInit ? getContext("cc-config") : null);
|
||||||
|
const globalMetrics = $derived(thisInit ? getContext("globalMetrics") : null);
|
||||||
let presetProject = $derived(filterPresets?.project ? filterPresets.project : "");
|
let presetProject = $derived(filterPresets?.project ? filterPresets.project : "");
|
||||||
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
|
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
|
||||||
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
|
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
|
||||||
let metrics = $derived.by(() => {
|
let metrics = $derived.by(() => {
|
||||||
|
if (thisInit && ccconfig) {
|
||||||
if (selectedCluster) {
|
if (selectedCluster) {
|
||||||
if (selectedSubCluster) {
|
if (selectedSubCluster) {
|
||||||
return ccconfig[`metricConfig_jobListMetrics:${selectedCluster}:${selectedSubCluster}`] ||
|
return ccconfig[`metricConfig_jobListMetrics:${selectedCluster}:${selectedSubCluster}`] ||
|
||||||
@@ -67,11 +72,15 @@
|
|||||||
ccconfig.metricConfig_jobListMetrics
|
ccconfig.metricConfig_jobListMetrics
|
||||||
}
|
}
|
||||||
return ccconfig.metricConfig_jobListMetrics
|
return ccconfig.metricConfig_jobListMetrics
|
||||||
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
let showFootprint = $derived(selectedCluster
|
let showFootprint = $derived((thisInit && ccconfig)
|
||||||
? !!ccconfig[`jobList_showFootprint:${selectedCluster}`]
|
? selectedCluster
|
||||||
: !!ccconfig.jobList_showFootprint
|
? ccconfig[`jobList_showFootprint:${selectedCluster}`]
|
||||||
|
: ccconfig.jobList_showFootprint
|
||||||
|
: {}
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -219,6 +228,7 @@
|
|||||||
<Sorting
|
<Sorting
|
||||||
bind:isOpen={isSortingOpen}
|
bind:isOpen={isSortingOpen}
|
||||||
presetSorting={sorting}
|
presetSorting={sorting}
|
||||||
|
{globalMetrics}
|
||||||
applySorting={(newSort) =>
|
applySorting={(newSort) =>
|
||||||
sorting = {...newSort}
|
sorting = {...newSort}
|
||||||
}
|
}
|
||||||
@@ -232,6 +242,7 @@
|
|||||||
subCluster={selectedSubCluster}
|
subCluster={selectedSubCluster}
|
||||||
configName="metricConfig_jobListMetrics"
|
configName="metricConfig_jobListMetrics"
|
||||||
footprintSelect
|
footprintSelect
|
||||||
|
{globalMetrics}
|
||||||
applyMetrics={(newMetrics) =>
|
applyMetrics={(newMetrics) =>
|
||||||
metrics = [...newMetrics]
|
metrics = [...newMetrics]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,14 +49,10 @@
|
|||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const { query: initq } = init();
|
const { query: initq } = init();
|
||||||
const initialized = getContext("initialized")
|
const client = getContextClient();
|
||||||
const globalMetrics = getContext("globalMetrics")
|
|
||||||
const ccconfig = getContext("cc-config");
|
|
||||||
const clusters = getContext("clusters");
|
|
||||||
const nowEpoch = Date.now();
|
const nowEpoch = Date.now();
|
||||||
const paging = { itemsPerPage: 50, page: 1 };
|
const paging = { itemsPerPage: 50, page: 1 };
|
||||||
const sorting = { field: "startTime", type: "col", order: "DESC" };
|
const sorting = { field: "startTime", type: "col", order: "DESC" };
|
||||||
const client = getContextClient();
|
|
||||||
const nodeMetricsQuery = gql`
|
const nodeMetricsQuery = gql`
|
||||||
query ($cluster: String!, $nodes: [String!], $from: Time!, $to: Time!) {
|
query ($cluster: String!, $nodes: [String!], $from: Time!, $to: Time!) {
|
||||||
nodeMetrics(cluster: $cluster, nodes: $nodes, from: $from, to: $to) {
|
nodeMetrics(cluster: $cluster, nodes: $nodes, from: $from, to: $to) {
|
||||||
@@ -112,14 +108,32 @@
|
|||||||
let from = $state(presetFrom ? presetFrom : new Date(nowEpoch - (4 * 3600 * 1000)));
|
let from = $state(presetFrom ? presetFrom : new Date(nowEpoch - (4 * 3600 * 1000)));
|
||||||
// svelte-ignore state_referenced_locally
|
// svelte-ignore state_referenced_locally
|
||||||
let to = $state(presetTo ? presetTo : new Date(nowEpoch));
|
let to = $state(presetTo ? presetTo : new Date(nowEpoch));
|
||||||
let systemUnits = $state({});
|
|
||||||
|
/* Derived Init Return */
|
||||||
|
const thisInit = $derived($initq?.data ? true : false);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const ccconfig = $derived(thisInit ? getContext("cc-config") : null);
|
||||||
|
const globalMetrics = $derived(thisInit ? getContext("globalMetrics") : null);
|
||||||
|
const clusterInfos = $derived(thisInit ? getContext("clusters") : null);
|
||||||
|
|
||||||
const filter = $derived([
|
const filter = $derived([
|
||||||
{ cluster: { eq: cluster } },
|
{ cluster: { eq: cluster } },
|
||||||
{ node: { contains: hostname } },
|
{ node: { contains: hostname } },
|
||||||
{ state: ["running"] },
|
{ state: ["running"] },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const systemUnits = $derived.by(() => {
|
||||||
|
const pendingUnits = {};
|
||||||
|
if (thisInit) {
|
||||||
|
const systemMetrics = [...globalMetrics.filter((gm) => gm?.availability.find((av) => av.cluster == cluster))]
|
||||||
|
for (let sm of systemMetrics) {
|
||||||
|
pendingUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {...pendingUnits};
|
||||||
|
});
|
||||||
|
|
||||||
const nodeMetricsData = $derived(queryStore({
|
const nodeMetricsData = $derived(queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: nodeMetricsQuery,
|
query: nodeMetricsQuery,
|
||||||
@@ -140,20 +154,6 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const thisNodeState = $derived($nodeMetricsData?.data?.nodeMetrics[0]?.state ? $nodeMetricsData.data.nodeMetrics[0].state : 'notindb');
|
const thisNodeState = $derived($nodeMetricsData?.data?.nodeMetrics[0]?.state ? $nodeMetricsData.data.nodeMetrics[0].state : 'notindb');
|
||||||
|
|
||||||
/* Effect */
|
|
||||||
$effect(() => {
|
|
||||||
loadUnits($initialized);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Functions */
|
|
||||||
function loadUnits(isInitialized) {
|
|
||||||
if (!isInitialized) return
|
|
||||||
const systemMetrics = [...globalMetrics.filter((gm) => gm?.availability.find((av) => av.cluster == cluster))]
|
|
||||||
for (let sm of systemMetrics) {
|
|
||||||
systemUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={{ xs: 2, lg: 5 }}>
|
<Row cols={{ xs: 2, lg: 5 }}>
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
<MetricPlot
|
<MetricPlot
|
||||||
metric={item.name}
|
metric={item.name}
|
||||||
timestep={item.metric.timestep}
|
timestep={item.metric.timestep}
|
||||||
cluster={clusters.find((c) => c.name == cluster)}
|
cluster={clusterInfos.find((c) => c.name == cluster)}
|
||||||
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
|
subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster}
|
||||||
series={item.metric.series}
|
series={item.metric.series}
|
||||||
enableFlip
|
enableFlip
|
||||||
@@ -277,6 +277,7 @@
|
|||||||
.map((m) => ({
|
.map((m) => ({
|
||||||
...m,
|
...m,
|
||||||
disabled: checkMetricDisabled(
|
disabled: checkMetricDisabled(
|
||||||
|
globalMetrics,
|
||||||
m.name,
|
m.name,
|
||||||
cluster,
|
cluster,
|
||||||
$nodeMetricsData.data.nodeMetrics[0].subCluster,
|
$nodeMetricsData.data.nodeMetrics[0].subCluster,
|
||||||
|
|||||||
@@ -51,13 +51,6 @@
|
|||||||
/* Const Init */
|
/* Const Init */
|
||||||
const { query: initq } = init();
|
const { query: initq } = init();
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const ccconfig = getContext("cc-config");
|
|
||||||
const initialized = getContext("initialized");
|
|
||||||
const globalMetrics = getContext("globalMetrics");
|
|
||||||
const resampleConfig = getContext("resampling") || null;
|
|
||||||
|
|
||||||
const resampleResolutions = resampleConfig ? [...resampleConfig.resolutions] : [];
|
|
||||||
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
|
||||||
const stateOptions = ['all', 'allocated', 'idle', 'reserved', 'mixed', 'down', 'unknown', 'notindb'];
|
const stateOptions = ['all', 'allocated', 'idle', 'reserved', 'mixed', 'down', 'unknown', 'notindb'];
|
||||||
const nowDate = new Date(Date.now());
|
const nowDate = new Date(Date.now());
|
||||||
|
|
||||||
@@ -65,35 +58,55 @@
|
|||||||
let timeoutId = null;
|
let timeoutId = null;
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let selectedResolution = $state(resampleConfig ? resampleDefault : 0);
|
|
||||||
let hostnameFilter = $state("");
|
let hostnameFilter = $state("");
|
||||||
let hoststateFilter = $state("all");
|
let hoststateFilter = $state("all");
|
||||||
let pendingHostnameFilter = $state("");
|
let pendingHostnameFilter = $state("");
|
||||||
let isMetricsSelectionOpen = $state(false);
|
let isMetricsSelectionOpen = $state(false);
|
||||||
|
|
||||||
|
/* Derived Init Return */
|
||||||
|
const thisInit = $derived($initq?.data ? true : false);
|
||||||
|
|
||||||
/* Derived States */
|
/* Derived States */
|
||||||
|
const ccconfig = $derived(thisInit ? getContext("cc-config") : null);
|
||||||
|
const globalMetrics = $derived(thisInit ? getContext("globalMetrics") : null);
|
||||||
|
const resampleConfig = $derived(thisInit ? getContext("resampling") : null);
|
||||||
|
const resampleResolutions = $derived(resampleConfig ? [...resampleConfig.resolutions] : []);
|
||||||
|
const resampleDefault = $derived(resampleConfig ? Math.max(...resampleConfig.resolutions) : 0);
|
||||||
|
const displayNodeOverview = $derived((displayType === 'OVERVIEW'));
|
||||||
|
|
||||||
|
const systemMetrics = $derived(globalMetrics ? [...globalMetrics.filter((gm) => gm?.availability.find((av) => av.cluster == cluster))] : []);
|
||||||
|
const systemUnits = $derived.by(() => {
|
||||||
|
const pendingUnits = {};
|
||||||
|
if (thisInit && systemMetrics.length > 0) {
|
||||||
|
for (let sm of systemMetrics) {
|
||||||
|
pendingUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {...pendingUnits};
|
||||||
|
});
|
||||||
|
|
||||||
|
let selectedResolution = $derived(resampleDefault);
|
||||||
let to = $derived(presetTo ? presetTo : new Date(Date.now()));
|
let to = $derived(presetTo ? presetTo : new Date(Date.now()));
|
||||||
let from = $derived(presetFrom ? presetFrom : new Date(nowDate.setHours(nowDate.getHours() - 4)));
|
let from = $derived(presetFrom ? presetFrom : new Date(nowDate.setHours(nowDate.getHours() - 4)));
|
||||||
const displayNodeOverview = $derived((displayType === 'OVERVIEW'));
|
|
||||||
const systemMetrics = $derived($initialized ? [...globalMetrics.filter((gm) => gm?.availability.find((av) => av.cluster == cluster))] : []);
|
|
||||||
const presetSystemUnits = $derived(loadUnits(systemMetrics));
|
|
||||||
let selectedMetric = $derived.by(() => {
|
let selectedMetric = $derived.by(() => {
|
||||||
let configKey = `nodeOverview_selectedMetric`;
|
let configKey = `nodeOverview_selectedMetric`;
|
||||||
if (cluster) configKey += `:${cluster}`;
|
if (cluster) configKey += `:${cluster}`;
|
||||||
if (subCluster) configKey += `:${subCluster}`;
|
if (subCluster) configKey += `:${subCluster}`;
|
||||||
|
|
||||||
if ($initialized) {
|
if (thisInit) {
|
||||||
if (ccconfig[configKey]) return ccconfig[configKey]
|
if (ccconfig[configKey]) return ccconfig[configKey]
|
||||||
else if (systemMetrics.length !== 0) return systemMetrics[0].name
|
else if (systemMetrics.length !== 0) return systemMetrics[0].name
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
});
|
});
|
||||||
|
|
||||||
let selectedMetrics = $derived.by(() => {
|
let selectedMetrics = $derived.by(() => {
|
||||||
let configKey = `nodeList_selectedMetrics`;
|
let configKey = `nodeList_selectedMetrics`;
|
||||||
if (cluster) configKey += `:${cluster}`;
|
if (cluster) configKey += `:${cluster}`;
|
||||||
if (subCluster) configKey += `:${subCluster}`;
|
if (subCluster) configKey += `:${subCluster}`;
|
||||||
|
|
||||||
if ($initialized) {
|
if (thisInit) {
|
||||||
if (ccconfig[configKey]) return ccconfig[configKey]
|
if (ccconfig[configKey]) return ccconfig[configKey]
|
||||||
else if (systemMetrics.length >= 3) return [systemMetrics[0].name, systemMetrics[1].name, systemMetrics[2].name]
|
else if (systemMetrics.length >= 3) return [systemMetrics[0].name, systemMetrics[1].name, systemMetrics[2].name]
|
||||||
}
|
}
|
||||||
@@ -108,16 +121,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
function loadUnits(systemMetrics) {
|
|
||||||
let pendingUnits = {};
|
|
||||||
if (systemMetrics.length > 0) {
|
|
||||||
for (let sm of systemMetrics) {
|
|
||||||
pendingUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return {...pendingUnits};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wait after input for some time to prevent too many requests
|
// Wait after input for some time to prevent too many requests
|
||||||
function updateHostnameFilter() {
|
function updateHostnameFilter() {
|
||||||
if (timeoutId != null) clearTimeout(timeoutId);
|
if (timeoutId != null) clearTimeout(timeoutId);
|
||||||
@@ -157,7 +160,7 @@
|
|||||||
|
|
||||||
<!-- ROW1: Tools-->
|
<!-- ROW1: Tools-->
|
||||||
<Row cols={{ xs: 2, lg: !displayNodeOverview ? (resampleConfig ? 6 : 5) : 5 }} class="mb-3">
|
<Row cols={{ xs: 2, lg: !displayNodeOverview ? (resampleConfig ? 6 : 5) : 5 }} class="mb-3">
|
||||||
{#if $initq?.data}
|
{#if thisInit}
|
||||||
<!-- List Metric Select Col-->
|
<!-- List Metric Select Col-->
|
||||||
{#if !displayNodeOverview}
|
{#if !displayNodeOverview}
|
||||||
<Col>
|
<Col>
|
||||||
@@ -234,7 +237,7 @@
|
|||||||
<Input type="select" bind:value={selectedMetric}>
|
<Input type="select" bind:value={selectedMetric}>
|
||||||
{#each systemMetrics as metric (metric.name)}
|
{#each systemMetrics as metric (metric.name)}
|
||||||
<option value={metric.name}
|
<option value={metric.name}
|
||||||
>{metric.name} {presetSystemUnits[metric.name] ? "("+presetSystemUnits[metric.name]+")" : ""}</option
|
>{metric.name} {systemUnits[metric.name] ? "("+systemUnits[metric.name]+")" : ""}</option
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<option disabled>No available options</option>
|
<option disabled>No available options</option>
|
||||||
@@ -266,10 +269,11 @@
|
|||||||
{:else}
|
{:else}
|
||||||
{#if displayNodeOverview}
|
{#if displayNodeOverview}
|
||||||
<!-- ROW2-1: Node Overview (Grid Included)-->
|
<!-- ROW2-1: Node Overview (Grid Included)-->
|
||||||
<NodeOverview {cluster} {ccconfig} {selectedMetric} {from} {to} {hostnameFilter} {hoststateFilter}/>
|
<NodeOverview {cluster} {ccconfig} {selectedMetric} {globalMetrics} {from} {to} {hostnameFilter} {hoststateFilter}/>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- ROW2-2: Node List (Grid Included)-->
|
<!-- ROW2-2: Node List (Grid Included)-->
|
||||||
<NodeList {cluster} {subCluster} {ccconfig} pendingSelectedMetrics={selectedMetrics} {selectedResolution} {hostnameFilter} {hoststateFilter} {from} {to} {presetSystemUnits}/>
|
<NodeList {cluster} {subCluster} {ccconfig} {globalMetrics}
|
||||||
|
pendingSelectedMetrics={selectedMetrics} {selectedResolution} {hostnameFilter} {hoststateFilter} {from} {to} {systemUnits}/>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -279,6 +283,7 @@
|
|||||||
presetMetrics={selectedMetrics}
|
presetMetrics={selectedMetrics}
|
||||||
{cluster}
|
{cluster}
|
||||||
{subCluster}
|
{subCluster}
|
||||||
|
{globalMetrics}
|
||||||
configName="nodeList_selectedMetrics"
|
configName="nodeList_selectedMetrics"
|
||||||
applyMetrics={(newMetrics) =>
|
applyMetrics={(newMetrics) =>
|
||||||
selectedMetrics = [...newMetrics]
|
selectedMetrics = [...newMetrics]
|
||||||
|
|||||||
@@ -56,12 +56,10 @@
|
|||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const { query: initq } = init();
|
const { query: initq } = init();
|
||||||
const ccconfig = getContext("cc-config");
|
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const durationBinOptions = ["1m","10m","1h","6h","12h"];
|
const durationBinOptions = ["1m","10m","1h","6h","12h"];
|
||||||
const metricBinOptions = [10, 20, 50, 100];
|
const metricBinOptions = [10, 20, 50, 100];
|
||||||
const matchedJobCompareLimit = 500;
|
const matchedJobCompareLimit = 500;
|
||||||
const shortDuration = ccconfig.jobList_hideShortRunningJobs; // Always configured
|
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
// List & Control Vars
|
// List & Control Vars
|
||||||
@@ -73,7 +71,6 @@
|
|||||||
let isSortingOpen = $state(false);
|
let isSortingOpen = $state(false);
|
||||||
let isMetricsSelectionOpen = $state(false);
|
let isMetricsSelectionOpen = $state(false);
|
||||||
let sorting = $state({ field: "startTime", type: "col", order: "DESC" });
|
let sorting = $state({ field: "startTime", type: "col", order: "DESC" });
|
||||||
let selectedHistogramsBuffer = $state({ all: (ccconfig['userView_histogramMetrics'] || []) })
|
|
||||||
let jobCompare = $state(null);
|
let jobCompare = $state(null);
|
||||||
let matchedCompareJobs = $state(0);
|
let matchedCompareJobs = $state(0);
|
||||||
let showCompare = $state(false);
|
let showCompare = $state(false);
|
||||||
@@ -84,10 +81,17 @@
|
|||||||
let numDurationBins = $state("1h");
|
let numDurationBins = $state("1h");
|
||||||
let numMetricBins = $state(10);
|
let numMetricBins = $state(10);
|
||||||
|
|
||||||
|
/* Derived Init Return */
|
||||||
|
const thisInit = $derived($initq?.data ? true : false);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const ccconfig = $derived(thisInit ? getContext("cc-config") : null);
|
||||||
|
const globalMetrics = $derived(thisInit ? getContext("globalMetrics") : null);
|
||||||
|
const shortDuration = $derived(ccconfig?.jobList_hideShortRunningJobs);
|
||||||
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
|
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
|
||||||
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
|
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
|
||||||
let metrics = $derived.by(() => {
|
let metrics = $derived.by(() => {
|
||||||
|
if (thisInit && ccconfig) {
|
||||||
if (selectedCluster) {
|
if (selectedCluster) {
|
||||||
if (selectedSubCluster) {
|
if (selectedSubCluster) {
|
||||||
return ccconfig[`metricConfig_jobListMetrics:${selectedCluster}:${selectedSubCluster}`] ||
|
return ccconfig[`metricConfig_jobListMetrics:${selectedCluster}:${selectedSubCluster}`] ||
|
||||||
@@ -98,12 +102,27 @@
|
|||||||
ccconfig.metricConfig_jobListMetrics
|
ccconfig.metricConfig_jobListMetrics
|
||||||
}
|
}
|
||||||
return ccconfig.metricConfig_jobListMetrics
|
return ccconfig.metricConfig_jobListMetrics
|
||||||
|
}
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
let showFootprint = $derived(filterPresets.cluster
|
|
||||||
? !!ccconfig[`jobList_showFootprint:${filterPresets.cluster}`]
|
let showFootprint = $derived((thisInit && ccconfig)
|
||||||
: !!ccconfig.jobList_showFootprint
|
? filterPresets?.cluster
|
||||||
|
? ccconfig[`jobList_showFootprint:${filterPresets.cluster}`]
|
||||||
|
: ccconfig.jobList_showFootprint
|
||||||
|
: {}
|
||||||
);
|
);
|
||||||
let selectedHistograms = $derived(selectedCluster ? selectedHistogramsBuffer[selectedCluster] : selectedHistogramsBuffer['all']);
|
|
||||||
|
let selectedHistograms = $derived.by(() => {
|
||||||
|
if (thisInit && ccconfig) {
|
||||||
|
if (selectedCluster) {
|
||||||
|
return ccconfig[`userView_histogramMetrics:${selectedCluster}`] // No Fallback; Unspecific lists an include unavailable metrics
|
||||||
|
}
|
||||||
|
return ccconfig.userView_histogramMetrics
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
});
|
||||||
|
|
||||||
let stats = $derived(
|
let stats = $derived(
|
||||||
queryStore({
|
queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
@@ -159,19 +178,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (!selectedHistogramsBuffer[selectedCluster]) {
|
|
||||||
selectedHistogramsBuffer[selectedCluster] = ccconfig[`userView_histogramMetrics:${selectedCluster}`];
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
/* On Mount */
|
/* On Mount */
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
filterComponent.updateFilters();
|
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[`userView_histogramMetrics:${filterPresets.cluster}`];
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -508,6 +517,7 @@
|
|||||||
<Sorting
|
<Sorting
|
||||||
bind:isOpen={isSortingOpen}
|
bind:isOpen={isSortingOpen}
|
||||||
presetSorting={sorting}
|
presetSorting={sorting}
|
||||||
|
{globalMetrics}
|
||||||
applySorting={(newSort) =>
|
applySorting={(newSort) =>
|
||||||
sorting = {...newSort}
|
sorting = {...newSort}
|
||||||
}
|
}
|
||||||
@@ -521,6 +531,7 @@
|
|||||||
subCluster={selectedSubCluster}
|
subCluster={selectedSubCluster}
|
||||||
configName="metricConfig_jobListMetrics"
|
configName="metricConfig_jobListMetrics"
|
||||||
footprintSelect
|
footprintSelect
|
||||||
|
{globalMetrics}
|
||||||
applyMetrics={(newMetrics) =>
|
applyMetrics={(newMetrics) =>
|
||||||
metrics = [...newMetrics]
|
metrics = [...newMetrics]
|
||||||
}
|
}
|
||||||
@@ -531,7 +542,8 @@
|
|||||||
bind:isOpen={isHistogramSelectionOpen}
|
bind:isOpen={isHistogramSelectionOpen}
|
||||||
presetSelectedHistograms={selectedHistograms}
|
presetSelectedHistograms={selectedHistograms}
|
||||||
configName="userView_histogramMetrics"
|
configName="userView_histogramMetrics"
|
||||||
|
{globalMetrics}
|
||||||
applyChange={(newSelection) => {
|
applyChange={(newSelection) => {
|
||||||
selectedHistogramsBuffer[selectedCluster || 'all'] = [...newSelection];
|
selectedHistograms = [...newSelection];
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,10 +39,6 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const ccconfig = getContext("cc-config");
|
|
||||||
const initialized = getContext("initialized");
|
|
||||||
const globalMetrics = getContext("globalMetrics");
|
|
||||||
const usePaging = ccconfig?.jobList_usePaging || false;
|
|
||||||
const jobInfoColumnWidth = 250;
|
const jobInfoColumnWidth = 250;
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const query = gql`
|
const query = gql`
|
||||||
@@ -100,11 +96,18 @@
|
|||||||
let headerPaddingTop = $state(0);
|
let headerPaddingTop = $state(0);
|
||||||
let jobs = $state([]);
|
let jobs = $state([]);
|
||||||
let page = $state(1);
|
let page = $state(1);
|
||||||
let itemsPerPage = $state(usePaging ? (ccconfig?.jobList_jobsPerPage || 10) : 10);
|
|
||||||
let triggerMetricRefresh = $state(false);
|
let triggerMetricRefresh = $state(false);
|
||||||
let tableWidth = $state(0);
|
let tableWidth = $state(0);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const initialized = $derived(getContext("initialized") || false);
|
||||||
|
const ccconfig = $derived(initialized ? getContext("cc-config") : null);
|
||||||
|
const globalMetrics = $derived(initialized ? getContext("globalMetrics") : null);
|
||||||
|
const clusterInfos = $derived(initialized ? getContext("clusters"): null);
|
||||||
|
const resampleConfig = $derived(initialized ? getContext("resampling") : null);
|
||||||
|
const usePaging = $derived(ccconfig?.jobList_usePaging || false);
|
||||||
|
|
||||||
|
let itemsPerPage = $derived(usePaging ? (ccconfig?.jobList_jobsPerPage || 10) : 10);
|
||||||
let filter = $derived([...filterBuffer]);
|
let filter = $derived([...filterBuffer]);
|
||||||
let paging = $derived({ itemsPerPage, page });
|
let paging = $derived({ itemsPerPage, page });
|
||||||
const plotWidth = $derived.by(() => {
|
const plotWidth = $derived.by(() => {
|
||||||
@@ -274,7 +277,7 @@
|
|||||||
style="width: {plotWidth}px; padding-top: {headerPaddingTop}px"
|
style="width: {plotWidth}px; padding-top: {headerPaddingTop}px"
|
||||||
>
|
>
|
||||||
{metric}
|
{metric}
|
||||||
{#if $initialized}
|
{#if initialized}
|
||||||
({getUnit(metric)})
|
({getUnit(metric)})
|
||||||
{/if}
|
{/if}
|
||||||
</th>
|
</th>
|
||||||
@@ -292,7 +295,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{:else}
|
{:else}
|
||||||
{#each jobs as job (job.id)}
|
{#each jobs as job (job.id)}
|
||||||
<JobListRow {triggerMetricRefresh} {job} {metrics} {plotWidth} {showFootprint} previousSelect={selectedJobs.includes(job.id)}
|
<JobListRow {triggerMetricRefresh} {job} {metrics} {plotWidth} {showFootprint} {globalMetrics} {clusterInfos} {resampleConfig}
|
||||||
|
previousSelect={selectedJobs.includes(job.id)}
|
||||||
selectJob={(detail) => selectedJobs = [...selectedJobs, detail]}
|
selectJob={(detail) => selectedJobs = [...selectedJobs, detail]}
|
||||||
unselectJob={(detail) => selectedJobs = selectedJobs.filter(item => item !== detail)}
|
unselectJob={(detail) => selectedJobs = selectedJobs.filter(item => item !== detail)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,11 +30,10 @@
|
|||||||
setFilter
|
setFilter
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
|
||||||
const clusters = getContext("clusters");
|
|
||||||
const initialized = getContext("initialized");
|
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const initialized = $derived(getContext("initialized") || false);
|
||||||
|
const clusterInfos = $derived($initialized ? getContext("clusters") : null);
|
||||||
let pendingCluster = $derived(presetCluster);
|
let pendingCluster = $derived(presetCluster);
|
||||||
let pendingPartition = $derived(presetPartition);
|
let pendingPartition = $derived(presetPartition);
|
||||||
</script>
|
</script>
|
||||||
@@ -56,7 +55,7 @@
|
|||||||
>
|
>
|
||||||
Any Cluster
|
Any Cluster
|
||||||
</ListGroupItem>
|
</ListGroupItem>
|
||||||
{#each clusters as cluster}
|
{#each clusterInfos as cluster}
|
||||||
<ListGroupItem
|
<ListGroupItem
|
||||||
disabled={disableClusterSelection}
|
disabled={disableClusterSelection}
|
||||||
active={pendingCluster == cluster.name}
|
active={pendingCluster == cluster.name}
|
||||||
@@ -80,7 +79,7 @@
|
|||||||
>
|
>
|
||||||
Any Partition
|
Any Partition
|
||||||
</ListGroupItem>
|
</ListGroupItem>
|
||||||
{#each clusters?.find((c) => c.name == pendingCluster)?.partitions as partition}
|
{#each clusterInfos?.find((c) => c.name == pendingCluster)?.partitions as partition}
|
||||||
<ListGroupItem
|
<ListGroupItem
|
||||||
active={pendingPartition == partition}
|
active={pendingPartition == partition}
|
||||||
onclick={() => (pendingPartition = partition)}
|
onclick={() => (pendingPartition = partition)}
|
||||||
|
|||||||
@@ -42,8 +42,8 @@
|
|||||||
contains: "Contains",
|
contains: "Contains",
|
||||||
}
|
}
|
||||||
|
|
||||||
const findMaxNumAccels = (clusters) =>
|
const findMaxNumAccels = (infos) =>
|
||||||
clusters.reduce(
|
infos.reduce(
|
||||||
(max, cluster) =>
|
(max, cluster) =>
|
||||||
Math.max(
|
Math.max(
|
||||||
max,
|
max,
|
||||||
@@ -56,8 +56,8 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Limited to Single-Node Thread Count
|
// Limited to Single-Node Thread Count
|
||||||
const findMaxNumHWThreadsPerNode = (clusters) =>
|
const findMaxNumHWThreadsPerNode = (infos) =>
|
||||||
clusters.reduce(
|
infos.reduce(
|
||||||
(max, cluster) =>
|
(max, cluster) =>
|
||||||
Math.max(
|
Math.max(
|
||||||
max,
|
max,
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
let threadState = $derived(presetNumHWThreads);
|
let threadState = $derived(presetNumHWThreads);
|
||||||
let accState = $derived(presetNumAccelerators);
|
let accState = $derived(presetNumAccelerators);
|
||||||
|
|
||||||
const clusters = $derived(getContext("clusters"));
|
const initialized = $derived(getContext("initialized") || false);
|
||||||
const initialized = $derived(getContext("initialized"));
|
const clusterInfos = $derived($initialized ? getContext("clusters") : null);
|
||||||
// Is Selection Active
|
// Is Selection Active
|
||||||
const nodesActive = $derived(!(JSON.stringify(nodesState) === JSON.stringify({ from: 1, to: maxNumNodes })));
|
const nodesActive = $derived(!(JSON.stringify(nodesState) === JSON.stringify({ from: 1, to: maxNumNodes })));
|
||||||
const threadActive = $derived(!(JSON.stringify(threadState) === JSON.stringify({ from: 1, to: maxNumHWThreads })));
|
const threadActive = $derived(!(JSON.stringify(threadState) === JSON.stringify({ from: 1, to: maxNumHWThreads })));
|
||||||
@@ -109,12 +109,12 @@
|
|||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($initialized) {
|
if ($initialized) {
|
||||||
if (activeCluster != null) {
|
if (activeCluster != null) {
|
||||||
const { subClusters } = clusters.find((c) => c.name == activeCluster);
|
const { subClusters } = clusterInfos.find((c) => c.name == activeCluster);
|
||||||
maxNumAccelerators = findMaxNumAccels([{ subClusters }]);
|
maxNumAccelerators = findMaxNumAccels([{ subClusters }]);
|
||||||
maxNumHWThreads = findMaxNumHWThreadsPerNode([{ subClusters }]);
|
maxNumHWThreads = findMaxNumHWThreadsPerNode([{ subClusters }]);
|
||||||
} else if (clusters.length > 0) {
|
} else if (clusterInfos.length > 0) {
|
||||||
maxNumAccelerators = findMaxNumAccels(clusters);
|
maxNumAccelerators = findMaxNumAccels(clusterInfos);
|
||||||
maxNumHWThreads = findMaxNumHWThreadsPerNode(clusters);
|
maxNumHWThreads = findMaxNumHWThreadsPerNode(clusterInfos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
const allTags = $derived(getContext("tags"))
|
const initialized = $derived(getContext("initialized") || false)
|
||||||
const initialized = $derived(getContext("initialized"))
|
const allTags = $derived($initialized ? getContext("tags") : [])
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let searchTerm = $state("");
|
let searchTerm = $state("");
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
const allTags = $derived(getContext('tags'));
|
const initialized = $derived(getContext('initialized') || false);
|
||||||
const initialized = $derived(getContext('initialized'));
|
const allTags = $derived($initialized ? getContext('tags') : []);
|
||||||
|
|
||||||
/* Effects */
|
/* Effects */
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -48,8 +48,6 @@
|
|||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let initialized = getContext("initialized")
|
|
||||||
let allTags = getContext("tags")
|
|
||||||
let newTagType = $state("");
|
let newTagType = $state("");
|
||||||
let newTagName = $state("");
|
let newTagName = $state("");
|
||||||
let filterTerm = $state("");
|
let filterTerm = $state("");
|
||||||
@@ -57,10 +55,13 @@
|
|||||||
let isOpen = $state(false);
|
let isOpen = $state(false);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const initialized = $derived(getContext("initialized") || false );
|
||||||
|
let allTags = $derived(initialized ? getContext("tags") : [])
|
||||||
let newTagScope = $derived(username);
|
let newTagScope = $derived(username);
|
||||||
|
|
||||||
const isAdmin = $derived((roles && authlevel == roles.admin));
|
const isAdmin = $derived((roles && authlevel == roles.admin));
|
||||||
const isSupport = $derived((roles && authlevel == roles.support));
|
const isSupport = $derived((roles && authlevel == roles.support));
|
||||||
const allTagsFiltered = $derived(($initialized, jobTags, fuzzySearchTags(filterTerm, allTags))); // $init und JobTags only for triggering react
|
const allTagsFiltered = $derived((initialized, jobTags, fuzzySearchTags(filterTerm, allTags))); // $init und JobTags only for triggering react
|
||||||
const usedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'used', isAdmin, isSupport));
|
const usedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'used', isAdmin, isSupport));
|
||||||
const unusedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'unused', isAdmin, isSupport));
|
const unusedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'unused', isAdmin, isSupport));
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,13 @@
|
|||||||
- `triggerMetricRefresh Bool?`: If changed to true from upstream, will trigger metric query [Default: false]
|
- `triggerMetricRefresh Bool?`: If changed to true from upstream, will trigger metric query [Default: false]
|
||||||
- `selectJob Func`: The callback function to select a job for comparison
|
- `selectJob Func`: The callback function to select a job for comparison
|
||||||
- `unselectJob Func`: The callback function to unselect a job from comparison
|
- `unselectJob Func`: The callback function to unselect a job from comparison
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
|
- `clusterInfos [Obj]`: Includes the backend supplied cluster topology
|
||||||
|
- `resampleConfig [Obj]`: Includes the backend supplied resampling info
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||||
import { getContext } from "svelte";
|
|
||||||
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
||||||
import { maxScope, checkMetricDisabled } from "../utils.js";
|
import { maxScope, checkMetricDisabled } from "../utils.js";
|
||||||
import JobInfo from "./JobInfo.svelte";
|
import JobInfo from "./JobInfo.svelte";
|
||||||
@@ -33,13 +35,13 @@
|
|||||||
triggerMetricRefresh = false,
|
triggerMetricRefresh = false,
|
||||||
selectJob,
|
selectJob,
|
||||||
unselectJob,
|
unselectJob,
|
||||||
|
globalMetrics,
|
||||||
|
clusterInfos,
|
||||||
|
resampleConfig
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const cluster = getContext("clusters");
|
|
||||||
const resampleConfig = getContext("resampling") || null;
|
|
||||||
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
|
||||||
const query = gql`
|
const query = gql`
|
||||||
query ($id: ID!, $metrics: [String!]!, $scopes: [MetricScope!]!, $selectedResolution: Int) {
|
query ($id: ID!, $metrics: [String!]!, $scopes: [MetricScope!]!, $selectedResolution: Int) {
|
||||||
jobMetrics(id: $id, metrics: $metrics, scopes: $scopes, resolution: $selectedResolution) {
|
jobMetrics(id: $id, metrics: $metrics, scopes: $scopes, resolution: $selectedResolution) {
|
||||||
@@ -73,11 +75,11 @@
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let selectedResolution = $state(resampleDefault);
|
|
||||||
let zoomStates = $state({});
|
let zoomStates = $state({});
|
||||||
let thresholdStates = $state({});
|
let thresholdStates = $state({});
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
|
const resampleDefault = $derived(resampleConfig ? Math.max(...resampleConfig.resolutions) : 0);
|
||||||
const jobId = $derived(job?.id);
|
const jobId = $derived(job?.id);
|
||||||
const scopes = $derived.by(() => {
|
const scopes = $derived.by(() => {
|
||||||
if (job.numNodes == 1) {
|
if (job.numNodes == 1) {
|
||||||
@@ -87,6 +89,8 @@
|
|||||||
return ["node"];
|
return ["node"];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let selectedResolution = $derived(resampleDefault);
|
||||||
let isSelected = $derived(previousSelect);
|
let isSelected = $derived(previousSelect);
|
||||||
let metricsQuery = $derived(queryStore({
|
let metricsQuery = $derived(queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
@@ -94,6 +98,7 @@
|
|||||||
variables: { id: jobId, metrics, scopes, selectedResolution },
|
variables: { id: jobId, metrics, scopes, selectedResolution },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const refinedData = $derived($metricsQuery?.data?.jobMetrics ? sortAndSelectScope($metricsQuery.data.jobMetrics) : []);
|
const refinedData = $derived($metricsQuery?.data?.jobMetrics ? sortAndSelectScope($metricsQuery.data.jobMetrics) : []);
|
||||||
|
|
||||||
/* Effects */
|
/* Effects */
|
||||||
@@ -160,6 +165,7 @@
|
|||||||
return {
|
return {
|
||||||
name: jobMetric.data.name,
|
name: jobMetric.data.name,
|
||||||
disabled: checkMetricDisabled(
|
disabled: checkMetricDisabled(
|
||||||
|
globalMetrics,
|
||||||
jobMetric.data.name,
|
jobMetric.data.name,
|
||||||
job.cluster,
|
job.cluster,
|
||||||
job.subCluster,
|
job.subCluster,
|
||||||
@@ -220,7 +226,7 @@
|
|||||||
series={metric.data.metric.series}
|
series={metric.data.metric.series}
|
||||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||||
metric={metric.data.name}
|
metric={metric.data.name}
|
||||||
cluster={cluster.find((c) => c.name == job.cluster)}
|
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
||||||
subCluster={job.subCluster}
|
subCluster={job.subCluster}
|
||||||
isShared={job.shared != "none"}
|
isShared={job.shared != "none"}
|
||||||
numhwthreads={job.numHWThreads}
|
numhwthreads={job.numHWThreads}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
- `ìsOpen Bool`: Is selection opened [Bindable]
|
- `ìsOpen Bool`: Is selection opened [Bindable]
|
||||||
- `configName String`: The config id string to be updated in database on selection change
|
- `configName String`: The config id string to be updated in database on selection change
|
||||||
- `presetSelectedHistograms [String]`: The currently selected metrics to display as histogram
|
- `presetSelectedHistograms [String]`: The currently selected metrics to display as histogram
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
- `applyChange Func`: The callback function to apply current selection
|
- `applyChange Func`: The callback function to apply current selection
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@@ -24,10 +25,11 @@
|
|||||||
|
|
||||||
/* Svelte 5 Props */
|
/* Svelte 5 Props */
|
||||||
let {
|
let {
|
||||||
cluster,
|
cluster = "",
|
||||||
isOpen = $bindable(),
|
isOpen = $bindable(),
|
||||||
configName,
|
configName,
|
||||||
presetSelectedHistograms,
|
presetSelectedHistograms,
|
||||||
|
globalMetrics,
|
||||||
applyChange
|
applyChange
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
@@ -42,11 +44,11 @@
|
|||||||
function loadHistoMetrics(thisCluster) {
|
function loadHistoMetrics(thisCluster) {
|
||||||
// isInit Check Removed: Parent Component has finished Init-Query: Globalmetrics available here.
|
// isInit Check Removed: Parent Component has finished Init-Query: Globalmetrics available here.
|
||||||
if (!thisCluster) {
|
if (!thisCluster) {
|
||||||
return getContext("globalMetrics")
|
return globalMetrics
|
||||||
.filter((gm) => gm?.footprint)
|
.filter((gm) => gm?.footprint)
|
||||||
.map((fgm) => { return fgm.name })
|
.map((fgm) => { return fgm.name })
|
||||||
} else {
|
} else {
|
||||||
return getContext("globalMetrics")
|
return globalMetrics
|
||||||
.filter((gm) => gm?.availability.find((av) => av.cluster == thisCluster))
|
.filter((gm) => gm?.availability.find((av) => av.cluster == thisCluster))
|
||||||
.filter((agm) => agm?.footprint)
|
.filter((agm) => agm?.footprint)
|
||||||
.map((afgm) => { return afgm.name })
|
.map((afgm) => { return afgm.name })
|
||||||
|
|||||||
@@ -9,13 +9,12 @@
|
|||||||
- `cluster String?`: The currently selected cluster [Default: null]
|
- `cluster String?`: The currently selected cluster [Default: null]
|
||||||
- `subCluster String?`: The currently selected subCluster [Default: null]
|
- `subCluster String?`: The currently selected subCluster [Default: null]
|
||||||
- `footprintSelect Bool?`: Render checkbox for footprint display in upstream component [Default: false]
|
- `footprintSelect Bool?`: Render checkbox for footprint display in upstream component [Default: false]
|
||||||
- `preInitialized Bool?`: If the parent component has a dedicated call to init() [Default: false]
|
|
||||||
- `configName String`: The config key for the last saved selection (constant)
|
- `configName String`: The config key for the last saved selection (constant)
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
- `applyMetrics Func`: The callback function to apply current selection
|
- `applyMetrics Func`: The callback function to apply current selection
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte";
|
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
@@ -35,14 +34,12 @@
|
|||||||
cluster = null,
|
cluster = null,
|
||||||
subCluster = null,
|
subCluster = null,
|
||||||
footprintSelect = false,
|
footprintSelect = false,
|
||||||
preInitialized = false, // Job View is Pre-Init'd: $initialized "alone" store returns false
|
|
||||||
configName,
|
configName,
|
||||||
|
globalMetrics,
|
||||||
applyMetrics
|
applyMetrics
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const globalMetrics = getContext("globalMetrics");
|
|
||||||
const initialized = getContext("initialized");
|
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const updateConfigurationMutation = ({ name, value }) => {
|
const updateConfigurationMutation = ({ name, value }) => {
|
||||||
return mutationStore({
|
return mutationStore({
|
||||||
@@ -58,27 +55,23 @@
|
|||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let pendingShowFootprint = $state(!!showFootprint);
|
let pendingShowFootprint = $state(!!showFootprint);
|
||||||
let listedMetrics = $state([]);
|
|
||||||
let columnHovering = $state(null);
|
let columnHovering = $state(null);
|
||||||
|
|
||||||
/* Derives States */
|
/* Derives States */
|
||||||
let pendingMetrics = $derived(presetMetrics);
|
const allMetrics = $derived(loadAvailable(globalMetrics));
|
||||||
const allMetrics = $derived(loadAvailable(preInitialized || $initialized));
|
let pendingMetrics = $derived(presetMetrics || []);
|
||||||
|
let listedMetrics = $derived([...presetMetrics, ...allMetrics.difference(new Set(presetMetrics))]); // List (preset) active metrics first, then list inactives
|
||||||
|
|
||||||
/* Reactive Effects */
|
/* Reactive Effects */
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
totalMetrics = allMetrics?.size || 0;
|
totalMetrics = allMetrics?.size || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
listedMetrics = [...presetMetrics, ...allMetrics.difference(new Set(presetMetrics))]; // List (preset) active metrics first, then list inactives
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
function loadAvailable(init) {
|
function loadAvailable(gms) {
|
||||||
const availableMetrics = new Set();
|
const availableMetrics = new Set();
|
||||||
if (init) {
|
if (gms) {
|
||||||
for (let gm of globalMetrics) {
|
for (let gm of gms) {
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
availableMetrics.add(gm.name)
|
availableMetrics.add(gm.name)
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +83,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return availableMetrics
|
return availableMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
function printAvailability(metric, cluster) {
|
function printAvailability(metric, cluster) {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
- `presetSorting Object?`: The latest sort selection state
|
- `presetSorting Object?`: The latest sort selection state
|
||||||
- Default { field: "startTime", type: "col", order: "DESC" }
|
- Default { field: "startTime", type: "col", order: "DESC" }
|
||||||
- `isOpen Bool?`: Is modal opened [Bindable, Default: false]
|
- `isOpen Bool?`: Is modal opened [Bindable, Default: false]
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
- `applySorting Func`: The callback function to apply current selection
|
- `applySorting Func`: The callback function to apply current selection
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte";
|
|
||||||
import {
|
import {
|
||||||
Icon,
|
Icon,
|
||||||
Button,
|
Button,
|
||||||
@@ -25,12 +25,11 @@
|
|||||||
let {
|
let {
|
||||||
isOpen = $bindable(false),
|
isOpen = $bindable(false),
|
||||||
presetSorting = { field: "startTime", type: "col", order: "DESC" },
|
presetSorting = { field: "startTime", type: "col", order: "DESC" },
|
||||||
|
globalMetrics,
|
||||||
applySorting
|
applySorting
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const initialized = getContext("initialized");
|
|
||||||
const globalMetrics = getContext("globalMetrics");
|
|
||||||
const fixedSortables = $state([
|
const fixedSortables = $state([
|
||||||
{ field: "startTime", type: "col", text: "Start Time (Default)", order: "DESC" },
|
{ field: "startTime", type: "col", text: "Start Time (Default)", order: "DESC" },
|
||||||
{ field: "duration", type: "col", text: "Duration", order: "DESC" },
|
{ field: "duration", type: "col", text: "Duration", order: "DESC" },
|
||||||
@@ -42,22 +41,11 @@
|
|||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let activeColumnIdx = $state(0);
|
let activeColumnIdx = $state(0);
|
||||||
let metricSortables = $state([]);
|
|
||||||
|
|
||||||
/* Derived */
|
/* Derived */
|
||||||
let sorting = $derived({...presetSorting})
|
let sorting = $derived({...presetSorting})
|
||||||
let sortableColumns = $derived([...fixedSortables, ...metricSortables]);
|
let metricSortables = $derived.by(() => {
|
||||||
|
return globalMetrics.map((gm) => {
|
||||||
/* Effect */
|
|
||||||
$effect(() => {
|
|
||||||
if ($initialized) {
|
|
||||||
loadMetricSortables();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Functions */
|
|
||||||
function loadMetricSortables() {
|
|
||||||
metricSortables = globalMetrics.map((gm) => {
|
|
||||||
if (gm?.footprint) {
|
if (gm?.footprint) {
|
||||||
return {
|
return {
|
||||||
field: gm.name + '_' + gm.footprint,
|
field: gm.name + '_' + gm.footprint,
|
||||||
@@ -68,8 +56,10 @@
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}).filter((r) => r != null)
|
}).filter((r) => r != null)
|
||||||
};
|
});
|
||||||
|
let sortableColumns = $derived([...fixedSortables, ...metricSortables]);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function loadActiveIndex() {
|
function loadActiveIndex() {
|
||||||
activeColumnIdx = sortableColumns.findIndex(
|
activeColumnIdx = sortableColumns.findIndex(
|
||||||
(col) => col.field == sorting.field,
|
(col) => col.field == sorting.field,
|
||||||
|
|||||||
@@ -302,19 +302,17 @@ export function stickyHeader(datatableHeaderSelector, updatePading) {
|
|||||||
onDestroy(() => document.removeEventListener("scroll", onscroll));
|
onDestroy(() => document.removeEventListener("scroll", onscroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkMetricDisabled(m, c, s) { // [m]etric, [c]luster, [s]ubcluster
|
export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
|
||||||
const metrics = getContext("globalMetrics");
|
const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
||||||
const available = metrics?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
|
||||||
// Return inverse logic
|
// Return inverse logic
|
||||||
return !available
|
return !available
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkMetricsDisabled(ma, c, s) { // [m]etric[a]rray, [c]luster, [s]ubcluster
|
export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
|
||||||
let result = {};
|
let result = {};
|
||||||
const metrics = getContext("globalMetrics");
|
|
||||||
ma.forEach((m) => {
|
ma.forEach((m) => {
|
||||||
// Return named inverse logic: !available
|
// Return named inverse logic: !available
|
||||||
result[m] = !(metrics?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
result[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
||||||
});
|
});
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
- `job Object`: The GQL job object
|
- `job Object`: The GQL job object
|
||||||
- `clusters Array`: The GQL clusters array
|
- `clusterInfo Array`: The GQL clusters array
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
/* Svelte 5 Props */
|
/* Svelte 5 Props */
|
||||||
let {
|
let {
|
||||||
job,
|
job,
|
||||||
clusters,
|
clusterInfo,
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<div bind:clientWidth={roofWidth}>
|
<div bind:clientWidth={roofWidth}>
|
||||||
<Roofline
|
<Roofline
|
||||||
width={roofWidth}
|
width={roofWidth}
|
||||||
subCluster={clusters
|
subCluster={clusterInfo
|
||||||
.find((c) => c.name == job.cluster)
|
.find((c) => c.name == job.cluster)
|
||||||
.subClusters.find((sc) => sc.name == job.subCluster)}
|
.subClusters.find((sc) => sc.name == job.subCluster)}
|
||||||
data={transformDataForRoofline(
|
data={transformDataForRoofline(
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
- `job Object`: The job object
|
- `job Object`: The job object
|
||||||
- `clusters Object`: The clusters object
|
- `clusterInfo Object`: The clusters object
|
||||||
- `tabActive bool`: Boolean if StatsTabe Tab is Active on Creation
|
- `tabActive bool`: Boolean if StatsTabe Tab is Active on Creation
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
|
- `ccconfig Object?`: The ClusterCockpit Config Context
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -13,7 +15,6 @@
|
|||||||
gql,
|
gql,
|
||||||
getContextClient
|
getContextClient
|
||||||
} from "@urql/svelte";
|
} from "@urql/svelte";
|
||||||
import { getContext } from "svelte";
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Button,
|
Button,
|
||||||
@@ -29,8 +30,10 @@
|
|||||||
/* Svelte 5 Props */
|
/* Svelte 5 Props */
|
||||||
let {
|
let {
|
||||||
job,
|
job,
|
||||||
clusters,
|
clusterInfo,
|
||||||
tabActive,
|
tabActive,
|
||||||
|
globalMetrics,
|
||||||
|
ccconfig
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
@@ -55,65 +58,73 @@
|
|||||||
|
|
||||||
/* State Init */
|
/* State Init */
|
||||||
let moreScopes = $state(false);
|
let moreScopes = $state(false);
|
||||||
let selectedScopes = $state([]);
|
|
||||||
let selectedMetrics = $state([]);
|
|
||||||
let totalMetrics = $state(0); // For Info Only, filled by MetricSelection Component
|
let totalMetrics = $state(0); // For Info Only, filled by MetricSelection Component
|
||||||
let isMetricSelectionOpen = $state(false);
|
let isMetricSelectionOpen = $state(false);
|
||||||
|
|
||||||
/* Derived */
|
/* Derived Var Preprocessing*/
|
||||||
const scopedStats = $derived(queryStore({
|
let selectedTableMetrics = $derived.by(() => {
|
||||||
client: client,
|
if(job && ccconfig) {
|
||||||
query: query,
|
if (job.cluster) {
|
||||||
variables: { dbid: job.id, selectedMetrics, selectedScopes },
|
if (job.subCluster) {
|
||||||
})
|
return ccconfig[`metricConfig_jobViewTableMetrics:${job.cluster}:${job.subCluster}`] ||
|
||||||
);
|
ccconfig[`metricConfig_jobViewTableMetrics:${job.cluster}`] ||
|
||||||
|
ccconfig.metricConfig_jobViewTableMetrics
|
||||||
/* Functions */
|
}
|
||||||
function loadScopes() {
|
return ccconfig[`metricConfig_jobViewTableMetrics:${job.cluster}`] ||
|
||||||
// Archived Jobs Load All Scopes By Default (See Backend)
|
ccconfig.metricConfig_jobViewTableMetrics
|
||||||
moreScopes = true;
|
}
|
||||||
selectedScopes = ["node", "socket", "core", "hwthread", "accelerator"];
|
return ccconfig.metricConfig_jobViewTableMetrics
|
||||||
};
|
}
|
||||||
|
return [];
|
||||||
/* On Init */
|
});
|
||||||
// Handle Job Query on Init -> is not executed anymore
|
|
||||||
getContext("on-init")(() => {
|
|
||||||
if (!job) return;
|
|
||||||
|
|
||||||
const pendingMetrics = (
|
|
||||||
getContext("cc-config")[`metricConfig_jobViewTableMetrics:${job.cluster}:${job.subCluster}`] ||
|
|
||||||
getContext("cc-config")[`metricConfig_jobViewTableMetrics:${job.cluster}`]
|
|
||||||
) || getContext("cc-config")["metricConfig_jobViewTableMetrics"];
|
|
||||||
|
|
||||||
|
let selectedTableScopes = $derived.by(() => {
|
||||||
|
if (job) {
|
||||||
|
if (!moreScopes) {
|
||||||
// Select default Scopes to load: Check before if any metric has accelerator scope by default
|
// Select default Scopes to load: Check before if any metric has accelerator scope by default
|
||||||
const accScopeDefault = [...pendingMetrics].some(function (m) {
|
const pendingScopes = ["node"]
|
||||||
const cluster = clusters.find((c) => c.name == job.cluster);
|
const accScopeDefault = [...selectedTableMetrics].some(function (m) {
|
||||||
|
const cluster = clusterInfo.find((c) => c.name == job.cluster);
|
||||||
const subCluster = cluster.subClusters.find((sc) => sc.name == job.subCluster);
|
const subCluster = cluster.subClusters.find((sc) => sc.name == job.subCluster);
|
||||||
return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator";
|
return subCluster.metricConfig.find((smc) => smc.name == m)?.scope === "accelerator";
|
||||||
});
|
});
|
||||||
|
|
||||||
const pendingScopes = ["node"]
|
|
||||||
if (job.numNodes === 1) {
|
if (job.numNodes === 1) {
|
||||||
pendingScopes.push("socket")
|
pendingScopes.push("socket")
|
||||||
pendingScopes.push("core")
|
pendingScopes.push("core")
|
||||||
pendingScopes.push("hwthread")
|
pendingScopes.push("hwthread")
|
||||||
if (accScopeDefault) { pendingScopes.push("accelerator") }
|
if (accScopeDefault) { pendingScopes.push("accelerator") }
|
||||||
}
|
}
|
||||||
|
return[...new Set(pendingScopes)];
|
||||||
selectedMetrics = [...pendingMetrics];
|
} else {
|
||||||
selectedScopes = [...pendingScopes];
|
// If flag set: Always load all scopes
|
||||||
|
return ["node", "socket", "core", "hwthread", "accelerator"];
|
||||||
|
}
|
||||||
|
} // Fallback
|
||||||
|
return ["node"]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Derived Query */
|
||||||
|
const scopedStats = $derived(queryStore({
|
||||||
|
client: client,
|
||||||
|
query: query,
|
||||||
|
variables: {
|
||||||
|
dbid: job.id,
|
||||||
|
selectedMetrics: selectedTableMetrics,
|
||||||
|
selectedScopes: selectedTableScopes
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TabPane tabId="stats" tab="Statistics Table" class="overflow-x-auto" active={tabActive}>
|
<TabPane tabId="stats" tab="Statistics Table" class="overflow-x-auto" active={tabActive}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col class="m-2">
|
<Col class="m-2">
|
||||||
<Button outline onclick={() => (isMetricSelectionOpen = true)} class="px-2" color="primary" style="margin-right:0.5rem">
|
<Button outline onclick={() => (isMetricSelectionOpen = true)} class="px-2" color="primary" style="margin-right:0.5rem">
|
||||||
Select Metrics (Selected {selectedMetrics.length} of {totalMetrics} available)
|
Select Metrics (Selected {selectedTableMetrics.length} of {totalMetrics} available)
|
||||||
</Button>
|
</Button>
|
||||||
{#if job.numNodes > 1 && job.state === "running"}
|
{#if job.numNodes > 1 && job.state === "running"}
|
||||||
<Button class="px-2 ml-auto" color="success" outline onclick={loadScopes} disabled={moreScopes}>
|
<Button class="px-2 ml-auto" color="success" outline onclick={() => (moreScopes = !moreScopes)} disabled={moreScopes}>
|
||||||
{#if !moreScopes}
|
{#if !moreScopes}
|
||||||
<Icon name="plus-square-fill" style="margin-right:0.25rem"/> Add More Scopes
|
<Icon name="plus-square-fill" style="margin-right:0.25rem"/> Add More Scopes
|
||||||
{:else}
|
{:else}
|
||||||
@@ -141,7 +152,7 @@
|
|||||||
<StatsTable
|
<StatsTable
|
||||||
hosts={job.resources.map((r) => r.hostname).sort()}
|
hosts={job.resources.map((r) => r.hostname).sort()}
|
||||||
jobStats={$scopedStats?.data?.scopedJobStats}
|
jobStats={$scopedStats?.data?.scopedJobStats}
|
||||||
{selectedMetrics}
|
selectedMetrics={selectedTableMetrics}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</TabPane>
|
</TabPane>
|
||||||
@@ -149,12 +160,12 @@
|
|||||||
<MetricSelection
|
<MetricSelection
|
||||||
bind:isOpen={isMetricSelectionOpen}
|
bind:isOpen={isMetricSelectionOpen}
|
||||||
bind:totalMetrics
|
bind:totalMetrics
|
||||||
presetMetrics={selectedMetrics}
|
presetMetrics={selectedTableMetrics}
|
||||||
cluster={job.cluster}
|
cluster={job.cluster}
|
||||||
subCluster={job.subCluster}
|
subCluster={job.subCluster}
|
||||||
configName="metricConfig_jobViewTableMetrics"
|
configName="metricConfig_jobViewTableMetrics"
|
||||||
preInitialized
|
{globalMetrics}
|
||||||
applyMetrics={(newMetrics) =>
|
applyMetrics={(newMetrics) =>
|
||||||
selectedMetrics = [...newMetrics]
|
selectedTableMetrics = [...newMetrics]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
- `cluster String`: The nodes' cluster
|
- `cluster String`: The nodes' cluster
|
||||||
- `subCluster String`: The nodes' subCluster [Default: ""]
|
- `subCluster String`: The nodes' subCluster [Default: ""]
|
||||||
- `ccconfig Object?`: The ClusterCockpit Config Context [Default: null]
|
- `ccconfig Object?`: The ClusterCockpit Config Context [Default: null]
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
- `pendingSelectedMetrics [String]`: The array of selected metrics [Default []]
|
- `pendingSelectedMetrics [String]`: The array of selected metrics [Default []]
|
||||||
- `selectedResolution Number?`: The selected data resolution [Default: 0]
|
- `selectedResolution Number?`: The selected data resolution [Default: 0]
|
||||||
- `hostnameFilter String?`: The active hostnamefilter [Default: ""]
|
- `hostnameFilter String?`: The active hostnamefilter [Default: ""]
|
||||||
- `hoststateFilter String?`: The active hoststatefilter [Default: ""]
|
- `hoststateFilter String?`: The active hoststatefilter [Default: ""]
|
||||||
- `presetSystemUnits Object`: The object of metric units [Default: null]
|
- `systemUnits Object`: The object of metric units [Default: null]
|
||||||
- `from Date?`: The selected "from" date [Default: null]
|
- `from Date?`: The selected "from" date [Default: null]
|
||||||
- `to Date?`: The selected "to" date [Default: null]
|
- `to Date?`: The selected "to" date [Default: null]
|
||||||
-->
|
-->
|
||||||
@@ -27,11 +28,12 @@
|
|||||||
cluster,
|
cluster,
|
||||||
subCluster = "",
|
subCluster = "",
|
||||||
ccconfig = null,
|
ccconfig = null,
|
||||||
|
globalMetrics = null,
|
||||||
pendingSelectedMetrics = [],
|
pendingSelectedMetrics = [],
|
||||||
selectedResolution = 0,
|
selectedResolution = 0,
|
||||||
hostnameFilter = "",
|
hostnameFilter = "",
|
||||||
hoststateFilter = "",
|
hoststateFilter = "",
|
||||||
presetSystemUnits = null,
|
systemUnits = null,
|
||||||
from = null,
|
from = null,
|
||||||
to = null
|
to = null
|
||||||
} = $props();
|
} = $props();
|
||||||
@@ -236,7 +238,7 @@
|
|||||||
scope="col"
|
scope="col"
|
||||||
style="padding-top: {headerPaddingTop}px"
|
style="padding-top: {headerPaddingTop}px"
|
||||||
>
|
>
|
||||||
{metric} ({presetSystemUnits[metric]})
|
{metric} ({systemUnits[metric]})
|
||||||
</th>
|
</th>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -250,7 +252,7 @@
|
|||||||
</Row>
|
</Row>
|
||||||
{:else}
|
{:else}
|
||||||
{#each nodes as nodeData (nodeData.host)}
|
{#each nodes as nodeData (nodeData.host)}
|
||||||
<NodeListRow {nodeData} {cluster} {selectedMetrics}/>
|
<NodeListRow {nodeData} {cluster} {selectedMetrics} {globalMetrics}/>
|
||||||
{:else}
|
{:else}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan={selectedMetrics.length + 1}> No nodes found </td>
|
<td colspan={selectedMetrics.length + 1}> No nodes found </td>
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
- `hostnameFilter String?`: The active hoststatefilter [Default: ""]
|
- `hostnameFilter String?`: The active hoststatefilter [Default: ""]
|
||||||
- `from Date?`: The selected "from" date [Default: null]
|
- `from Date?`: The selected "from" date [Default: null]
|
||||||
- `to Date?`: The selected "to" date [Default: null]
|
- `to Date?`: The selected "to" date [Default: null]
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte";
|
|
||||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||||
import { Row, Col, Card, CardHeader, CardBody, Spinner, Badge } from "@sveltestrap/sveltestrap";
|
import { Row, Col, Card, CardHeader, CardBody, Spinner, Badge } from "@sveltestrap/sveltestrap";
|
||||||
import { checkMetricDisabled } from "../generic/utils.js";
|
import { checkMetricDisabled } from "../generic/utils.js";
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
hostnameFilter = "",
|
hostnameFilter = "",
|
||||||
hoststateFilter = "",
|
hoststateFilter = "",
|
||||||
from = null,
|
from = null,
|
||||||
to = null
|
to = null,
|
||||||
|
globalMetrics
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Const Init */
|
/* Const Init */
|
||||||
const initialized = getContext("initialized");
|
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
// Node State Colors
|
// Node State Colors
|
||||||
const stateColors = {
|
const stateColors = {
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mappedData = $derived(handleQueryData($initialized, $nodesQuery?.data));
|
const mappedData = $derived(handleQueryData($nodesQuery?.data));
|
||||||
const filteredData = $derived(mappedData.filter((h) => {
|
const filteredData = $derived(mappedData.filter((h) => {
|
||||||
if (hostnameFilter) {
|
if (hostnameFilter) {
|
||||||
if (hoststateFilter == 'all') return h.host.includes(hostnameFilter)
|
if (hoststateFilter == 'all') return h.host.includes(hostnameFilter)
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
function handleQueryData(isInitialized, queryData) {
|
function handleQueryData(queryData) {
|
||||||
let rawData = []
|
let rawData = []
|
||||||
if (queryData) {
|
if (queryData) {
|
||||||
rawData = queryData.nodeMetrics.filter((h) => {
|
rawData = queryData.nodeMetrics.filter((h) => {
|
||||||
@@ -120,7 +120,8 @@
|
|||||||
data: h.metrics.filter(
|
data: h.metrics.filter(
|
||||||
(m) => m?.name == selectedMetric && m.scope == "node",
|
(m) => m?.name == selectedMetric && m.scope == "node",
|
||||||
),
|
),
|
||||||
disabled: isInitialized ? checkMetricDisabled(selectedMetric, cluster, h.subCluster) : null,
|
// TODO: Move To New Func Variant With Disabled Check on WHole Cluster Level: This never Triggers!
|
||||||
|
disabled: checkMetricDisabled(globalMetrics, selectedMetric, cluster, h.subCluster),
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.host.localeCompare(b.host))
|
.sort((a, b) => a.host.localeCompare(b.host))
|
||||||
}
|
}
|
||||||
@@ -163,6 +164,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if item?.data}
|
{#if item?.data}
|
||||||
{#if item.disabled === true}
|
{#if item.disabled === true}
|
||||||
|
<!-- TODO: Will never be Shown: Overview Single Metric Return Will be Null, see Else Case-->
|
||||||
<Card body class="mx-3" color="info"
|
<Card body class="mx-3" color="info"
|
||||||
>Metric disabled for subcluster <code
|
>Metric disabled for subcluster <code
|
||||||
>{selectedMetric}:{item.subCluster}</code
|
>{selectedMetric}:{item.subCluster}</code
|
||||||
@@ -182,7 +184,7 @@
|
|||||||
enableFlip
|
enableFlip
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
{:else if item.disabled === null}
|
{:else}
|
||||||
<Card body class="mx-3" color="info">
|
<Card body class="mx-3" color="info">
|
||||||
Global Metric List Not Initialized
|
Global Metric List Not Initialized
|
||||||
Can not determine {selectedMetric} availability: Please Reload Page
|
Can not determine {selectedMetric} availability: Please Reload Page
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
- `cluster String`: The nodes' cluster
|
- `cluster String`: The nodes' cluster
|
||||||
- `nodeData Object`: The node data object including metric data
|
- `nodeData Object`: The node data object including metric data
|
||||||
- `selectedMetrics [String]`: The array of selected metrics
|
- `selectedMetrics [String]`: The array of selected metrics
|
||||||
|
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
cluster,
|
cluster,
|
||||||
nodeData,
|
nodeData,
|
||||||
selectedMetrics,
|
selectedMetrics,
|
||||||
|
globalMetrics
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
/* Var Init*/
|
/* Var Init*/
|
||||||
@@ -92,6 +94,7 @@
|
|||||||
if (scopedNodeMetric?.data) {
|
if (scopedNodeMetric?.data) {
|
||||||
return {
|
return {
|
||||||
disabled: checkMetricDisabled(
|
disabled: checkMetricDisabled(
|
||||||
|
globalMetrics,
|
||||||
scopedNodeMetric.data.name,
|
scopedNodeMetric.data.name,
|
||||||
cluster,
|
cluster,
|
||||||
nodeData.subCluster,
|
nodeData.subCluster,
|
||||||
|
|||||||
Reference in New Issue
Block a user