Skip to content

Commit

Permalink
feat: Basic node view
Browse files Browse the repository at this point in the history
  • Loading branch information
tiithansen committed Jun 8, 2024
1 parent 9db38c0 commit 9c806e9
Show file tree
Hide file tree
Showing 35 changed files with 281 additions and 60 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions src/pages/Clusters/Clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { prefixRoute } from 'utils/utils.routing';
import { getOverviewScene } from './tabs/Overview/Overview';
import { getNodesScene } from './tabs/Nodes/Nodes';
import { usePluginProps } from 'utils/utils.plugin';
import { NodePage } from './pages/Node/Node';

const timeRange = new SceneTimeRange({
from: 'now-1h',
Expand Down Expand Up @@ -60,6 +61,12 @@ function getScene({ datasource }: { datasource: string }) {
}),
],
getScene: getOverviewScene,
drilldowns: [
{
routePath: prefixRoute(`${ROUTES.Clusters}/nodes/:name`),
getPage: NodePage
},
]
}),
]
})
Expand Down
212 changes: 212 additions & 0 deletions src/pages/Clusters/pages/Node/Node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { EmbeddedScene, PanelBuilders, SceneAppPage, SceneAppPageLike, SceneControlsSpacer, SceneFlexItem, SceneFlexLayout, SceneQueryRunner, SceneRefreshPicker, SceneRouteMatch, SceneTimePicker, VariableValueSelectors } from "@grafana/scenes";
import { ROUTES } from "../../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../../../../common/variableHelpers";
import { LabelFilters } from "common/queryHelpers";
import { getPodsScene } from "pages/Workloads/tabs/Pods/Pods";
import { Metrics } from "metrics/metrics";
import { LegendDisplayMode } from "@grafana/schema";

function getScene(node: string) {
return new EmbeddedScene({
controls: [
new VariableValueSelectors({}),
new SceneControlsSpacer(),
new SceneTimePicker({ isOnCanvas: true }),
new SceneRefreshPicker({
intervals: ['5s', '1m', '1h'],
isOnCanvas: true,
}),
],
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexLayout({
direction: 'row',
height: 300,
children: [
new SceneFlexItem({
body: getCPUPanel(node)
}),
new SceneFlexItem({
body: getMemoryPanel(node)
})
]
}),
new SceneFlexLayout({
direction: 'row',
children: [
new SceneFlexItem({
body: getPods(node)
})
]
}),
]
}),
})
}

function getCPUPanel(node: string) {
return PanelBuilders.timeseries()
.setTitle('CPU')
.setData(new SceneQueryRunner({
datasource: {
uid: '$datasource',
type: 'prometheus',
},
queries: [
{
refId: 'cpu_total',
expr: `
sum(
${Metrics.machineCpuCores.name}{cluster=~"$cluster",instance=~"${node}.*"}
) by (cluster)`,
legendFormat: 'Total'
},
{
refId: 'cpu_usage',
// Calculate CPU usage in cores per node and sum it up to get the total usage across all nodes
expr: `
(
sum (
rate(
${Metrics.nodeCpuSecondsTotal.name}{
${Metrics.nodeCpuSecondsTotal.labels.mode}!="idle",
instance=~"${node}.*",
cluster="$cluster"
}[$__rate_interval]
)
) by(${Metrics.nodeCpuSecondsTotal.labels.instance}, cluster)
/
on (${Metrics.nodeCpuSecondsTotal.labels.instance}, cluster) group_left sum (
(
rate(
${Metrics.nodeCpuSecondsTotal.name}{
cluster="$cluster",
instance=~"${node}.*",
}[$__rate_interval]
)
)
) by (${Metrics.nodeCpuSecondsTotal.labels.instance}, cluster)
)
* count(
count(
node_cpu_seconds_total{
cluster="$cluster",
instance=~"${node}.*",
}
) by (cpu, cluster, instance)
) by (cluster, instance)
`,
legendFormat: 'Usage'
},
{
refId: 'cpu_requested',
expr: `
sum(
kube_node_info{cluster=~"$cluster",internal_ip=~"${node}.*"} * on(node) group_right()
${Metrics.kubePodContainerResourceRequests.name}{
resource="cpu",
cluster=~"$cluster",
}
) by (cluster)`,
legendFormat: 'Requested'
}
],
}))
.setUnit('cores')
.setOption('legend', { displayMode: LegendDisplayMode.Table })
.setOverrides((builder) => {
builder.matchFieldsByQuery('cpu_total')
.overrideCustomFieldConfig('lineStyle', { fill: 'dash', dash: [5, 5] })
.overrideCustomFieldConfig('fillOpacity', 5)
builder.matchFieldsByQuery('cpu_requested')
.overrideCustomFieldConfig('lineStyle', { fill: 'dash', dash: [20, 5] })
.overrideCustomFieldConfig('fillOpacity', 5)
})
.build()
}

