mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-01-24 02:19:05 +01:00
fix legends, add resolution, add statsseries, add simple healthcheck
This commit is contained in:
parent
2a3383e9e6
commit
5ea11a5ad2
@ -264,7 +264,7 @@ func LoadNodeListData(
|
|||||||
from, to time.Time,
|
from, to time.Time,
|
||||||
page *model.PageRequest,
|
page *model.PageRequest,
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error) {
|
) (map[string]schema.JobData, int, bool, error) {
|
||||||
repo, err := metricdata.GetMetricDataRepo(cluster)
|
repo, err := metricdata.GetMetricDataRepo(cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, fmt.Errorf("METRICDATA/METRICDATA > no metric data repository configured for '%s'", cluster)
|
return nil, 0, false, fmt.Errorf("METRICDATA/METRICDATA > no metric data repository configured for '%s'", cluster)
|
||||||
@ -286,6 +286,19 @@ func LoadNodeListData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: New StatsSeries will always be calculated as 'min/median/max'
|
||||||
|
const maxSeriesSize int = 15
|
||||||
|
for _, jd := range data {
|
||||||
|
for _, scopes := range jd {
|
||||||
|
for _, jm := range scopes {
|
||||||
|
if jm.StatisticsSeries != nil || len(jm.Series) <= maxSeriesSize {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jm.AddStatisticsSeries()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return nil, totalNodes, hasNextPage, fmt.Errorf("METRICDATA/METRICDATA > the metric data repository for '%s' does not support this query", cluster)
|
return nil, totalNodes, hasNextPage, fmt.Errorf("METRICDATA/METRICDATA > the metric data repository for '%s' does not support this query", cluster)
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,7 @@ func (ccms *CCMetricStore) LoadNodeListData(
|
|||||||
from, to time.Time,
|
from, to time.Time,
|
||||||
page *model.PageRequest,
|
page *model.PageRequest,
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error) {
|
) (map[string]schema.JobData, int, bool, error) {
|
||||||
|
|
||||||
// 0) Init additional vars
|
// 0) Init additional vars
|
||||||
var totalNodes int = 0
|
var totalNodes int = 0
|
||||||
@ -747,6 +747,8 @@ func (ccms *CCMetricStore) LoadNodeListData(
|
|||||||
nodes = nodes[start:end]
|
nodes = nodes[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: Order of node data is not guaranteed after this point, but contents match page and filter criteria
|
||||||
|
|
||||||
queries, assignedScope, err := ccms.buildNodeQueries(cluster, subCluster, nodes, metrics, scopes, resolution)
|
queries, assignedScope, err := ccms.buildNodeQueries(cluster, subCluster, nodes, metrics, scopes, resolution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error while building queries")
|
log.Warn("Error while building queries")
|
||||||
@ -769,7 +771,7 @@ func (ccms *CCMetricStore) LoadNodeListData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var errors []string
|
var errors []string
|
||||||
data := make(map[string]map[string]map[schema.MetricScope]*schema.JobMetric)
|
data := make(map[string]schema.JobData)
|
||||||
for i, row := range resBody.Results {
|
for i, row := range resBody.Results {
|
||||||
var query ApiQuery
|
var query ApiQuery
|
||||||
if resBody.Queries != nil {
|
if resBody.Queries != nil {
|
||||||
@ -790,7 +792,7 @@ func (ccms *CCMetricStore) LoadNodeListData(
|
|||||||
// Init Nested Map Data Structures If Not Found
|
// Init Nested Map Data Structures If Not Found
|
||||||
hostData, ok := data[query.Hostname]
|
hostData, ok := data[query.Hostname]
|
||||||
if !ok {
|
if !ok {
|
||||||
hostData = make(map[string]map[schema.MetricScope]*schema.JobMetric)
|
hostData = make(schema.JobData)
|
||||||
data[query.Hostname] = hostData
|
data[query.Hostname] = hostData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ func (idb *InfluxDBv2DataRepository) LoadNodeListData(
|
|||||||
from, to time.Time,
|
from, to time.Time,
|
||||||
page *model.PageRequest,
|
page *model.PageRequest,
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error) {
|
) (map[string]schema.JobData, int, bool, error) {
|
||||||
|
|
||||||
var totalNodes int = 0
|
var totalNodes int = 0
|
||||||
var hasNextPage bool = false
|
var hasNextPage bool = false
|
||||||
|
@ -31,7 +31,7 @@ type MetricDataRepository interface {
|
|||||||
LoadNodeData(cluster string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error)
|
LoadNodeData(cluster string, metrics, nodes []string, scopes []schema.MetricScope, from, to time.Time, ctx context.Context) (map[string]map[string][]*schema.JobMetric, error)
|
||||||
|
|
||||||
// Return a map of hosts to a map of metrics to a map of scopes for multiple nodes.
|
// Return a map of hosts to a map of metrics to a map of scopes for multiple nodes.
|
||||||
LoadNodeListData(cluster, subCluster, nodeFilter string, metrics []string, scopes []schema.MetricScope, resolution int, from, to time.Time, page *model.PageRequest, ctx context.Context) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error)
|
LoadNodeListData(cluster, subCluster, nodeFilter string, metrics []string, scopes []schema.MetricScope, resolution int, from, to time.Time, page *model.PageRequest, ctx context.Context) (map[string]schema.JobData, int, bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{}
|
var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{}
|
||||||
|
@ -456,7 +456,7 @@ func (pdb *PrometheusDataRepository) LoadNodeListData(
|
|||||||
from, to time.Time,
|
from, to time.Time,
|
||||||
page *model.PageRequest,
|
page *model.PageRequest,
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error) {
|
) (map[string]schema.JobData, int, bool, error) {
|
||||||
|
|
||||||
var totalNodes int = 0
|
var totalNodes int = 0
|
||||||
var hasNextPage bool = false
|
var hasNextPage bool = false
|
||||||
|
@ -59,7 +59,7 @@ func (tmdr *TestMetricDataRepository) LoadNodeListData(
|
|||||||
from, to time.Time,
|
from, to time.Time,
|
||||||
page *model.PageRequest,
|
page *model.PageRequest,
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) (map[string]map[string]map[schema.MetricScope]*schema.JobMetric, int, bool, error) {
|
) (map[string]schema.JobData, int, bool, error) {
|
||||||
|
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte";
|
import { getContext, createEventDispatcher } from "svelte";
|
||||||
import {
|
import {
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
@ -52,15 +52,22 @@
|
|||||||
const globalMetrics = getContext("globalMetrics");
|
const globalMetrics = getContext("globalMetrics");
|
||||||
const displayNodeOverview = (displayType === 'OVERVIEW')
|
const displayNodeOverview = (displayType === 'OVERVIEW')
|
||||||
|
|
||||||
|
const resampleConfig = getContext("resampling") || null;
|
||||||
|
const resampleResolutions = resampleConfig ? [...resampleConfig.resolutions] : [];
|
||||||
|
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
||||||
|
let selectedResolution = resampleConfig ? resampleDefault : 0;
|
||||||
|
|
||||||
let hostnameFilter = "";
|
let hostnameFilter = "";
|
||||||
|
let pendingHostnameFilter = "";
|
||||||
let selectedMetric = ccconfig.system_view_selectedMetric || "";
|
let selectedMetric = ccconfig.system_view_selectedMetric || "";
|
||||||
let selectedMetrics = ccconfig[`node_list_selectedMetrics:${cluster}`] || [ccconfig.system_view_selectedMetric];
|
let selectedMetrics = ccconfig[`node_list_selectedMetrics:${cluster}`] || [ccconfig.system_view_selectedMetric];
|
||||||
let isMetricsSelectionOpen = false;
|
let isMetricsSelectionOpen = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note 1: Scope Selector or Auto-Scoped?
|
Note 1: Scope Selector or Auto-Scoped? -> USeful auto scoping with stats view where applicable -> CHeck with JVe
|
||||||
Note 2: "Sorting" as use-case ignored for now, probably default to alphanumerical on hostnames of cluster
|
Note 2: "Sorting" as use-case ignored for now, probably default to alphanumerical on hostnames of cluster (handled in frontend at the moment)
|
||||||
Note 3: Add Idle State Filter (== No allocated Jobs) [Frontend?] : Cannot be handled by CCMS, requires secondary job query and refiltering of visible nodes
|
Note 3: Add Idle State Filter (== No allocated Jobs) [Frontend?] : Cannot be handled by CCMS, requires secondary job query and refiltering of visible nodes
|
||||||
|
Note 4: Resolution changes as implemented only possible for all plots generally, not for individual metrics: Result list if build from GQL result *including* metric series
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let systemMetrics = [];
|
let systemMetrics = [];
|
||||||
@ -80,10 +87,15 @@
|
|||||||
selectedMetrics = [selectedMetric]
|
selectedMetrics = [selectedMetric]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: { // Wait after input for some time to prevent too many requests
|
||||||
|
setTimeout(function () {
|
||||||
|
hostnameFilter = pendingHostnameFilter;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- ROW1: Tools-->
|
<!-- ROW1: Tools-->
|
||||||
<Row cols={{ xs: 2, lg: 4 }} class="mb-3">
|
<Row cols={{ xs: 2, lg: !displayNodeOverview ? 5 : 4 }} class="mb-3">
|
||||||
{#if $initq.data}
|
{#if $initq.data}
|
||||||
<!-- List Metric Select Col-->
|
<!-- List Metric Select Col-->
|
||||||
{#if !displayNodeOverview}
|
{#if !displayNodeOverview}
|
||||||
@ -100,16 +112,29 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupText><Icon name="plus-slash-minus" /></InputGroupText>
|
||||||
|
<InputGroupText>Resolution</InputGroupText>
|
||||||
|
<Input type="select" bind:value={selectedResolution}>
|
||||||
|
{#each resampleResolutions as res}
|
||||||
|
<option value={res}
|
||||||
|
>{res} sec</option
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</Input>
|
||||||
|
</InputGroup>
|
||||||
|
</Col>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Node Col-->
|
<!-- Node Col-->
|
||||||
<Col>
|
<Col class="mt-2 mt-lg-0">
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<InputGroupText><Icon name="hdd" /></InputGroupText>
|
<InputGroupText><Icon name="hdd" /></InputGroupText>
|
||||||
<InputGroupText>Find Node(s)</InputGroupText>
|
<InputGroupText>Find Node(s)</InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter hostname ..."
|
placeholder="Filter hostname ..."
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={hostnameFilter}
|
bind:value={pendingHostnameFilter}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</Col>
|
</Col>
|
||||||
@ -159,7 +184,7 @@
|
|||||||
<NodeOverview {cluster} {subCluster} {ccconfig} {selectedMetrics} {from} {to} {hostnameFilter}/>
|
<NodeOverview {cluster} {subCluster} {ccconfig} {selectedMetrics} {from} {to} {hostnameFilter}/>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- ROW2-2: Node List (Grid Included)-->
|
<!-- ROW2-2: Node List (Grid Included)-->
|
||||||
<NodeList {cluster} {subCluster} {ccconfig} {selectedMetrics} {hostnameFilter} {from} {to} {systemUnits}/>
|
<NodeList {cluster} {subCluster} {ccconfig} {selectedMetrics} {selectedResolution} {hostnameFilter} {from} {to} {systemUnits}/>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
if (useStatsSeries == null) useStatsSeries = statisticsSeries != null;
|
if (useStatsSeries == null) useStatsSeries = statisticsSeries != null;
|
||||||
if (useStatsSeries == false && series == null) useStatsSeries = true;
|
if (useStatsSeries == false && series == null) useStatsSeries = true;
|
||||||
|
|
||||||
const usesMeanStatsSeries = (useStatsSeries && statisticsSeries.mean.length != 0)
|
const usesMeanStatsSeries = (useStatsSeries?.mean && statisticsSeries.mean.length != 0)
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const subClusterTopology = getContext("getHardwareTopology")(cluster, subCluster);
|
const subClusterTopology = getContext("getHardwareTopology")(cluster, subCluster);
|
||||||
const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric);
|
const metricConfig = getContext("getMetricConfig")(cluster, subCluster, metric);
|
||||||
|
@ -11,6 +11,7 @@ new Systems({
|
|||||||
to: infos.to
|
to: infos.to
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig],
|
||||||
|
['resampling', resampleConfig]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
export let subCluster = "";
|
export let subCluster = "";
|
||||||
export const ccconfig = null;
|
export const ccconfig = null;
|
||||||
export let selectedMetrics = [];
|
export let selectedMetrics = [];
|
||||||
|
export let selectedResolution = 0;
|
||||||
export let hostnameFilter = "";
|
export let hostnameFilter = "";
|
||||||
export let systemUnits = null;
|
export let systemUnits = null;
|
||||||
export let from = null;
|
export let from = null;
|
||||||
@ -39,7 +40,7 @@
|
|||||||
const { query: initq } = init();
|
const { query: initq } = init();
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const nodeListQuery = gql`
|
const nodeListQuery = gql`
|
||||||
query ($cluster: String!, $subCluster: String!, $nodeFilter: String!, $metrics: [String!], $scopes: [MetricScope!]!, $from: Time!, $to: Time!, $paging: PageRequest!) {
|
query ($cluster: String!, $subCluster: String!, $nodeFilter: String!, $metrics: [String!], $scopes: [MetricScope!]!, $from: Time!, $to: Time!, $paging: PageRequest!, $selectedResolution: Int) {
|
||||||
nodeMetricsList(
|
nodeMetricsList(
|
||||||
cluster: $cluster
|
cluster: $cluster
|
||||||
subCluster: $subCluster
|
subCluster: $subCluster
|
||||||
@ -49,6 +50,7 @@
|
|||||||
from: $from
|
from: $from
|
||||||
to: $to
|
to: $to
|
||||||
page: $paging
|
page: $paging
|
||||||
|
resolution: $selectedResolution
|
||||||
) {
|
) {
|
||||||
items {
|
items {
|
||||||
host
|
host
|
||||||
@ -63,12 +65,19 @@
|
|||||||
prefix
|
prefix
|
||||||
}
|
}
|
||||||
series {
|
series {
|
||||||
|
id
|
||||||
|
hostname
|
||||||
|
data
|
||||||
statistics {
|
statistics {
|
||||||
min
|
min
|
||||||
avg
|
avg
|
||||||
max
|
max
|
||||||
}
|
}
|
||||||
data
|
}
|
||||||
|
statisticsSeries {
|
||||||
|
min
|
||||||
|
median
|
||||||
|
max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,15 +95,19 @@
|
|||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
subCluster: subCluster,
|
subCluster: subCluster,
|
||||||
nodeFilter: hostnameFilter,
|
nodeFilter: hostnameFilter,
|
||||||
scopes: ["core", "accelerator"],
|
scopes: ["core", "socket", "accelerator"],
|
||||||
metrics: selectedMetrics,
|
metrics: selectedMetrics,
|
||||||
from: from.toISOString(),
|
from: from.toISOString(),
|
||||||
to: to.toISOString(),
|
to: to.toISOString(),
|
||||||
paging: paging,
|
paging: paging,
|
||||||
|
selectedResolution: selectedResolution,
|
||||||
},
|
},
|
||||||
|
requestPolicy: "network-only", // Resolution queries are cached, but how to access them? For now: reload on every change
|
||||||
});
|
});
|
||||||
|
|
||||||
$: matchedNodes = $nodesQuery.data?.nodeMetricsList.totalNodes || 0;
|
$: matchedNodes = $nodesQuery.data?.nodeMetricsList.totalNodes || 0;
|
||||||
|
$: orderedData = $nodesQuery.data?.nodeMetricsList.items.sort((a, b) => a.host.localeCompare(b.host));
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $nodesQuery.error}
|
{#if $nodesQuery.error}
|
||||||
@ -135,7 +148,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each $nodesQuery.data.nodeMetricsList.items as nodeData (nodeData.host)}
|
{#each orderedData as nodeData (nodeData.host)}
|
||||||
<NodeListRow {nodeData} {cluster} {selectedMetrics}/>
|
<NodeListRow {nodeData} {cluster} {selectedMetrics}/>
|
||||||
{:else}
|
{:else}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
export let cluster;
|
export let cluster;
|
||||||
export let subCluster
|
export let subCluster
|
||||||
export let hostname;
|
export let hostname;
|
||||||
|
export let dataHealth;
|
||||||
|
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const paging = { itemsPerPage: 50, page: 1 };
|
const paging = { itemsPerPage: 50, page: 1 };
|
||||||
@ -49,6 +50,11 @@
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Not at least one returned, selected metric: NodeHealth warning
|
||||||
|
const healthWarn = !dataHealth.includes(true);
|
||||||
|
// At least one non-returned selected metric: Metric config error?
|
||||||
|
const metricWarn = dataHealth.includes(false);
|
||||||
|
|
||||||
$: nodeJobsData = queryStore({
|
$: nodeJobsData = queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: nodeJobsQuery,
|
query: nodeJobsQuery,
|
||||||
@ -78,7 +84,31 @@
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
{:else if $nodeJobsData.data}
|
{:else if $nodeJobsData.data}
|
||||||
<p>
|
<p>
|
||||||
{#if $nodeJobsData.data.jobs.count > 0}
|
{#if healthWarn}
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupText>
|
||||||
|
<Icon name="exclamation-circle"/>
|
||||||
|
</InputGroupText>
|
||||||
|
<InputGroupText>
|
||||||
|
Status
|
||||||
|
</InputGroupText>
|
||||||
|
<Button color="danger" disabled>
|
||||||
|
Unhealthy
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
{:else if metricWarn}
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupText>
|
||||||
|
<Icon name="circle-half"/>
|
||||||
|
</InputGroupText>
|
||||||
|
<InputGroupText>
|
||||||
|
Status
|
||||||
|
</InputGroupText>
|
||||||
|
<Button color="warning" disabled>
|
||||||
|
Missing Metric
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
{:else if $nodeJobsData.data.jobs.count > 0}
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<InputGroupText>
|
<InputGroupText>
|
||||||
<Icon name="circle-fill"/>
|
<Icon name="circle-fill"/>
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<NodeInfo {cluster} subCluster={nodeData.subCluster} hostname={nodeData.host} />
|
<NodeInfo {cluster} subCluster={nodeData.subCluster} hostname={nodeData.host} dataHealth={nodeData?.metrics.map((m) => (m.metric.series.length > 0))}/>
|
||||||
</td>
|
</td>
|
||||||
{#each sortAndSelectScope(nodeData?.metrics) as metricData (metricData.data.name)}
|
{#each sortAndSelectScope(nodeData?.metrics) as metricData (metricData.data.name)}
|
||||||
<td>
|
<td>
|
||||||
@ -63,11 +63,14 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
<!-- "No Data"-Warning included in MetricPlot-Component -->
|
||||||
<MetricPlot
|
<MetricPlot
|
||||||
timestep={metricData.data.metric.timestep}
|
|
||||||
series={metricData.data.metric.series}
|
|
||||||
metric={metricData.data.name}
|
|
||||||
{cluster}
|
{cluster}
|
||||||
subCluster={nodeData.subCluster}
|
subCluster={nodeData.subCluster}
|
||||||
|
metric={metricData.data.name}
|
||||||
|
scope={metricData.data.scope}
|
||||||
|
timestep={metricData.data.metric.timestep}
|
||||||
|
series={metricData.data.metric.series}
|
||||||
|
statisticsSeries={metricData.data?.metric.statisticsSeries}
|
||||||
|
useStatsSeries={!!metricData.data?.metric.statisticsSeries}
|
||||||
forNode
|
forNode
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
const displayType = {{ .Infos.displayType }};
|
const displayType = {{ .Infos.displayType }};
|
||||||
const infos = {{ .Infos }};
|
const infos = {{ .Infos }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
|
const resampleConfig = {{ .Resampling }};
|
||||||
</script>
|
</script>
|
||||||
<script src='/build/systems.js'></script>
|
<script src='/build/systems.js'></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
Loading…
Reference in New Issue
Block a user