mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-24 12:29:05 +01:00
Continue fixing errors
This commit is contained in:
parent
f235b5e911
commit
518f4664a1
@ -6,7 +6,7 @@ import terser from '@rollup/plugin-terser';
|
|||||||
import css from 'rollup-plugin-css-only';
|
import css from 'rollup-plugin-css-only';
|
||||||
|
|
||||||
// const production = !process.env.ROLLUP_WATCH;
|
// const production = !process.env.ROLLUP_WATCH;
|
||||||
const production = true
|
const production = false
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
svelte({
|
svelte({
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import Filters from "./filters/Filters.svelte";
|
import Filters from "./filters/Filters.svelte";
|
||||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||||
import { scramble, scrambleNames } from "./joblist/JobInfo.svelte";
|
import { scramble, scrambleNames } from "./joblist/JobInfo.svelte";
|
||||||
|
import { UniqueInputFieldNamesRule } from "graphql";
|
||||||
|
|
||||||
const {} = init();
|
const {} = init();
|
||||||
|
|
||||||
@ -20,7 +21,9 @@
|
|||||||
"Invalid list type provided!"
|
"Invalid list type provided!"
|
||||||
);
|
);
|
||||||
|
|
||||||
const stats = queryStore({
|
let filters;
|
||||||
|
|
||||||
|
$: stats = queryStore({
|
||||||
client: getContextClient(),
|
client: getContextClient(),
|
||||||
query: gql`
|
query: gql`
|
||||||
query($filter: [JobFilter!]!) {
|
query($filter: [JobFilter!]!) {
|
||||||
@ -32,10 +35,10 @@
|
|||||||
totalCoreHours
|
totalCoreHours
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
variables: { filter, type },
|
variables: { filters },
|
||||||
|
pause: true
|
||||||
});
|
});
|
||||||
|
|
||||||
let filters;
|
|
||||||
let nameFilter = "";
|
let nameFilter = "";
|
||||||
let sorting = { field: "totalJobs", direction: "down" };
|
let sorting = { field: "totalJobs", direction: "down" };
|
||||||
|
|
||||||
@ -88,9 +91,8 @@
|
|||||||
startTimeQuickSelect={true}
|
startTimeQuickSelect={true}
|
||||||
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
|
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
|
||||||
on:update={({ detail }) => {
|
on:update={({ detail }) => {
|
||||||
$stats.variables = { filter: detail.filters };
|
filters = detail.filters;
|
||||||
$stats.context.pause = false;
|
stats.resume();
|
||||||
$stats.reexecute();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -81,8 +81,6 @@
|
|||||||
$jobs.reexecute({ requestPolicy: 'network-only' })
|
$jobs.reexecute({ requestPolicy: 'network-only' })
|
||||||
}
|
}
|
||||||
|
|
||||||
query(jobs)
|
|
||||||
|
|
||||||
let tableWidth = null
|
let tableWidth = null
|
||||||
let jobInfoColumnWidth = 250
|
let jobInfoColumnWidth = 250
|
||||||
$: plotWidth = Math.floor((tableWidth - jobInfoColumnWidth) / metrics.length - 10)
|
$: plotWidth = Math.floor((tableWidth - jobInfoColumnWidth) / metrics.length - 10)
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { expiringCacheExchange } from './cache-exchange.js'
|
import { expiringCacheExchange } from "./cache-exchange.js";
|
||||||
import { Client, setContextClient, fetchExchange } from '@urql/svelte';
|
import {
|
||||||
import { setContext, getContext, hasContext, onDestroy, tick } from 'svelte'
|
Client,
|
||||||
import { readable } from 'svelte/store'
|
setContextClient,
|
||||||
|
fetchExchange,
|
||||||
|
} from "@urql/svelte";
|
||||||
|
import { setContext, getContext, hasContext, onDestroy, tick } from "svelte";
|
||||||
|
import { readable } from "svelte/store";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call this function only at component initialization time!
|
* Call this function only at component initialization time!
|
||||||
@ -13,28 +17,29 @@ import { readable } from 'svelte/store'
|
|||||||
* - Adds 'clusters' to the context (object with cluster names as keys)
|
* - 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 'metrics' to the context, a function that takes a cluster and metric name and returns the MetricConfig (or undefined)
|
||||||
*/
|
*/
|
||||||
export function init(extraInitQuery = '') {
|
export function init(extraInitQuery = "") {
|
||||||
const jwt = hasContext('jwt')
|
const jwt = hasContext("jwt")
|
||||||
? getContext('jwt')
|
? getContext("jwt")
|
||||||
: getContext('cc-config')['jwt']
|
: getContext("cc-config")["jwt"];
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
url: `${window.location.origin}/query`,
|
url: `${window.location.origin}/query`,
|
||||||
fetchOptions: jwt != null
|
fetchOptions:
|
||||||
? { headers: { 'Authorization': `Bearer ${jwt}` } } : {},
|
jwt != null ? { headers: { Authorization: `Bearer ${jwt}` } } : {},
|
||||||
exchanges: [
|
exchanges: [
|
||||||
dedupExchange,
|
expiringCacheExchange({
|
||||||
expiringCacheExchange({
|
ttl: 5 * 60 * 1000,
|
||||||
ttl: 5 * 60 * 1000,
|
maxSize: 150,
|
||||||
maxSize: 150,
|
}),
|
||||||
}),
|
fetchExchange,
|
||||||
fetchExchange
|
],
|
||||||
]
|
});
|
||||||
})
|
|
||||||
|
|
||||||
setContextClient(client)
|
setContextClient(client);
|
||||||
|
|
||||||
const query = client.query(`query {
|
const query = client
|
||||||
|
.query(
|
||||||
|
`query {
|
||||||
clusters {
|
clusters {
|
||||||
name,
|
name,
|
||||||
metricConfig {
|
metricConfig {
|
||||||
@ -62,227 +67,247 @@ export function init(extraInitQuery = '') {
|
|||||||
}
|
}
|
||||||
tags { id, name, type }
|
tags { id, name, type }
|
||||||
${extraInitQuery}
|
${extraInitQuery}
|
||||||
}`).toPromise()
|
}`
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
|
||||||
let state = { fetching: true, error: null, data: null }
|
let state = { fetching: true, error: null, data: null };
|
||||||
let subscribers = []
|
let subscribers = [];
|
||||||
const subscribe = (callback) => {
|
const subscribe = (callback) => {
|
||||||
callback(state)
|
callback(state);
|
||||||
subscribers.push(callback)
|
subscribers.push(callback);
|
||||||
return () => {
|
return () => {
|
||||||
subscribers = subscribers.filter(cb => cb != callback)
|
subscribers = subscribers.filter((cb) => cb != callback);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const tags = [], clusters = []
|
const tags = [],
|
||||||
setContext('tags', tags)
|
clusters = [];
|
||||||
setContext('clusters', clusters)
|
setContext("tags", tags);
|
||||||
setContext('metrics', (cluster, metric) => {
|
setContext("clusters", clusters);
|
||||||
if (typeof cluster !== 'object')
|
setContext("metrics", (cluster, metric) => {
|
||||||
cluster = clusters.find(c => c.name == cluster)
|
if (typeof cluster !== "object")
|
||||||
|
cluster = clusters.find((c) => c.name == cluster);
|
||||||
|
|
||||||
return cluster.metricConfig.find(m => m.name == metric)
|
return cluster.metricConfig.find((m) => m.name == metric);
|
||||||
})
|
});
|
||||||
setContext('on-init', callback => state.fetching
|
setContext("on-init", (callback) =>
|
||||||
? subscribers.push(callback)
|
state.fetching ? subscribers.push(callback) : callback(state)
|
||||||
: callback(state))
|
);
|
||||||
setContext('initialized', readable(false, (set) =>
|
setContext(
|
||||||
subscribers.push(() => set(true))))
|
"initialized",
|
||||||
|
readable(false, (set) => subscribers.push(() => set(true)))
|
||||||
|
);
|
||||||
|
|
||||||
query.then(({ error, data }) => {
|
query.then(({ error, data }) => {
|
||||||
state.fetching = false
|
state.fetching = false;
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
state.error = error
|
state.error = error;
|
||||||
tick().then(() => subscribers.forEach(cb => cb(state)))
|
tick().then(() => subscribers.forEach((cb) => cb(state)));
|
||||||
return
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
for (let tag of data.tags)
|
|
||||||
tags.push(tag)
|
|
||||||
|
|
||||||
for (let cluster of data.clusters)
|
|
||||||
clusters.push(cluster)
|
|
||||||
|
|
||||||
state.data = data
|
|
||||||
tick().then(() => subscribers.forEach(cb => cb(state)))
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: { subscribe },
|
|
||||||
tags,
|
|
||||||
clusters,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let tag of data.tags) tags.push(tag);
|
||||||
|
|
||||||
|
for (let cluster of data.clusters) clusters.push(cluster);
|
||||||
|
|
||||||
|
state.data = data;
|
||||||
|
tick().then(() => subscribers.forEach((cb) => cb(state)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
query: { subscribe },
|
||||||
|
tags,
|
||||||
|
clusters,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNumber(x) {
|
export function formatNumber(x) {
|
||||||
let suffix = ''
|
let suffix = "";
|
||||||
if (x >= 1000000000) {
|
if (x >= 1000000000) {
|
||||||
x /= 1000000
|
x /= 1000000;
|
||||||
suffix = 'G'
|
suffix = "G";
|
||||||
} else if (x >= 1000000) {
|
} else if (x >= 1000000) {
|
||||||
x /= 1000000
|
x /= 1000000;
|
||||||
suffix = 'M'
|
suffix = "M";
|
||||||
} else if (x >= 1000) {
|
} else if (x >= 1000) {
|
||||||
x /= 1000
|
x /= 1000;
|
||||||
suffix = 'k'
|
suffix = "k";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${(Math.round(x * 100) / 100)} ${suffix}`
|
return `${Math.round(x * 100) / 100} ${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use https://developer.mozilla.org/en-US/docs/Web/API/structuredClone instead?
|
// Use https://developer.mozilla.org/en-US/docs/Web/API/structuredClone instead?
|
||||||
export function deepCopy(x) {
|
export function deepCopy(x) {
|
||||||
return JSON.parse(JSON.stringify(x))
|
return JSON.parse(JSON.stringify(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
function fuzzyMatch(term, string) {
|
function fuzzyMatch(term, string) {
|
||||||
return string.toLowerCase().includes(term)
|
return string.toLowerCase().includes(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fuzzySearchTags(term, tags) {
|
export function fuzzySearchTags(term, tags) {
|
||||||
if (!tags)
|
if (!tags) return [];
|
||||||
return []
|
|
||||||
|
|
||||||
let results = []
|
let results = [];
|
||||||
let termparts = term.split(':').map(s => s.trim()).filter(s => s.length > 0)
|
let termparts = term
|
||||||
|
.split(":")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
|
||||||
if (termparts.length == 0) {
|
if (termparts.length == 0) {
|
||||||
results = tags.slice()
|
results = tags.slice();
|
||||||
} else if (termparts.length == 1) {
|
} else if (termparts.length == 1) {
|
||||||
for (let tag of tags)
|
for (let tag of tags)
|
||||||
if (fuzzyMatch(termparts[0], tag.type)
|
if (
|
||||||
|| fuzzyMatch(termparts[0], tag.name))
|
fuzzyMatch(termparts[0], tag.type) ||
|
||||||
results.push(tag)
|
fuzzyMatch(termparts[0], tag.name)
|
||||||
} else if (termparts.length == 2) {
|
)
|
||||||
for (let tag of tags)
|
results.push(tag);
|
||||||
if (fuzzyMatch(termparts[0], tag.type)
|
} else if (termparts.length == 2) {
|
||||||
&& fuzzyMatch(termparts[1], tag.name))
|
for (let tag of tags)
|
||||||
results.push(tag)
|
if (
|
||||||
}
|
fuzzyMatch(termparts[0], tag.type) &&
|
||||||
|
fuzzyMatch(termparts[1], tag.name)
|
||||||
|
)
|
||||||
|
results.push(tag);
|
||||||
|
}
|
||||||
|
|
||||||
return results.sort((a, b) => {
|
return results.sort((a, b) => {
|
||||||
if (a.type < b.type) return -1
|
if (a.type < b.type) return -1;
|
||||||
if (a.type > b.type) return 1
|
if (a.type > b.type) return 1;
|
||||||
if (a.name < b.name) return -1
|
if (a.name < b.name) return -1;
|
||||||
if (a.name > b.name) return 1
|
if (a.name > b.name) return 1;
|
||||||
return 0
|
return 0;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function groupByScope(jobMetrics) {
|
export function groupByScope(jobMetrics) {
|
||||||
let metrics = new Map()
|
let metrics = new Map();
|
||||||
for (let metric of jobMetrics) {
|
for (let metric of jobMetrics) {
|
||||||
if (metrics.has(metric.name))
|
if (metrics.has(metric.name)) metrics.get(metric.name).push(metric);
|
||||||
metrics.get(metric.name).push(metric)
|
else metrics.set(metric.name, [metric]);
|
||||||
else
|
}
|
||||||
metrics.set(metric.name, [metric])
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...metrics.values()].sort((a, b) => a[0].name.localeCompare(b[0].name))
|
return [...metrics.values()].sort((a, b) =>
|
||||||
|
a[0].name.localeCompare(b[0].name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scopeGranularity = {
|
const scopeGranularity = {
|
||||||
"node": 10,
|
node: 10,
|
||||||
"socket": 5,
|
socket: 5,
|
||||||
"accelerator": 5,
|
accelerator: 5,
|
||||||
"core": 2,
|
core: 2,
|
||||||
"hwthread": 1
|
hwthread: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function maxScope(scopes) {
|
export function maxScope(scopes) {
|
||||||
console.assert(scopes.length > 0 && scopes.every(x => scopeGranularity[x] != null))
|
console.assert(
|
||||||
let sm = scopes[0], gran = scopeGranularity[scopes[0]]
|
scopes.length > 0 && scopes.every((x) => scopeGranularity[x] != null)
|
||||||
for (let scope of scopes) {
|
);
|
||||||
let otherGran = scopeGranularity[scope]
|
let sm = scopes[0],
|
||||||
if (otherGran > gran) {
|
gran = scopeGranularity[scopes[0]];
|
||||||
sm = scope
|
for (let scope of scopes) {
|
||||||
gran = otherGran
|
let otherGran = scopeGranularity[scope];
|
||||||
}
|
if (otherGran > gran) {
|
||||||
|
sm = scope;
|
||||||
|
gran = otherGran;
|
||||||
}
|
}
|
||||||
return sm
|
}
|
||||||
|
return sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function minScope(scopes) {
|
export function minScope(scopes) {
|
||||||
console.assert(scopes.length > 0 && scopes.every(x => scopeGranularity[x] != null))
|
console.assert(
|
||||||
let sm = scopes[0], gran = scopeGranularity[scopes[0]]
|
scopes.length > 0 && scopes.every((x) => scopeGranularity[x] != null)
|
||||||
for (let scope of scopes) {
|
);
|
||||||
let otherGran = scopeGranularity[scope]
|
let sm = scopes[0],
|
||||||
if (otherGran < gran) {
|
gran = scopeGranularity[scopes[0]];
|
||||||
sm = scope
|
for (let scope of scopes) {
|
||||||
gran = otherGran
|
let otherGran = scopeGranularity[scope];
|
||||||
}
|
if (otherGran < gran) {
|
||||||
|
sm = scope;
|
||||||
|
gran = otherGran;
|
||||||
}
|
}
|
||||||
return sm
|
}
|
||||||
|
return sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchMetrics(job, metrics, scopes) {
|
export async function fetchMetrics(job, metrics, scopes) {
|
||||||
if (job.monitoringStatus == 0)
|
if (job.monitoringStatus == 0) return null;
|
||||||
return null
|
|
||||||
|
|
||||||
let query = []
|
let query = [];
|
||||||
if (metrics != null) {
|
if (metrics != null) {
|
||||||
for (let metric of metrics) {
|
for (let metric of metrics) {
|
||||||
query.push(`metric=${metric}`)
|
query.push(`metric=${metric}`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (scopes != null) {
|
}
|
||||||
for (let scope of scopes) {
|
if (scopes != null) {
|
||||||
query.push(`scope=${scope}`)
|
for (let scope of scopes) {
|
||||||
}
|
query.push(`scope=${scope}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let res = await fetch(
|
||||||
|
`/api/jobs/metrics/${job.id}${query.length > 0 ? "?" : ""}${query.join(
|
||||||
|
"&"
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
if (res.status != 200) {
|
||||||
|
return { error: { status: res.status, message: await res.text() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return await res.json();
|
||||||
let res = await fetch(`/api/jobs/metrics/${job.id}${(query.length > 0) ? '?' : ''}${query.join('&')}`)
|
} catch (e) {
|
||||||
if (res.status != 200) {
|
return { error: e };
|
||||||
return { error: { status: res.status, message: await res.text() } }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return await res.json()
|
|
||||||
} catch (e) {
|
|
||||||
return { error: e }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchMetricsStore() {
|
export function fetchMetricsStore() {
|
||||||
let set = null
|
let set = null;
|
||||||
let prev = { fetching: true, error: null, data: null }
|
let prev = { fetching: true, error: null, data: null };
|
||||||
return [
|
return [
|
||||||
readable(prev, (_set) => { set = _set }),
|
readable(prev, (_set) => {
|
||||||
(job, metrics, scopes) => fetchMetrics(job, metrics, scopes).then(res => {
|
set = _set;
|
||||||
let next = { fetching: false, error: res.error, data: res.data }
|
}),
|
||||||
if (prev.data && next.data)
|
(job, metrics, scopes) =>
|
||||||
next.data.jobMetrics.push(...prev.data.jobMetrics)
|
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
|
prev = next;
|
||||||
set(next)
|
set(next);
|
||||||
})
|
}),
|
||||||
]
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stickyHeader(datatableHeaderSelector, updatePading) {
|
export function stickyHeader(datatableHeaderSelector, updatePading) {
|
||||||
const header = document.querySelector('header > nav.navbar')
|
const header = document.querySelector("header > nav.navbar");
|
||||||
if (!header)
|
if (!header) return;
|
||||||
return
|
|
||||||
|
|
||||||
let ticking = false, datatableHeader = null
|
let ticking = false,
|
||||||
const onscroll = event => {
|
datatableHeader = null;
|
||||||
if (ticking)
|
const onscroll = (event) => {
|
||||||
return
|
if (ticking) return;
|
||||||
|
|
||||||
ticking = true
|
ticking = true;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
ticking = false
|
ticking = false;
|
||||||
if (!datatableHeader)
|
if (!datatableHeader)
|
||||||
datatableHeader = document.querySelector(datatableHeaderSelector)
|
datatableHeader = document.querySelector(datatableHeaderSelector);
|
||||||
|
|
||||||
const top = datatableHeader.getBoundingClientRect().top
|
const top = datatableHeader.getBoundingClientRect().top;
|
||||||
updatePading(top < header.clientHeight
|
updatePading(
|
||||||
? (header.clientHeight - top) + 10
|
top < header.clientHeight ? header.clientHeight - top + 10 : 10
|
||||||
: 10)
|
);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener('scroll', onscroll)
|
document.addEventListener("scroll", onscroll);
|
||||||
onDestroy(() => document.removeEventListener('scroll', onscroll))
|
onDestroy(() => document.removeEventListener("scroll", onscroll));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user