function getMemoryPanel(node: string) {
return PanelBuilders.timeseries()
.setTitle('Memory')
.setData(new SceneQueryRunner({
datasource: {
uid: '$datasource',
type: 'prometheus',
},
queries: [
{
refId: 'memory_total',
expr: `
sum(
${Metrics.nodeMemoryMemTotalBytes.name}{cluster=~"$cluster", instance=~"${node}.*"}
) by (cluster)`,
legendFormat: 'Total [{{cluster}}]'
},
{
refId: 'memory_usage',
expr: `
sum(
${Metrics.nodeMemoryMemTotalBytes.name}{cluster=~"$cluster", instance=~"${node}.*"}
-
${Metrics.nodeMemoryMemAvailableBytes.name}{cluster=~"$cluster", instance=~"${node}.*"}
) by (cluster)`,
legendFormat: 'Used [{{cluster}}]'
},
{
refId: 'memory_requested',
expr: `
sum(
kube_node_info{cluster=~"$cluster",internal_ip=~"${node}.*"} * on(node) group_right()
${Metrics.kubePodContainerResourceRequests.name}{
resource="memory",
cluster=~"$cluster",
}
) by (cluster)`,
legendFormat: 'Requested [{{cluster}}]'
}
],
}))
.setUnit('bytes')
.setOption('legend', { displayMode: LegendDisplayMode.Table })
.setOverrides((builder) => {
builder.matchFieldsByQuery('memory_total')
.overrideCustomFieldConfig('lineStyle', { fill: 'dash', dash: [5, 5] })
.overrideCustomFieldConfig('fillOpacity', 5)
builder.matchFieldsByQuery('memory_requested')
.overrideCustomFieldConfig('lineStyle', { fill: 'dash', dash: [20, 5] })
.overrideCustomFieldConfig('fillOpacity', 5)
})
.build()
}

function getPods(node: string) {
const staticLabelFilters: LabelFilters = [
{
label: 'host_ip',
op: '=~',
value: `${node}.*`
},
]

return getPodsScene(staticLabelFilters, false, false)
}

export function NodePage(routeMatch: SceneRouteMatch<any>, parent: SceneAppPageLike) {

const props = usePluginProps();

return new SceneAppPage({
title: `Node - ${routeMatch.params.name}`,
titleIcon: 'dashboard',
$variables: createTopLevelVariables({
datasource: props?.meta.jsonData?.datasource || 'prometheus'
}),
$timeRange: createTimeRange(),
url: prefixRoute(`${ROUTES.Clusters}/nodes/${routeMatch.params.name}`),
getScene: () => getScene(routeMatch.params.name),
getParentPage: () => parent,
})
}
2 changes: 1 addition & 1 deletion src/pages/Clusters/tabs/Nodes/NodeExpandedRowScene.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SceneFlexLayout } from "@grafana/scenes";
import { LabelFilters } from "pages/Workloads/queryHelpers";
import { LabelFilters } from "common/queryHelpers";
import { getPodsScene } from "pages/Workloads/tabs/Pods/Pods";

