@@ -123,9 +118,9 @@
Metric
diff --git a/web/frontend/src/TagManagement.svelte b/web/frontend/src/TagManagement.svelte
index e9fb9e9..afec176 100644
--- a/web/frontend/src/TagManagement.svelte
+++ b/web/frontend/src/TagManagement.svelte
@@ -107,7 +107,6 @@
addTagToJob(res.data.createTag);
} else if (res.fetching === false && res.error) {
throw res.error;
- // console.log('Error on subscription: ' + res.error)
}
});
}
@@ -120,7 +119,6 @@
pendingChange = false;
} else if (res.fetching === false && res.error) {
throw res.error;
- // console.log('Error on subscription: ' + res.error)
}
});
}
@@ -134,7 +132,6 @@
pendingChange = false;
} else if (res.fetching === false && res.error) {
throw res.error;
- // console.log('Error on subscription: ' + res.error)
}
},
);
diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte
index 41969d9..6526e6f 100644
--- a/web/frontend/src/User.root.svelte
+++ b/web/frontend/src/User.root.svelte
@@ -32,7 +32,7 @@
let filterComponent; // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
let jobList;
let jobFilters = [];
- let sorting = { field: "startTime", order: "DESC" },
+ let sorting = { field: "startTime", type: "col", order: "DESC" },
isSortingOpen = false;
let metrics = ccconfig.plot_list_selectedMetrics,
isMetricsSelectionOpen = false;
@@ -70,6 +70,7 @@
histMetrics {
metric
unit
+ stat
data {
min
max
@@ -245,7 +246,7 @@
usesBins={true}
{width}
height={250}
- title="Distribution of '{item.metric}' averages"
+ title="Distribution of '{item.metric} ({item.stat})' footprints"
xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`}
xunit={item.unit}
ylabel="Number of Jobs"
diff --git a/web/frontend/src/Zoom.svelte b/web/frontend/src/Zoom.svelte
deleted file mode 100644
index c5f73c1..0000000
--- a/web/frontend/src/Zoom.svelte
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
- Window Size:
-
-
- ({windowSize}%)
-
-
-
- Window Position:
-
-
-
-
diff --git a/web/frontend/src/config/UserSettings.svelte b/web/frontend/src/config/UserSettings.svelte
index cd1d9a3..7eaa04e 100644
--- a/web/frontend/src/config/UserSettings.svelte
+++ b/web/frontend/src/config/UserSettings.svelte
@@ -23,7 +23,6 @@
popMessage(text, target, "#048109");
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
diff --git a/web/frontend/src/config/admin/AddUser.svelte b/web/frontend/src/config/admin/AddUser.svelte
index 84aacc3..154bb3e 100644
--- a/web/frontend/src/config/admin/AddUser.svelte
+++ b/web/frontend/src/config/admin/AddUser.svelte
@@ -23,7 +23,6 @@
form.reset();
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
diff --git a/web/frontend/src/config/admin/EditProject.svelte b/web/frontend/src/config/admin/EditProject.svelte
index e1c518f..e7d6379 100644
--- a/web/frontend/src/config/admin/EditProject.svelte
+++ b/web/frontend/src/config/admin/EditProject.svelte
@@ -32,7 +32,6 @@
reloadUserList();
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
@@ -64,7 +63,6 @@
reloadUserList();
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
diff --git a/web/frontend/src/config/admin/EditRole.svelte b/web/frontend/src/config/admin/EditRole.svelte
index 6b24e3e..a26c48b 100644
--- a/web/frontend/src/config/admin/EditRole.svelte
+++ b/web/frontend/src/config/admin/EditRole.svelte
@@ -34,7 +34,6 @@
reloadUserList();
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
@@ -66,7 +65,6 @@
reloadUserList();
} else {
let text = await res.text();
- // console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
diff --git a/web/frontend/src/filters/Filters.svelte b/web/frontend/src/filters/Filters.svelte
index 7253ff7..ef92c31 100644
--- a/web/frontend/src/filters/Filters.svelte
+++ b/web/frontend/src/filters/Filters.svelte
@@ -136,8 +136,8 @@
if (filters.project)
items.push({ project: { [filters.projectMatch]: filters.project } });
if (filters.jobName) items.push({ jobName: { contains: filters.jobName } });
- for (let stat of filters.stats)
- items.push({ [stat.field]: { from: stat.from, to: stat.to } });
+ if (filters.stats.length != 0)
+ items.push({ metricStats: filters.stats.map((st) => { return { metricName: st.field, range: { from: st.from, to: st.to }} }) });
dispatch("update", { filters: items });
changeURL();
@@ -412,7 +412,6 @@
/>
update()}
diff --git a/web/frontend/src/filters/Resources.svelte b/web/frontend/src/filters/Resources.svelte
index 01f1c57..19af205 100644
--- a/web/frontend/src/filters/Resources.svelte
+++ b/web/frontend/src/filters/Resources.svelte
@@ -59,7 +59,6 @@
0,
);
- // console.log(header)
let minNumNodes = 1,
maxNumNodes = 0,
minNumHWThreads = 1,
diff --git a/web/frontend/src/filters/Stats.svelte b/web/frontend/src/filters/Stats.svelte
index ee80a4b..b19793f 100644
--- a/web/frontend/src/filters/Stats.svelte
+++ b/web/frontend/src/filters/Stats.svelte
@@ -1,5 +1,6 @@
(isOpen = !isOpen)}>
@@ -126,8 +73,7 @@
color="danger"
on:click={() => {
isOpen = false;
- resetRange($initialized, cluster);
- statistics.forEach((stat) => (stat.enabled = false));
+ resetRanges();
stats = [];
dispatch("update", { stats });
}}>Reset gm.name === m)?.unit
+ return (rawUnit?.prefix ? rawUnit.prefix : "") + (rawUnit?.base ? rawUnit.base : "")
+ }
+
const client = getContextClient();
const query = gql`
query (
@@ -75,7 +80,11 @@
name
}
metaData
- footprint
+ footprint {
+ name
+ stat
+ value
+ }
}
count
hasNextPage
@@ -141,7 +150,6 @@
paging = { itemsPerPage: value, page: page }; // Trigger reload of jobList
} else if (res.fetching === false && res.error) {
throw res.error;
- // console.log('Error on subscription: ' + res.error)
}
});
}
@@ -215,22 +223,7 @@
>
{metric}
{#if $initialized}
- ({clusters
- .map((cluster) =>
- cluster.metricConfig.find((m) => m.name == metric),
- )
- .filter((m) => m != null)
- .map(
- (m) =>
- (m.unit?.prefix ? m.unit?.prefix : "") +
- (m.unit?.base ? m.unit?.base : ""),
- ) // Build unitStr
- .reduce(
- (arr, unitStr) =>
- arr.includes(unitStr) ? arr : [...arr, unitStr],
- [],
- ) // w/o this, output would be [unitStr, unitStr]
- .join(", ")})
+ ({getUnit(metric)})
{/if}
{/each}
diff --git a/web/frontend/src/joblist/Row.svelte b/web/frontend/src/joblist/Row.svelte
index dd92ec4..5d4b2c7 100644
--- a/web/frontend/src/joblist/Row.svelte
+++ b/web/frontend/src/joblist/Row.svelte
@@ -30,16 +30,11 @@
: ["core"]
: ["node"];
- function distinct(value, index, array) {
- return array.indexOf(value) === index;
- }
-
const cluster = getContext("clusters").find((c) => c.name == job.cluster);
- const metricConfig = getContext("metrics"); // Get all MetricConfs which include subCluster-specific settings for this job
const client = getContextClient();
const query = gql`
- query ($id: ID!, $queryMetrics: [String!]!, $scopes: [MetricScope!]!) {
- jobMetrics(id: $id, metrics: $queryMetrics, scopes: $scopes) {
+ query ($id: ID!, $metrics: [String!]!, $scopes: [MetricScope!]!) {
+ jobMetrics(id: $id, metrics: $metrics, scopes: $scopes) {
name
scope
metric {
@@ -71,34 +66,14 @@
$: metricsQuery = queryStore({
client: client,
query: query,
- variables: { id, queryMetrics, scopes },
+ variables: { id, metrics, scopes },
});
-
- let queryMetrics = null;
- $: if (showFootprint) {
- queryMetrics = [
- "cpu_load",
- "flops_any",
- "mem_used",
- "mem_bw",
- "acc_utilization",
- ...metrics,
- ].filter(distinct);
- scopes = ["node"];
- } else {
- queryMetrics = [...metrics];
- scopes = job.numNodes == 1
- ? job.numAcc >= 1
- ? ["core", "accelerator"]
- : ["core"]
- : ["node"];
- }
-
+
export function refresh() {
metricsQuery = queryStore({
client: client,
query: query,
- variables: { id, queryMetrics, scopes },
+ variables: { id, metrics, scopes },
// requestPolicy: 'network-only' // use default cache-first for refresh
});
}
@@ -166,8 +141,8 @@
|
diff --git a/web/frontend/src/joblist/SortSelection.svelte b/web/frontend/src/joblist/SortSelection.svelte
index 2cc8615..ba6f9b8 100644
--- a/web/frontend/src/joblist/SortSelection.svelte
+++ b/web/frontend/src/joblist/SortSelection.svelte
@@ -17,24 +17,39 @@
ModalHeader,
ModalFooter,
} from "@sveltestrap/sveltestrap";
+ import { getContext } from "svelte";
+ import { getSortItems } from "../utils.js";
export let isOpen = false;
- export let sorting = { field: "startTime", order: "DESC" };
+ export let sorting = { field: "startTime", type: "col", order: "DESC" };
- let sortableColumns = [
- { field: "startTime", text: "Start Time", order: "DESC" },
- { field: "duration", text: "Duration", order: "DESC" },
- { field: "numNodes", text: "Number of Nodes", order: "DESC" },
- { field: "memUsedMax", text: "Max. Memory Used", order: "DESC" },
- { field: "flopsAnyAvg", text: "Avg. FLOPs", order: "DESC" },
- { field: "memBwAvg", text: "Avg. Memory Bandwidth", order: "DESC" },
- { field: "netBwAvg", text: "Avg. Network Bandwidth", order: "DESC" },
- ];
+ let sortableColumns = [];
+ let activeColumnIdx;
- let activeColumnIdx = sortableColumns.findIndex(
- (col) => col.field == sorting.field,
- );
- sortableColumns[activeColumnIdx].order = sorting.order;
+ const initialized = getContext("initialized");
+
+ function loadSortables(isInitialized) {
+ if (!isInitialized) return;
+ sortableColumns = [
+ { field: "startTime", type: "col", text: "Start Time", order: "DESC" },
+ { field: "duration", type: "col", text: "Duration", order: "DESC" },
+ { field: "numNodes", type: "col", text: "Number of Nodes", order: "DESC" },
+ { field: "numHwthreads", type: "col", text: "Number of HWThreads", order: "DESC" },
+ { field: "numAcc", type: "col", text: "Number of Accelerators", order: "DESC" },
+ ...getSortItems()
+ ]
+ }
+
+ function loadActiveIndex(isInitialized) {
+ if (!isInitialized) return;
+ activeColumnIdx = sortableColumns.findIndex(
+ (col) => col.field == sorting.field,
+ );
+ sortableColumns[activeColumnIdx].order = sorting.order;
+ }
+
+ $: loadSortables($initialized);
+ $: loadActiveIndex($initialized)
- export function formatTime(t, forNode = false) {
+ function formatTime(t, forNode = false) {
if (t !== null) {
if (isNaN(t)) {
return t;
@@ -15,7 +15,7 @@
}
}
- export function timeIncrs(timestep, maxX, forNode) {
+ function timeIncrs(timestep, maxX, forNode) {
if (forNode === true) {
return [60, 300, 900, 1800, 3600, 7200, 14400, 21600]; // forNode fixed increments
} else {
@@ -27,93 +27,63 @@
}
}
- export function findThresholds(
+ // removed arg "subcluster": input metricconfig and topology now directly derived from subcluster
+ function findThresholds(
+ subClusterTopology,
metricConfig,
scope,
- subCluster,
isShared,
numhwthreads,
numaccs
) {
- // console.log('NAME ' + metricConfig.name + ' / SCOPE ' + scope + ' / SUBCLUSTER ' + subCluster.name)
- if (!metricConfig || !scope || !subCluster) {
+
+ if (!subClusterTopology || !metricConfig || !scope) {
console.warn("Argument missing for findThresholds!");
return null;
}
if (
(scope == "node" && isShared == false) ||
- metricConfig.aggregation == "avg"
+ metricConfig?.aggregation == "avg"
) {
- if (metricConfig.subClusters && metricConfig.subClusters.length === 0) {
- // console.log('subClusterConfigs array empty, use metricConfig defaults')
return {
normal: metricConfig.normal,
caution: metricConfig.caution,
alert: metricConfig.alert,
peak: metricConfig.peak,
};
- } else if (
- metricConfig.subClusters &&
- metricConfig.subClusters.length > 0
- ) {
- // console.log('subClusterConfigs found, use subCluster Settings if matching jobs subcluster:')
- let forSubCluster = metricConfig.subClusters.find(
- (sc) => sc.name == subCluster.name,
- );
- if (
- forSubCluster &&
- forSubCluster.normal &&
- forSubCluster.caution &&
- forSubCluster.alert &&
- forSubCluster.peak
- )
- return forSubCluster;
- else
- return {
- normal: metricConfig.normal,
- caution: metricConfig.caution,
- alert: metricConfig.alert,
- peak: metricConfig.peak,
- };
- } else {
- console.warn("metricConfig.subClusters not found!");
+ }
+
+
+ if (metricConfig?.aggregation == "sum") {
+ let divisor = 1
+ if (isShared == true) { // Shared
+ if (numaccs > 0) divisor = subClusterTopology.accelerators.length / numaccs;
+ else if (numhwthreads > 0) divisor = subClusterTopology.node.length / numhwthreads;
+ }
+ else if (scope == 'socket') divisor = subClusterTopology.socket.length;
+ else if (scope == "core") divisor = subClusterTopology.core.length;
+ else if (scope == "accelerator")
+ divisor = subClusterTopology.accelerators.length;
+ else if (scope == "hwthread") divisor = subClusterTopology.node.length;
+ else {
+ // console.log('TODO: how to calc thresholds for ', scope)
return null;
}
+
+ return {
+ peak: metricConfig.peak / divisor,
+ normal: metricConfig.normal / divisor,
+ caution: metricConfig.caution / divisor,
+ alert: metricConfig.alert / divisor,
+ };
}
- if (metricConfig.aggregation != "sum") {
- console.warn(
- "Missing or unkown aggregation mode (sum/avg) for metric:",
- metricConfig,
- );
- return null;
- }
-
- let divisor = 1
- if (isShared == true) { // Shared
- if (numaccs > 0) divisor = subCluster.topology.accelerators.length / numaccs;
- else if (numhwthreads > 0) divisor = subCluster.topology.node.length / numhwthreads;
- }
- else if (scope == 'socket') divisor = subCluster.topology.socket.length;
- else if (scope == "core") divisor = subCluster.topology.core.length;
- else if (scope == "accelerator")
- divisor = subCluster.topology.accelerators.length;
- else if (scope == "hwthread") divisor = subCluster.topology.node.length;
- else {
- // console.log('TODO: how to calc thresholds for ', scope)
- return null;
- }
-
- let mc =
- metricConfig?.subClusters?.find((sc) => sc.name == subCluster.name) ||
- metricConfig;
- return {
- peak: mc.peak / divisor,
- normal: mc.normal / divisor,
- caution: mc.caution / divisor,
- alert: mc.alert / divisor,
- };
+ console.warn(
+ "Missing or unkown aggregation mode (sum/avg) for metric:",
+ metricConfig,
+ );
+ return null;
}
@@ -165,7 +135,8 @@
if (useStatsSeries == false && series == null) useStatsSeries = true;
- const metricConfig = getContext("metrics")(cluster, metric);
+ const subClusterTopology = getContext("getHardwareTopology")(cluster, subCluster);
+ const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric);
const clusterCockpitConfig = getContext("cc-config");
const resizeSleepTime = 250;
const normalLineColor = "#000000";
@@ -178,11 +149,9 @@
alert: "rgba(255, 0, 0, 0.3)",
};
const thresholds = findThresholds(
+ subClusterTopology,
metricConfig,
scope,
- typeof subCluster == "string"
- ? cluster.subClusters.find((sc) => sc.name == subCluster)
- : subCluster,
isShared,
numhwthreads,
numaccs
@@ -479,8 +448,6 @@
cursor: { drag: { x: true, y: true } },
};
- // console.log(opts)
-
let plotWrapper = null;
let uplot = null;
let timeoutId = null;
diff --git a/web/frontend/src/plots/Polar.svelte b/web/frontend/src/plots/Polar.svelte
index 59f89f3..ae0e249 100644
--- a/web/frontend/src/plots/Polar.svelte
+++ b/web/frontend/src/plots/Polar.svelte
@@ -24,10 +24,11 @@
export let metrics
export let cluster
+ export let subCluster
export let jobMetrics
export let height = 365
- const metricConfig = getContext('metrics')
+ const getMetricConfig = getContext("getMetricConfig")
const labels = metrics.filter(name => {
if (!jobMetrics.find(m => m.name == name && m.scope == "node")) {
@@ -38,7 +39,7 @@
})
const getValuesForStat = (getStat) => labels.map(name => {
- const peak = metricConfig(cluster, name).peak
+ const peak = getMetricConfig(cluster, subCluster, name).peak
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
const value = getStat(metric.metric) / peak
return value <= 1. ? value : 1.
diff --git a/web/frontend/src/plots/Roofline.svelte b/web/frontend/src/plots/Roofline.svelte
index 11d1d25..a05eec3 100644
--- a/web/frontend/src/plots/Roofline.svelte
+++ b/web/frontend/src/plots/Roofline.svelte
@@ -209,7 +209,6 @@
draw: [
(u) => {
// draw roofs when cluster set
- // console.log(u)
if (cluster != null) {
const padding = u._padding; // [top, right, bottom, left]
@@ -237,9 +236,6 @@
true,
);
- // Debug get zoomLevel from browser
- // console.log("Zoom", Math.round(window.devicePixelRatio * 100))
-
if (
scalarKneeX <
width * window.devicePixelRatio -
@@ -323,7 +319,7 @@
};
uplot = new uPlot(opts, plotData, plotWrapper);
} else {
- console.log("No data for roofline!");
+ // console.log("No data for roofline!");
}
}
diff --git a/web/frontend/src/units.js b/web/frontend/src/units.js
index 9a4defd..4c1fea4 100644
--- a/web/frontend/src/units.js
+++ b/web/frontend/src/units.js
@@ -31,3 +31,4 @@ export function scaleNumbers(x, y , p = '') {
return Math.abs(rawYValue) >= 1000 ? `${rawXValue.toExponential()} / ${rawYValue.toExponential()}` : `${rawYValue.toString()} / ${rawYValue.toString()}`
}
+// export const dateToUnixEpoch = (rfc3339) => Math.floor(Date.parse(rfc3339) / 1000);
diff --git a/web/frontend/src/utils.js b/web/frontend/src/utils.js
index 3ab86da..7510ace 100644
--- a/web/frontend/src/utils.js
+++ b/web/frontend/src/utils.js
@@ -6,7 +6,6 @@ import {
} from "@urql/svelte";
import { setContext, getContext, hasContext, onDestroy, tick } from "svelte";
import { readable } from "svelte/store";
-// import { formatNumber } from './units.js'
/*
* Call this function only at component initialization time!
@@ -16,7 +15,9 @@ import { readable } from "svelte/store";
* - Creates a readable store 'initialization' which indicates when the values below can be used.
* - Adds 'tags' to the context (list of all tags)
* - Adds 'clusters' to the context (object with cluster names as keys)
- * - Adds 'metrics' to the context, a function that takes a cluster and metric name and returns the MetricConfig (or undefined)
+ * - Adds 'globalMetrics' to the context (list of globally available metric infos)
+ * - Adds 'getMetricConfig' to the context, a function that takes a cluster, subCluster and metric name and returns the MetricConfig (or undefined)
+ * - Adds 'getHardwareTopology' to the context, a function that takes a cluster nad subCluster and returns the subCluster topology (or undefined)
*/
export function init(extraInitQuery = "") {
const jwt = hasContext("jwt")
@@ -71,11 +72,19 @@ export function init(extraInitQuery = "") {
normal
caution
alert
+ lowerIsBetter
}
footprint
}
}
tags { id, name, type }
+ globalMetrics {
+ name
+ scope
+ footprint
+ unit { base, prefix }
+ availability { cluster, subClusters }
+ }
${extraInitQuery}
}`
)
@@ -91,12 +100,13 @@ export function init(extraInitQuery = "") {
};
};
- const tags = [],
- clusters = [];
- const allMetrics = [];
+ const tags = []
+ const clusters = []
+ const globalMetrics = []
+
setContext("tags", tags);
setContext("clusters", clusters);
- setContext("allmetrics", allMetrics);
+ setContext("globalMetrics", globalMetrics);
setContext("getMetricConfig", (cluster, subCluster, metric) => {
if (typeof cluster !== "object")
cluster = clusters.find((c) => c.name == cluster);
@@ -106,6 +116,15 @@ export function init(extraInitQuery = "") {
return subCluster.metricConfig.find((m) => m.name == metric);
});
+ setContext("getHardwareTopology", (cluster, subCluster) => {
+ if (typeof cluster !== "object")
+ cluster = clusters.find((c) => c.name == cluster);
+
+ if (typeof subCluster !== "object")
+ subCluster = cluster.subClusters.find((sc) => sc.name == subCluster);
+
+ return subCluster?.topology;
+ });
setContext("on-init", (callback) =>
state.fetching ? subscribers.push(callback) : callback(state)
);
@@ -124,32 +143,11 @@ export function init(extraInitQuery = "") {
}
for (let tag of data.tags) tags.push(tag);
+ for (let cluster of data.clusters) clusters.push(cluster);
+ for (let gm of data.globalMetrics) globalMetrics.push(gm);
- let globalmetrics = [];
- for (let cluster of data.clusters) {
- // Add full info to context object
- clusters.push(cluster);
- // Build global metric list with availability for joblist metricselect
- for (let subcluster of cluster.subClusters) {
- for (let scm of subcluster.metricConfig) {
- let match = globalmetrics.find((gm) => gm.name == scm.name);
- if (match) {
- let submatch = match.availability.find((av) => av.cluster == cluster.name);
- if (submatch) {
- submatch.subclusters.push(subcluster.name)
- } else {
- match.availability.push({cluster: cluster.name, subclusters: [subcluster.name]})
- }
- } else {
- globalmetrics.push({name: scm.name, availability: [{cluster: cluster.name, subclusters: [subcluster.name]}]});
- }
- }
- }
- }
- // Add to ctx object
- for (let gm of globalmetrics) allMetrics.push(gm);
-
- console.log('All Metrics List', allMetrics);
+ // Unified Sort
+ globalMetrics.sort((a, b) => a.name.localeCompare(b.name))
state.data = data;
tick().then(() => subscribers.forEach((cb) => cb(state)));
@@ -159,6 +157,7 @@ export function init(extraInitQuery = "") {
query: { subscribe },
tags,
clusters,
+ globalMetrics
};
}
@@ -171,6 +170,11 @@ function fuzzyMatch(term, string) {
return string.toLowerCase().includes(term);
}
+// Use in filter() function to return only unique values
+export function distinct(value, index, array) {
+ return array.indexOf(value) === index;
+}
+
export function fuzzySearchTags(term, tags) {
if (!tags) return [];
@@ -260,56 +264,6 @@ export function minScope(scopes) {
return sm;
}
-export async function fetchMetrics(job, metrics, scopes) {
- if (job.monitoringStatus == 0) return null;
-
- let query = [];
- if (metrics != null) {
- for (let metric of metrics) {
- query.push(`metric=${metric}`);
- }
- }
- if (scopes != null) {
- for (let scope of scopes) {
- query.push(`scope=${scope}`);
- }
- }
-
- try {
- let res = await fetch(
- `/frontend/jobs/metrics/${job.id}${query.length > 0 ? "?" : ""}${query.join(
- "&"
- )}`
- );
- if (res.status != 200) {
- return { error: { status: res.status, message: await res.text() } };
- }
-
- return await res.json();
- } catch (e) {
- return { error: e };
- }
-}
-
-export function fetchMetricsStore() {
- let set = null;
- let prev = { fetching: true, error: null, data: null };
- return [
- readable(prev, (_set) => {
- set = _set;
- }),
- (job, metrics, scopes) =>
- fetchMetrics(job, metrics, scopes).then((res) => {
- let next = { fetching: false, error: res.error, data: res.data };
- if (prev.data && next.data)
- next.data.jobMetrics.push(...prev.data.jobMetrics);
-
- prev = next;
- set(next);
- }),
- ];
-}
-
export function stickyHeader(datatableHeaderSelector, updatePading) {
const header = document.querySelector("header > nav.navbar");
if (!header) return;
@@ -336,22 +290,98 @@ export function stickyHeader(datatableHeaderSelector, updatePading) {
onDestroy(() => document.removeEventListener("scroll", onscroll));
}
-// Outdated: Frontend Will Now Receive final MetricList from backend
export function checkMetricDisabled(m, c, s) { //[m]etric, [c]luster, [s]ubcluster
- const mc = getContext("metrics");
- const thisConfig = mc(c, m);
- let thisSCIndex = -1;
- if (thisConfig) {
- thisSCIndex = thisConfig.subClusters.findIndex(
- (subcluster) => subcluster.name == s
- );
- };
- if (thisSCIndex >= 0) {
- if (thisConfig.subClusters[thisSCIndex].remove == true) {
- return true;
+ const metrics = getContext("globalMetrics");
+ const result = metrics?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
+ return !result
+}
+
+export function getStatsItems() {
+ // console.time('stats')
+ // console.log('getStatsItems ...')
+ const globalMetrics = getContext("globalMetrics")
+ const result = globalMetrics.map((gm) => {
+ if (gm?.footprint) {
+ // Footprint contains suffix naming the used stat-type
+ // console.time('deep')
+ // console.log('Deep Config for', gm.name)
+ const mc = getMetricConfigDeep(gm.name, null, null)
+ // console.timeEnd('deep')
+ return {
+ field: gm.name + '_' + gm.footprint,
+ text: gm.name + ' (' + gm.footprint + ')',
+ metric: gm.name,
+ from: 0,
+ to: mc.peak,
+ peak: mc.peak,
+ enabled: false
+ }
}
+ return null
+ }).filter((r) => r != null)
+ // console.timeEnd('stats')
+ return [...result];
+};
+
+export function getSortItems() {
+ //console.time('sort')
+ //console.log('getSortItems ...')
+ const globalMetrics = getContext("globalMetrics")
+ const result = globalMetrics.map((gm) => {
+ if (gm?.footprint) {
+ // Footprint contains suffix naming the used stat-type
+ return {
+ field: gm.name + '_' + gm.footprint,
+ type: 'foot',
+ text: gm.name + ' (' + gm.footprint + ')',
+ order: 'DESC'
+ }
+ }
+ return null
+ }).filter((r) => r != null)
+ //console.timeEnd('sort')
+ return [...result];
+};
+
+function getMetricConfigDeep(metric, cluster, subCluster) {
+ const clusters = getContext("clusters");
+ if (cluster != null) {
+ let c = clusters.find((c) => c.name == cluster);
+ if (subCluster != null) {
+ let sc = c.subClusters.find((sc) => sc.name == subCluster);
+ return sc.metricConfig.find((mc) => mc.name == metric)
+ } else {
+ let result;
+ for (let sc of c.subClusters) {
+ const mc = sc.metricConfig.find((mc) => mc.name == metric)
+ if (result) { // If lowerIsBetter: Peak is still maximum value, no special case required
+ result.alert = (mc.alert > result.alert) ? mc.alert : result.alert
+ result.caution = (mc.caution > result.caution) ? mc.caution : result.caution
+ result.normal = (mc.normal > result.normal) ? mc.normal : result.normal
+ result.peak = (mc.peak > result.peak) ? mc.peak : result.peak
+ } else {
+ if (mc) result = {...mc};
+ }
+ }
+ return result
+ }
+ } else {
+ let result;
+ for (let c of clusters) {
+ for (let sc of c.subClusters) {
+ const mc = sc.metricConfig.find((mc) => mc.name == metric)
+ if (result) { // If lowerIsBetter: Peak is still maximum value, no special case required
+ result.alert = (mc.alert > result.alert) ? mc.alert : result.alert
+ result.caution = (mc.caution > result.caution) ? mc.caution : result.caution
+ result.normal = (mc.normal > result.normal) ? mc.normal : result.normal
+ result.peak = (mc.peak > result.peak) ? mc.peak : result.peak
+ } else {
+ if (mc) result = {...mc};
+ }
+ }
+ }
+ return result
}
- return false;
}
export function convert2uplot(canvasData) {
@@ -413,14 +443,14 @@ export function binsFromFootprint(weights, scope, values, numBins) {
}
export function transformDataForRoofline(flopsAny, memBw) { // Uses Metric Objects: {series:[{},{},...], timestep:60, name:$NAME}
- const nodes = flopsAny.series.length
- const timesteps = flopsAny.series[0].data.length
-
/* c will contain values from 0 to 1 representing the time */
let data = null
const x = [], y = [], c = []
if (flopsAny && memBw) {
+ const nodes = flopsAny.series.length
+ const timesteps = flopsAny.series[0].data.length
+
for (let i = 0; i < nodes; i++) {
const flopsData = flopsAny.series[i].data
const memBwData = memBw.series[i].data
@@ -446,7 +476,7 @@ export function transformDataForRoofline(flopsAny, memBw) { // Uses Metric Objec
// Return something to be plotted. The argument shall be the result of the
// `nodeMetrics` GraphQL query.
-// Remove "hardcoded" here or deemed necessary?
+// Hardcoded metric names required for correct render
export function transformPerNodeDataForRoofline(nodes) {
let data = null
const x = [], y = []
diff --git a/web/templates/monitoring/job.tmpl b/web/templates/monitoring/job.tmpl
index 1e3b09c..9b344f9 100644
--- a/web/templates/monitoring/job.tmpl
+++ b/web/templates/monitoring/job.tmpl
@@ -9,8 +9,6 @@