Move common logic into systems view again

- adds backend log if subcluster for node not configured
This commit is contained in:
Christoph Kluge
2024-10-14 11:55:59 +02:00
parent 2cbe8e9517
commit 2f6e5a7648
9 changed files with 350 additions and 445 deletions

View File

@@ -9,36 +9,259 @@
-->
<script>
import { getContext } from "svelte";
import { queryStore, gql, getContextClient } from "@urql/svelte"
import {
Row,
Col,
Card,
Input,
InputGroup,
InputGroupText,
Icon,
Button,
Spinner,
} from "@sveltestrap/sveltestrap";
import { init, checkMetricsDisabled } from "./generic/utils.js";
import NodeOverview from "./systems/NodeOverview.svelte";
import NodeList from "./systems/NodeList.svelte";
import MetricSelection from "./generic/select/MetricSelection.svelte";
import TimeSelection from "./generic/select/TimeSelection.svelte";
import Refresher from "./generic/helper/Refresher.svelte";
export let displayType;
export let cluster;
export let from = null;
export let to = null;
const { query: initq } = init();
console.assert(
displayType == "OVERVIEW" || displayType == "LIST",
"Invalid nodes displayType provided!",
);
if (from == null || to == null) {
to = new Date(Date.now());
from = new Date(to.getTime());
from.setHours(from.getHours() - 12);
}
const initialized = getContext("initialized");
const ccconfig = getContext("cc-config");
const globalMetrics = getContext("globalMetrics");
const displayNodeOverview = (displayType === 'OVERVIEW')
let nodeList;
let hostnameFilter = "";
let selectedMetric = ccconfig.system_view_selectedMetric || "";
let selectedMetrics = ccconfig.node_list_selectedMetrics || ['cpu_load', 'mem_bw', 'acc_utilization', 'net_bytes_in', 'net_bytes_out'];
let isMetricsSelectionOpen = false;
const client = getContextClient();
const nodeQuery = gql`
query ($cluster: String!, $metrics: [String!], $from: Time!, $to: Time!) {
nodeMetrics(
cluster: $cluster
metrics: $metrics
from: $from
to: $to
) {
host
subCluster
metrics {
name
scope
metric {
timestep
unit {
base
prefix
}
series {
statistics {
min
avg
max
}
data
}
}
}
}
}
`
$: nodesQuery = queryStore({
client: client,
query: nodeQuery,
variables: {
cluster: cluster,
metrics: selectedMetrics,
from: from.toISOString(),
to: to.toISOString(),
},
});
let systemMetrics = [];
let systemUnits = {};
function loadMetrics(isInitialized) {
if (!isInitialized) return
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 : "")
}
}
$: loadMetrics($initialized)
$: if (displayNodeOverview) {
selectedMetrics = [selectedMetric]
}
let rawData = []
$: if ($initq.data && $nodesQuery?.data) {
rawData = $nodesQuery?.data?.nodeMetrics.filter((h) => {
if (h.subCluster === '') { // Exclude nodes with empty subCluster field
console.warn('subCluster not configured for node', h.host)
return false
} else {
return h.metrics.some(
(m) => selectedMetrics.includes(m.name) && m.scope == "node",
)
}
})
}
let mappedData = []
$: if (rawData?.length > 0) {
mappedData = rawData.map((h) => ({
host: h.host,
subCluster: h.subCluster,
data: h.metrics.find(
(m) => selectedMetrics.includes(m.name) && m.scope == "node",
),
disabled: checkMetricsDisabled(
selectedMetrics,
cluster,
h.subCluster,
),
}))
.sort((a, b) => a.host.localeCompare(b.host))
}
let filteredData = []
$: if (mappedData?.length > 0) {
filteredData = mappedData.filter((h) =>
h.host.includes(hostnameFilter)
)
}
</script>
{#if displayType === 'OVERVIEW'}
<NodeOverview {cluster} {from} {to}/>
{:else if displayType === 'LIST'}
<NodeList {cluster} {from} {to}/>
{:else}
<Row>
<Col>
<Card color="danger">
Unknown displayList type!
</Card>
</Col>
<!-- ROW1: Tools-->
<Row cols={{ xs: 2, lg: 4 }}>
{#if $initq.data}
<!-- List Metric Select Col-->
{#if !displayNodeOverview}
<Col>
<InputGroup>
<InputGroupText><Icon name="graph-up" /></InputGroupText>
<InputGroupText>Metrics</InputGroupText>
<Button
outline
color="secondary"
on:click={() => (isMetricsSelectionOpen = true)}
>
Select for {cluster} ...
</Button>
</InputGroup>
</Col>
{/if}
<!-- Node Col-->
<Col>
<InputGroup>
<InputGroupText><Icon name="hdd" /></InputGroupText>
<InputGroupText>Find Node(s)</InputGroupText>
<Input
placeholder="Filter hostname ..."
type="text"
title="Search with at least three characters ..."
bind:value={hostnameFilter}
/>
</InputGroup>
</Col>
<!-- Range Col-->
<Col>
<TimeSelection bind:from bind:to />
</Col>
<!-- Overview Metric Col-->
{#if displayNodeOverview}
<Col class="mt-2 mt-lg-0">
<InputGroup>
<InputGroupText><Icon name="graph-up" /></InputGroupText>
<InputGroupText>Metric</InputGroupText>
<Input type="select" bind:value={selectedMetric}>
{#each systemMetrics as metric}
<option value={metric.name}
>{metric.name} {systemUnits[metric.name] ? "("+systemUnits[metric.name]+")" : ""}</option
>
{/each}
</Input>
</InputGroup>
</Col>
{/if}
<!-- Refresh Col-->
<Col class="mt-2 mt-lg-0">
<Refresher
on:refresh={() => {
const diff = Date.now() - to;
from = new Date(from.getTime() + diff);
to = new Date(to.getTime() + diff);
}}
/>
</Col>
{/if}
</Row>
<!-- ROW2: Content-->
{#if displayType !== "OVERVIEW" && displayType !== "LIST"}
<Row>
<Col>
<Card body color="danger">Unknown displayList type! </Card>
</Col>
</Row>
{:else if $nodesQuery.error}
<Row>
<Col>
<Card body color="danger">{$nodesQuery.error.message}</Card>
</Col>
</Row>
{:else if $nodesQuery.fetching }
<Row>
<Col>
<Spinner />
</Col>
</Row>
{:else if $initialized && $nodesQuery?.data}
{#if displayNodeOverview}
<!-- ROW2-1: Node Overview (Grid Included)-->
<NodeOverview {ccconfig} {cluster} bind:selectedMetric data={filteredData}/>
{:else}
<!-- ROW2-2: Node List-->
<Row>
<Col>
<!-- <NodeList bind:nodesData={$nodesQuery.data} {cluster} {selectedMetrics} {systemUnits} bind:hostnameFilter/> -->
</Col>
</Row>
<MetricSelection
{cluster}
configName="node_list_selectedMetrics"
bind:metrics={selectedMetrics}
bind:isOpen={isMetricsSelectionOpen}
/>
{/if}
{/if}