export function buildExpandedRowScene(node: string) {
Expand Down
6 changes: 3 additions & 3 deletions src/pages/Clusters/tabs/Nodes/Nodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import React, { useEffect, useMemo } from 'react';
import { DataFrameView } from '@grafana/data';
import { InteractiveTable } from '../../../../components/InteractiveTable/InterativeTable';
import { createRowQueries } from './Queries';
import { asyncQueryRunner } from 'pages/Workloads/queryHelpers';
import { asyncQueryRunner } from 'common/queryHelpers';
import { buildExpandedRowScene } from './NodeExpandedRowScene';
import { getSeriesValue } from 'pages/Workloads/seriesHelpers';
import { createClusterVariable, resolveVariable } from 'pages/Workloads/variableHelpers';
import { getSeriesValue } from 'common/seriesHelpers';
import { createClusterVariable, resolveVariable } from 'common/variableHelpers';
import { LinkCell } from 'pages/Workloads/components/LinkCell';
import { CellContext } from '@tanstack/react-table';
import { FormattedCell, TextColor } from 'pages/Workloads/components/FormattedCell';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Clusters/tabs/Nodes/Queries.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SceneVariables } from "@grafana/scenes";
import { resolveVariable } from "pages/Workloads/variableHelpers";
import { resolveVariable } from "common/variableHelpers";
import { Metrics } from "metrics/metrics";

export function createRowQueries(nodes: string, nodeNames: string, sceneVariables: SceneVariables) {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Clusters/tabs/Overview/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EmbeddedScene, PanelBuilders, SceneFlexItem, SceneFlexLayout, SceneQueryRunner, SceneVariableSet, VariableValueSelectors} from "@grafana/scenes"
import { LegendDisplayMode } from "@grafana/schema"
import { Metrics } from "metrics/metrics"
import { createClusterVariable } from "pages/Workloads/variableHelpers"
import { createClusterVariable } from "common/variableHelpers"

function getNodesPerClusterPanel() {
return PanelBuilders.timeseries()
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/Workloads.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getOverviewScene } from './tabs/Overview/Overview';
import { usePluginProps } from 'utils/utils.plugin';
import { DeploymentPage } from './pages/DeploymentPage';
import { StatefulSetPage } from './pages/StatefulSetPage';
import { createTimeRange, createTopLevelVariables } from './variableHelpers';
import { createTimeRange, createTopLevelVariables } from '../../common/variableHelpers';
import { DaemonSetPage } from './pages/DaemonSetPage';
import { CronJobPage } from './pages/CronJobPage';
import { JobPage } from './pages/JobPage';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { FormattedCell } from '../FormattedCell';
import { InteractiveTable } from '../../../../components/InteractiveTable/InterativeTable';
import { RestartsCellBuilder } from '../../components/RestartsCell';
import { createRowQueries } from './Queries';
import { getSeriesValue } from 'pages/Workloads/seriesHelpers';
import { LabelFilters, asyncQueryRunner } from 'pages/Workloads/queryHelpers';
import { createNamespaceVariable, resolveVariable } from 'pages/Workloads/variableHelpers';
import { getSeriesValue } from 'common/seriesHelpers';
import { LabelFilters, asyncQueryRunner } from 'common/queryHelpers';
import { createNamespaceVariable, resolveVariable } from 'common/variableHelpers';
import { CellContext } from '@tanstack/react-table';
import { Metrics } from 'metrics/metrics';

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Workloads/components/ContainersTable/Queries.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SceneVariables } from "@grafana/scenes";
import { LabelFilters, serializeLabelFilters } from "pages/Workloads/queryHelpers";
import { resolveVariable } from "pages/Workloads/variableHelpers";
import { LabelFilters, serializeLabelFilters } from "common/queryHelpers";
import { resolveVariable } from "common/variableHelpers";
import { Metrics } from "metrics/metrics";

