2022-06-22 11:20:57 +02:00
|
|
|
<!--
|
|
|
|
@component
|
|
|
|
|
|
|
|
Properties:
|
|
|
|
- job: GraphQL.Job (constant/key)
|
|
|
|
- metrics: [String] (can change)
|
|
|
|
- plotWidth: Number
|
|
|
|
- plotHeight: Number
|
|
|
|
-->
|
|
|
|
|
|
|
|
<script>
|
2023-05-03 16:41:17 +02:00
|
|
|
import { queryStore, gql, getContextClient } from '@urql/svelte'
|
2022-06-22 11:20:57 +02:00
|
|
|
import { getContext } from 'svelte'
|
|
|
|
import { Card, Spinner } from 'sveltestrap'
|
|
|
|
import MetricPlot from '../plots/MetricPlot.svelte'
|
|
|
|
import JobInfo from './JobInfo.svelte'
|
|
|
|
import { maxScope } from '../utils.js'
|
|
|
|
|
|
|
|
export let job
|
|
|
|
export let metrics
|
|
|
|
export let plotWidth
|
|
|
|
export let plotHeight = 275
|
|
|
|
|
|
|
|
let scopes = [job.numNodes == 1 ? 'core' : 'node']
|
|
|
|
|
|
|
|
const cluster = getContext('clusters').find(c => c.name == job.cluster)
|
2023-03-30 15:21:35 +02:00
|
|
|
// Get all MetricConfs which include subCluster-specific settings for this job
|
|
|
|
const metricConfig = getContext('metrics')
|
2023-05-03 16:41:17 +02:00
|
|
|
const metricsQuery = queryStore({
|
|
|
|
client: getContextClient(),
|
|
|
|
query: gql`
|
|
|
|
query($id: ID!, $metrics: [String!]!, $scopes: [MetricScope!]!) {
|
2022-06-22 11:20:57 +02:00
|
|
|
jobMetrics(id: $id, metrics: $metrics, scopes: $scopes) {
|
|
|
|
name
|
2023-03-30 15:21:35 +02:00
|
|
|
scope
|
2022-06-22 11:20:57 +02:00
|
|
|
metric {
|
2023-03-30 15:21:35 +02:00
|
|
|
unit { prefix, base }, timestep
|
2022-06-22 11:20:57 +02:00
|
|
|
statisticsSeries { min, mean, max }
|
|
|
|
series {
|
|
|
|
hostname, id, data
|
|
|
|
statistics { min, avg, max }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-03 16:41:17 +02:00
|
|
|
}`,
|
|
|
|
pause: true,
|
|
|
|
variables: {
|
2022-06-22 11:20:57 +02:00
|
|
|
id: job.id,
|
|
|
|
metrics,
|
2023-05-03 16:41:17 +02:00
|
|
|
scopes}
|
2022-06-22 11:20:57 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
const selectScope = (jobMetrics) => jobMetrics.reduce(
|
2023-03-30 15:21:35 +02:00
|
|
|
(a, b) => maxScope([a.scope, b.scope]) == a.scope
|
2022-06-22 11:20:57 +02:00
|
|
|
? (job.numNodes > 1 ? a : b)
|
|
|
|
: (job.numNodes > 1 ? b : a), jobMetrics[0])
|
|
|
|
|
|
|
|
const sortAndSelectScope = (jobMetrics) => metrics
|
2023-03-30 15:21:35 +02:00
|
|
|
.map(function(name) {
|
|
|
|
// Get MetricConf for this selected/requested metric
|
|
|
|
let thisConfig = metricConfig(cluster, name)
|
|
|
|
let thisSCIndex = thisConfig.subClusters.findIndex(sc => sc.name == job.subCluster)
|
|
|
|
// Check if Subcluster has MetricConf: If not found (index == -1), no further remove flag check required
|
|
|
|
if (thisSCIndex >= 0) {
|
|
|
|
// SubCluster Config present: Check if remove flag is set
|
|
|
|
if (thisConfig.subClusters[thisSCIndex].remove == true) {
|
|
|
|
// Return null data and informational flag
|
|
|
|
return {removed: true, data: null}
|
|
|
|
} else {
|
|
|
|
// load and return metric, if data available
|
|
|
|
let thisMetric = jobMetrics.filter(jobMetric => jobMetric.name == name) // Returns Array
|
|
|
|
if (thisMetric.length > 0) {
|
|
|
|
return {removed: false, data: thisMetric}
|
|
|
|
} else {
|
|
|
|
return {removed: false, data: null}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// No specific subCluster config: 'remove' flag not set, deemed false -> load and return metric, if data available
|
|
|
|
let thisMetric = jobMetrics.filter(jobMetric => jobMetric.name == name) // Returns Array
|
|
|
|
if (thisMetric.length > 0) {
|
|
|
|
return {removed: false, data: thisMetric}
|
|
|
|
} else {
|
|
|
|
return {removed: false, data: null}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(function(jobMetrics) {
|
|
|
|
if (jobMetrics.data != null && jobMetrics.data.length > 0) {
|
2023-03-30 15:23:31 +02:00
|
|
|
return {removed: jobMetrics.removed, data: selectScope(jobMetrics.data)}
|
2023-03-30 15:21:35 +02:00
|
|
|
} else {
|
|
|
|
return jobMetrics
|
|
|
|
}
|
|
|
|
})
|
2022-06-22 11:20:57 +02:00
|
|
|
|
|
|
|
$: metricsQuery.variables = { id: job.id, metrics, scopes }
|
|
|
|
|
|
|
|
if (job.monitoringStatus)
|
2023-05-03 16:41:17 +02:00
|
|
|
$metricsQuery.resume()
|
2022-06-22 11:20:57 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<JobInfo job={job}/>
|
|
|
|
</td>
|
|
|
|
{#if job.monitoringStatus == 0 || job.monitoringStatus == 2}
|
|
|
|
<td colspan="{metrics.length}">
|
|
|
|
<Card body color="warning">Not monitored or archiving failed</Card>
|
|
|
|
</td>
|
|
|
|
{:else if $metricsQuery.fetching}
|
|
|
|
<td colspan="{metrics.length}" style="text-align: center;">
|
|
|
|
<Spinner secondary />
|
|
|
|
</td>
|
|
|
|
{:else if $metricsQuery.error}
|
|
|
|
<td colspan="{metrics.length}">
|
|
|
|
<Card body color="danger" class="mb-3">
|
|
|
|
{$metricsQuery.error.message.length > 500
|
|
|
|
? $metricsQuery.error.message.substring(0, 499)+'...'
|
|
|
|
: $metricsQuery.error.message}
|
|
|
|
</Card>
|
|
|
|
</td>
|
|
|
|
{:else}
|
|
|
|
{#each sortAndSelectScope($metricsQuery.data.jobMetrics) as metric, i (metric || i)}
|
|
|
|
<td>
|
2023-03-30 15:21:35 +02:00
|
|
|
<!-- Subluster Metricconfig remove keyword for jobtables (joblist main, user joblist, project joblist) to be used here as toplevel case-->
|
|
|
|
{#if metric.removed == false && metric.data != null}
|
2022-06-22 11:20:57 +02:00
|
|
|
<MetricPlot
|
|
|
|
width={plotWidth}
|
|
|
|
height={plotHeight}
|
2023-03-30 15:21:35 +02:00
|
|
|
timestep={metric.data.metric.timestep}
|
|
|
|
scope={metric.data.scope}
|
|
|
|
series={metric.data.metric.series}
|
|
|
|
statisticsSeries={metric.data.metric.statisticsSeries}
|
|
|
|
metric={metric.data.name}
|
2022-06-22 11:20:57 +02:00
|
|
|
cluster={cluster}
|
|
|
|
subCluster={job.subCluster} />
|
2023-03-30 15:21:35 +02:00
|
|
|
{:else if metric.removed == true && metric.data == null}
|
|
|
|
<Card body color="info">Metric disabled for subcluster '{ job.subCluster }'</Card>
|
2022-06-22 11:20:57 +02:00
|
|
|
{:else}
|
|
|
|
<Card body color="warning">Missing Data</Card>
|
|
|
|
{/if}
|
|
|
|
</td>
|
|
|
|
{/each}
|
|
|
|
{/if}
|
|
|
|
</tr>
|