From c04aea89c93035e9b50c8058769dcb05031eeebc Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Fri, 30 Jun 2023 12:01:27 +0200 Subject: [PATCH] Implement node filter in frontend, fix backend - Add running job count and link to list to single node view --- internal/routerConfig/routes.go | 3 + web/frontend/src/Node.root.svelte | 72 +++++++++++++++++++---- web/frontend/src/filters/Filters.svelte | 12 ++++ web/frontend/src/filters/Resources.svelte | 14 +++-- 4 files changed, 85 insertions(+), 16 deletions(-) diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index 3c38a02..c6b777c 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -184,6 +184,9 @@ func buildFilterPresets(query url.Values) map[string]interface{} { } } } + if query.Get("node") != "" { + filterPresets["node"] = query.Get("node") + } if query.Get("numNodes") != "" { parts := strings.Split(query.Get("numNodes"), "-") if len(parts) == 2 { diff --git a/web/frontend/src/Node.root.svelte b/web/frontend/src/Node.root.svelte index ef4a00c..40c3ce3 100644 --- a/web/frontend/src/Node.root.svelte +++ b/web/frontend/src/Node.root.svelte @@ -23,7 +23,7 @@ const ccconfig = getContext('cc-config') const clusters = getContext('clusters') const client = getContextClient(); - const query = gql`query($cluster: String!, $nodes: [String!], $from: Time!, $to: Time!) { + const nodeMetricsQuery = gql`query($cluster: String!, $nodes: [String!], $from: Time!, $to: Time!) { nodeMetrics(cluster: $cluster, nodes: $nodes, from: $from, to: $to) { host subCluster @@ -42,9 +42,9 @@ } }`; - $: nodesQuery = queryStore({ + $: nodeMetricsData = queryStore({ client: client, - query: query, + query: nodeMetricsQuery, variables: { cluster: cluster, nodes: [hostname], @@ -53,8 +53,44 @@ } }); + let itemsPerPage = ccconfig.plot_list_jobsPerPage; + let page = 1; + let paging = { itemsPerPage, page }; + let sorting = { field: "startTime", order: "DESC" }; + $: filter = [ + {cluster: { eq: cluster }}, + {node: { eq: hostname }}, + {state: 'running'}, + {startTime: { + from: from.toISOString(), + to: to.toISOString() + }} + ]; + + const nodeJobsQuery = gql` + query ( + $filter: [JobFilter!]! + $sorting: OrderByInput! + $paging: PageRequest! + ) { + jobs(filter: $filter, order: $sorting, page: $paging) { + # items { + # id + # jobId + # } + count + } + } + `; + + $: nodeJobsData = queryStore({ + client: client, + query: nodeJobsQuery, + variables: { paging, sorting, filter } + }); + let metricUnits = {} - $: if ($nodesQuery.data) { + $: if ($nodeMetricsData.data) { let thisCluster = clusters.find(c => c.name == cluster) if (thisCluster) { for (let metric of thisCluster.metricConfig) { @@ -67,7 +103,7 @@ } } - // $: console.log($nodesQuery?.data?.nodeMetrics[0].metrics) + const dateToUnixEpoch = rfc3339 => Math.floor(Date.parse(rfc3339) / 1000) @@ -82,6 +118,18 @@ {hostname} ({cluster}) + + {#if $nodeJobsData.fetching } + + {:else} + {#if $nodeJobsData.data} + Currently running jobs on this node: { $nodeJobsData.data.jobs.count } + [ View in Job List ] + {:else} + No currently running jobs. + {/if} + {/if} + - {#if $nodesQuery.error} - {$nodesQuery.error.message} - {:else if $nodesQuery.fetching || $initq.fetching} + {#if $nodeMetricsData.error} + {$nodeMetricsData.error.message} + {:else if $nodeMetricsData.fetching || $initq.fetching} {:else} ({ ...m, disabled: checkMetricDisabled(m.name, cluster, $nodesQuery.data.nodeMetrics[0].subCluster)})) + items={$nodeMetricsData.data.nodeMetrics[0].metrics + .map(m => ({ ...m, disabled: checkMetricDisabled(m.name, cluster, $nodeMetricsData.data.nodeMetrics[0].subCluster)})) .sort((a, b) => a.name.localeCompare(b.name))}>

{item.name} {metricUnits[item.name]}

{#if item.disabled === false && item.metric} c.name == cluster)} subCluster={$nodesQuery.data.nodeMetrics[0].subCluster} + cluster={clusters.find(c => c.name == cluster)} subCluster={$nodeMetricsData.data.nodeMetrics[0].subCluster} series={item.metric.series} /> {:else if item.disabled === true && item.metric} - Metric disabled for subcluster {item.name}:{$nodesQuery.data.nodeMetrics[0].subCluster} + Metric disabled for subcluster {item.name}:{$nodeMetricsData.data.nodeMetrics[0].subCluster} {:else} No dataset returned for {item.name} {/if} diff --git a/web/frontend/src/filters/Filters.svelte b/web/frontend/src/filters/Filters.svelte index 1431293..f05a4cd 100644 --- a/web/frontend/src/filters/Filters.svelte +++ b/web/frontend/src/filters/Filters.svelte @@ -47,6 +47,7 @@ project: filterPresets.project || '', jobName: filterPresets.jobName || '', + node: filterPresets.node || null, numNodes: filterPresets.numNodes || { from: null, to: null }, numHWThreads: filterPresets.numHWThreads || { from: null, to: null }, numAccelerators: filterPresets.numAccelerators || { from: null, to: null }, @@ -74,6 +75,8 @@ let items = [] if (filters.cluster) items.push({ cluster: { eq: filters.cluster } }) + if (filters.node) + items.push({ node: { contains: filters.node } }) if (filters.partition) items.push({ partition: { eq: filters.partition } }) if (filters.states.length != allJobStates.length) @@ -114,6 +117,8 @@ let opts = [] if (filters.cluster) opts.push(`cluster=${filters.cluster}`) + if (filters.node) + opts.push(`node=${filters.node}`) if (filters.partition) opts.push(`partition=${filters.partition}`) if (filters.states.length != allJobStates.length) @@ -272,6 +277,12 @@ {/if} + {#if filters.node != null } + (isResourcesOpen = true)}> + Node: {filters.node} + + {/if} + {#if filters.stats.length > 0} (isStatsOpen = true)}> {filters.stats.map(stat => `${stat.text}: ${stat.from} - ${stat.to}`).join(', ')} @@ -318,6 +329,7 @@ bind:numNodes={filters.numNodes} bind:numHWThreads={filters.numHWThreads} bind:numAccelerators={filters.numAccelerators} + bind:namedNode={filters.node} bind:isNodesModified={isNodesModified} bind:isHwthreadsModified={isHwthreadsModified} bind:isAccsModified={isAccsModified} diff --git a/web/frontend/src/filters/Resources.svelte b/web/frontend/src/filters/Resources.svelte index ae1bc10..be5995a 100644 --- a/web/frontend/src/filters/Resources.svelte +++ b/web/frontend/src/filters/Resources.svelte @@ -16,8 +16,9 @@ export let isNodesModified = false export let isHwthreadsModified = false export let isAccsModified = false + export let namedNode = null - let pendingNumNodes = numNodes, pendingNumHWThreads = numHWThreads, pendingNumAccelerators = numAccelerators + let pendingNumNodes = numNodes, pendingNumHWThreads = numHWThreads, pendingNumAccelerators = numAccelerators, pendingNamedNode = namedNode const findMaxNumAccels = clusters => clusters.reduce((max, cluster) => Math.max(max, cluster.subClusters.reduce((max, sc) => Math.max(max, sc.topology.accelerators?.length || 0), 0)), 0) @@ -76,7 +77,9 @@ Select number of utilized Resources -
Number of Nodes
+
Named Node
+ +
Number of Nodes
{ pendingNumNodes = { from: detail[0], to: detail[1] } @@ -117,7 +120,8 @@ numNodes ={ from: pendingNumNodes.from, to: pendingNumNodes.to } numHWThreads = { from: pendingNumHWThreads.from, to: pendingNumHWThreads.to } numAccelerators = { from: pendingNumAccelerators.from, to: pendingNumAccelerators.to } - dispatch('update', { numNodes, numHWThreads, numAccelerators }) + namedNode = pendingNamedNode + dispatch('update', { numNodes, numHWThreads, numAccelerators, namedNode }) }}> Close & Apply @@ -126,13 +130,15 @@ pendingNumNodes = { from: null, to: null } pendingNumHWThreads = { from: null, to: null } pendingNumAccelerators = { from: null, to: null } + pendingNamedNode = null numNodes = { from: pendingNumNodes.from, to: pendingNumNodes.to } numHWThreads = { from: pendingNumHWThreads.from, to: pendingNumHWThreads.to } numAccelerators = { from: pendingNumAccelerators.from, to: pendingNumAccelerators.to } isNodesModified = false isHwthreadsModified = false isAccsModified = false - dispatch('update', { numNodes, numHWThreads, numAccelerators }) + namedNode = pendingNamedNode + dispatch('update', { numNodes, numHWThreads, numAccelerators, namedNode}) }}>Reset