export function createRowQueries(containers: string, staticLabelFilters: LabelFilters, sceneVariables: SceneVariables) {
Expand Down
4 changes: 1 addition & 3 deletions src/pages/Workloads/components/ResourceLabels.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { SceneComponentProps, SceneObjectBase, SceneObjectState, SceneQueryRunner, sceneGraph } from "@grafana/scenes";
import { LabelFilters, serializeLabelFilters } from '../queryHelpers';
import { LabelFilters, serializeLabelFilters } from '../../../common/queryHelpers';
import { DataFrameView, GrafanaTheme2, LoadingState } from '@grafana/data';
import { css } from '@emotion/css';
import { useStyles2 } from '@grafana/ui';
Expand Down Expand Up @@ -96,8 +96,6 @@ class ResourceLabels extends SceneObjectBase<ResourceLabelsState> {

const result: Label[] = [];

console.log(data)

if (data && data.state === LoadingState.Done) {
const df = new DataFrameView(data.series[0] || [])
const frames = df.toArray()
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/pages/CronJobPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EmbeddedScene, SceneAppPage, SceneAppPageLike, SceneControlsSpacer, Sce
import { ROUTES } from "../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../variableHelpers";
import { createTopLevelVariables, createTimeRange } from "../../../common/variableHelpers";

function getScene(cronJob: string) {
return new EmbeddedScene({
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/pages/DaemonSetPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EmbeddedScene, SceneAppPage, SceneAppPageLike, SceneControlsSpacer, Sce
import { ROUTES } from "../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../variableHelpers";
import { createTopLevelVariables, createTimeRange } from "../../../common/variableHelpers";

function getScene(daemonSet: string) {
return new EmbeddedScene({
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Workloads/pages/DeploymentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { EmbeddedScene, PanelBuilders, SceneAppPage, SceneAppPageLike, SceneCont
import { ROUTES } from "../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../variableHelpers";
import { createTopLevelVariables, createTimeRange } from "../../../common/variableHelpers";
import { createResourceLabels } from "../components/ResourceLabels";
import { getPodsScene } from "../tabs/Pods/Pods";
import { LabelFilters } from "../queryHelpers";
import { LabelFilters } from "../../../common/queryHelpers";
import { Metrics } from "metrics/metrics";

function getPods(deployment: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/pages/JobPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EmbeddedScene, SceneAppPage, SceneAppPageLike, SceneControlsSpacer, Sce
import { ROUTES } from "../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../variableHelpers";
import { createTopLevelVariables, createTimeRange } from "../../../common/variableHelpers";

function getScene(job: string) {
return new EmbeddedScene({
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/pages/PodPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GraphTransform } from "@grafana/schema";
import { createResourceLabels } from "../components/ResourceLabels";
import { getContainersScene } from "../components/ContainersTable/ContainersTable";
import { usePluginProps } from "utils/utils.plugin";
import { createTopLevelVariables, createTimeRange } from "../variableHelpers";
import { createTopLevelVariables, createTimeRange } from "../../../common/variableHelpers";
import { Metrics } from "metrics/metrics";

export function getPodMemoryPanel(pod: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/pages/StatefulSetPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EmbeddedScene, SceneAppPage, SceneAppPageLike, SceneControlsSpacer, SceneFlexLayout, SceneRefreshPicker, SceneRouteMatch, SceneTimePicker, VariableValueSelectors } from "@grafana/scenes";
import { ROUTES } from "../../../constants";
import { prefixRoute } from "utils/utils.routing";
import { createTimeRange, createTopLevelVariables } from "../variableHelpers";
import { createTimeRange, createTopLevelVariables } from "../../../common/variableHelpers";
import { usePluginProps } from "utils/utils.plugin";

function getScene(statefulSet: string) {
Expand Down
6 changes: 3 additions & 3 deletions src/pages/Workloads/tabs/CronJobs/CronJobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import React, { useEffect, useMemo } from 'react';
import { DataFrameView } from '@grafana/data';
import { InteractiveTable } from '../../../../components/InteractiveTable/InterativeTable';
import { LinkCell } from 'pages/Workloads/components/LinkCell';
import { asyncQueryRunner } from 'pages/Workloads/queryHelpers';
import { getSeriesValue } from 'pages/Workloads/seriesHelpers';
import { createNamespaceVariable, resolveVariable } from 'pages/Workloads/variableHelpers';
import { asyncQueryRunner } from 'common/queryHelpers';
import { getSeriesValue } from 'common/seriesHelpers';
import { createNamespaceVariable, resolveVariable } from 'common/variableHelpers';
import { createRowQueries } from './Queries';
import { DurationCell } from 'pages/Workloads/components/DurationCell';
import { CellContext } from '@tanstack/react-table';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Workloads/tabs/CronJobs/Queries.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SceneVariables } from "@grafana/scenes";
import { resolveVariable } from "pages/Workloads/variableHelpers";
import { resolveVariable } from "common/variableHelpers";
import { Metrics } from "metrics/metrics";

export function createRowQueries(cronJob: string, sceneVariables: SceneVariables) {
Expand Down
Loading

0 comments on commit 9c806e9

Please sign in to comment.