Skip to content

Commit aab93cf

Browse files
CONSOLE-4951: Add Health tab to Node View, remove events tab
1 parent c9649dd commit aab93cf

File tree

9 files changed

+264
-8
lines changed

9 files changed

+264
-8
lines changed

frontend/packages/console-app/locales/en/console-app.json

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@
381381
"Triggered by": "Triggered by",
382382
"Config object": "Config object",
383383
"Last action": "Last action",
384-
"No matching remediation actions": "No matching remediation actions",
384+
"No matching remediation actions": "No matching remediation agents",
385385
"Host addresses": "Host addresses",
386386
"Management": "Management",
387387
"NICs": "NICs",
@@ -444,6 +444,20 @@
444444
"MachineConfig selector": "MachineConfig selector",
445445
"Current configuration": "Current configuration",
446446
"Current configuration source": "Current configuration source",
447+
"Performance": "Performance",
448+
"Logs": "Logs",
449+
"CPU": "CPU",
450+
"CPU Utilisation": "CPU Utilisation",
451+
"CPU Saturation (Load per CPU)": "CPU Saturation (Load per CPU)",
452+
"Memory": "Memory",
453+
"Memory Utilisation": "Memory Utilisation",
454+
"Memory Saturation (Major Page Faults)": "Memory Saturation (Major Page Faults)",
455+
"Network": "Network",
456+
"Network Utilisation (Bytes Receive/Transmit)": "Network Utilisation (Bytes Receive/Transmit)",
457+
"Network Saturation (Drops Receive/Transmit)": "Network Saturation (Drops Receive/Transmit)",
458+
"Disk IO": "Disk IO",
459+
"Disk IO Utilisation": "Disk IO Utilisation",
460+
"Disk IO Saturation": "Disk IO Saturation",
447461
"This action cannot be undone. Deleting a node will instruct Kubernetes that the node is down or unrecoverable and delete all pods scheduled to that node. If the node is still running but unresponsive and the node is deleted, stateful workloads and persistent volumes may suffer corruption or data loss. Only delete a node that you have confirmed is completely stopped and cannot be restored.": "This action cannot be undone. Deleting a node will instruct Kubernetes that the node is down or unrecoverable and delete all pods scheduled to that node. If the node is still running but unresponsive and the node is deleted, stateful workloads and persistent volumes may suffer corruption or data loss. Only delete a node that you have confirmed is completely stopped and cannot be restored.",
448462
"Mark as schedulable": "Mark as schedulable",
449463
"Mark as unschedulable": "Mark as unschedulable",
@@ -473,7 +487,6 @@
473487
"Activity": "Activity",
474488
"Disk": "Disk",
475489
"Network interface": "Network interface",
476-
"CPU": "CPU",
477490
"Node name": "Node name",
478491
"Not available": "Not available",
479492
"Node addresses": "Node addresses",
@@ -496,7 +509,6 @@
496509
"Only one {{ machineHealthCheckLabel }} resource should match this node.": "Only one {{ machineHealthCheckLabel }} resource should match this node.",
497510
"Not configured": "Not configured",
498511
"No conditions": "No conditions",
499-
"Memory": "Memory",
500512
"Network in": "Network in",
501513
"Network out": "Network out",
502514
"Utilization": "Utilization",
@@ -526,6 +538,7 @@
526538
"Kubelet version": "Kubelet version",
527539
"Kube-Proxy version": "Kube-Proxy version",
528540
"Configuration": "Configuration",
541+
"Health": "Health",
529542
"Workload": "Workload",
530543
"Machine set": "Machine set",
531544
"This count is based on your access permissions and might not include all virtual machines.": "This count is based on your access permissions and might not include all virtual machines.",

frontend/packages/console-app/src/components/nodes/NodeDetailsPage.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useFlag } from '@console/shared/src/hooks/useFlag';
1717
import { isWindowsNode } from '@console/shared/src/selectors/node';
1818
import { nodeStatus } from '../../status/node';
1919
import { NodeConfiguration } from './configuration/NodeConfiguration';
20+
import { NodeHealth } from './health/NodeHealth';
2021
import NodeDashboard from './node-dashboard/NodeDashboard';
2122
import NodeDetails from './NodeDetails';
2223
import NodeLogs from './NodeLogs';
@@ -56,6 +57,12 @@ export const NodeDetailsPage: FC<ComponentProps<typeof DetailsPage>> = (props) =
5657
nameKey: 'console-app~Configuration',
5758
component: NodeConfiguration,
5859
},
60+
{
61+
href: 'health',
62+
// t('console-app~Health')
63+
nameKey: 'console-app~Health',
64+
component: NodeHealth,
65+
},
5966
{
6067
href: 'workload',
6168
// t('console-app~Workload')
@@ -64,9 +71,12 @@ export const NodeDetailsPage: FC<ComponentProps<typeof DetailsPage>> = (props) =
6471
},
6572
navFactory.editYaml(),
6673
]
67-
: [navFactory.editYaml(), navFactory.pods(NodePodsPage)]),
68-
navFactory.logs(NodeLogs),
69-
navFactory.events(ResourceEventStream),
74+
: [
75+
navFactory.editYaml(),
76+
navFactory.pods(NodePodsPage),
77+
navFactory.logs(NodeLogs),
78+
navFactory.events(ResourceEventStream),
79+
]),
7080
...(!isWindowsNode(node) ? [navFactory.terminal(NodeTerminal)] : []),
7181
],
7282
[nodeMgmtV1Enabled],

frontend/packages/console-app/src/components/nodes/NodeLogs.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ import {
1919
import { LogViewer, LogViewerSearch } from '@patternfly/react-log-viewer';
2020
import { css } from '@patternfly/react-styles';
2121
import { Trans, useTranslation } from 'react-i18next';
22+
import { FLAG_NODE_MGMT_V1 } from '@console/app/src/consts';
23+
import { useFlag } from '@console/dynamic-plugin-sdk/src/utils/flags';
2224
import { coFetch } from '@console/internal/co-fetch';
2325
import { useTheme } from '@console/internal/components/ThemeProvider';
26+
import { SectionHeading } from '@console/internal/components/utils';
2427
import { LoadingBox, LoadingInline } from '@console/internal/components/utils/status-box';
2528
import type { NodeKind } from '@console/internal/module/k8s';
2629
import { modelFor, resourceURL } from '@console/internal/module/k8s';
@@ -176,6 +179,7 @@ const HeaderBanner: FC<{ lineCount: number }> = ({ lineCount }) => {
176179

177180
const NodeLogs: FC<NodeLogsProps> = ({ obj: node }) => {
178181
const { getQueryArgument, setQueryArgument, removeQueryArgument } = useQueryParamsMutator();
182+
const nodeMgmtV1Enabled = useFlag(FLAG_NODE_MGMT_V1);
179183

180184
const {
181185
kind,
@@ -369,6 +373,7 @@ const NodeLogs: FC<NodeLogsProps> = ({ obj: node }) => {
369373

370374
return (
371375
<PaneBody fullHeight>
376+
{nodeMgmtV1Enabled ? <SectionHeading text={t('console-app~Logs')} /> : null}
372377
<div className="log-window-wrapper">
373378
{(isLoadingLog || errorExists) && logControls}
374379
{(lineCount >= MAX_LINE_COUNT || trimmedContent?.length > 0) && !isLoadingLog && (

frontend/packages/console-app/src/components/nodes/NodeSubNavPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useQueryParams } from '@console/shared/src/hooks/useQueryParams';
1111
import { useQueryParamsMutator } from '@console/shared/src/hooks/useQueryParamsMutator';
1212

1313
export const CONFIG_PAGE_ID = 'configuration';
14+
export const HEALTH_PAGE_ID = 'health';
1415
export const WORKLOADS_PAGE_ID = 'workloads';
1516

1617
export type SubPageType = {

frontend/packages/console-app/src/components/nodes/configuration/high-availability/RemediationAgent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const RemediationAgent: FC<RemediationAgentProps> = ({
113113
{rows.length === 0 ? (
114114
<tr className="pf-v6-c-table__tr">
115115
<td className="pf-v6-c-table__td" colSpan={4}>
116-
{t('console-app~No matching remediation actions')}
116+
{t('console-app~No matching remediation agents')}
117117
</td>
118118
</tr>
119119
) : (
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { FC } from 'react';
2+
import type { NodeKind } from '@console/dynamic-plugin-sdk/src';
3+
import NodeLogs from '../NodeLogs';
4+
import { HEALTH_PAGE_ID, NodeSubNavPage } from '../NodeSubNavPage';
5+
import NodePerformance from './NodePerformance';
6+
7+
type NodeHealthProps = {
8+
obj: NodeKind;
9+
};
10+
11+
const standardPages = [
12+
{
13+
tabId: 'performance',
14+
// t('console-app~Performance')
15+
nameKey: 'console-app~Performance',
16+
component: NodePerformance,
17+
priority: 70,
18+
},
19+
{
20+
tabId: 'logs',
21+
// t('console-app~Logs')
22+
nameKey: 'console-app~Logs',
23+
component: NodeLogs,
24+
priority: 30,
25+
},
26+
];
27+
28+
export const NodeHealth: FC<NodeHealthProps> = ({ obj }) => (
29+
<NodeSubNavPage obj={obj} pageId={HEALTH_PAGE_ID} standardPages={standardPages} />
30+
);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import type { FC } from 'react';
2+
import { useState, useMemo } from 'react';
3+
import {
4+
Card,
5+
CardBody,
6+
CardHeader,
7+
CardTitle,
8+
Flex,
9+
FlexItem,
10+
Grid,
11+
GridItem,
12+
ExpandableSectionToggle,
13+
} from '@patternfly/react-core';
14+
import { useTranslation } from 'react-i18next';
15+
import type { NodeKind } from '@console/dynamic-plugin-sdk';
16+
import { SectionHeading } from '@console/internal/components/utils';
17+
import PaneBody from '@console/shared/src/components/layout/PaneBody';
18+
import { QueryBrowser } from '@console/shared/src/components/query-browser';
19+
20+
type NodePerformanceProps = {
21+
obj: NodeKind;
22+
};
23+
24+
type ChartConfig = {
25+
title: string;
26+
queries: string[];
27+
units?: string;
28+
isStack?: boolean;
29+
};
30+
31+
type RowConfig = {
32+
title: string;
33+
charts: ChartConfig[];
34+
};
35+
36+
const PerformanceChart: FC<{ config: ChartConfig }> = ({ config }) => (
37+
<Card isFullHeight>
38+
<CardHeader>
39+
<CardTitle>{config.title}</CardTitle>
40+
</CardHeader>
41+
<CardBody>
42+
<QueryBrowser
43+
queries={config.queries}
44+
disableZoom
45+
hideControls
46+
units={config.units}
47+
isStack={config.isStack}
48+
/>
49+
</CardBody>
50+
</Card>
51+
);
52+
53+
type PerformanceRowProps = {
54+
row: RowConfig;
55+
defaultExpanded?: boolean;
56+
};
57+
58+
const PerformanceRow: FC<PerformanceRowProps> = ({ row, defaultExpanded = true }) => {
59+
const [isExpanded, setIsExpanded] = useState<boolean>(defaultExpanded);
60+
61+
return (
62+
<Flex direction={{ default: 'column' }} data-test-id={`panel-${row.title.toLowerCase()}`}>
63+
<FlexItem>
64+
<ExpandableSectionToggle
65+
isExpanded={isExpanded}
66+
onToggle={() => setIsExpanded((prev) => !prev)}
67+
>
68+
<span className="pf-v6-u-font-size-lg pf-v6-u-font-weight-bold">{row.title}</span>
69+
</ExpandableSectionToggle>
70+
</FlexItem>
71+
{isExpanded && (
72+
<FlexItem>
73+
<Grid hasGutter>
74+
{row.charts.map((chart) => (
75+
<GridItem key={chart.title} xl={6} lg={12}>
76+
<PerformanceChart config={chart} />
77+
</GridItem>
78+
))}
79+
</Grid>
80+
</FlexItem>
81+
)}
82+
</Flex>
83+
);
84+
};
85+
86+
const NodePerformance: FC<NodePerformanceProps> = ({ obj }) => {
87+
const { t } = useTranslation();
88+
const nodeName = obj?.metadata?.name;
89+
90+
const rows: RowConfig[] = useMemo(
91+
() => [
92+
{
93+
title: t('console-app~CPU'),
94+
charts: [
95+
{
96+
title: t('console-app~CPU Utilisation'),
97+
queries: [`instance:node_cpu:rate:sum{instance='${nodeName}'}`],
98+
units: 'cores',
99+
isStack: true,
100+
},
101+
{
102+
title: t('console-app~CPU Saturation (Load per CPU)'),
103+
queries: [
104+
`node_load1{instance='${nodeName}'} / instance:node_num_cpu:sum{instance='${nodeName}'}`,
105+
],
106+
isStack: true,
107+
},
108+
],
109+
},
110+
{
111+
title: t('console-app~Memory'),
112+
charts: [
113+
{
114+
title: t('console-app~Memory Utilisation'),
115+
queries: [
116+
`node_memory_MemTotal_bytes{instance='${nodeName}'} - node_memory_MemAvailable_bytes{instance='${nodeName}'}`,
117+
],
118+
units: 'bytes',
119+
isStack: true,
120+
},
121+
{
122+
title: t('console-app~Memory Saturation (Major Page Faults)'),
123+
queries: [`rate(node_vmstat_pgmajfault{instance='${nodeName}'}[5m])`],
124+
isStack: true,
125+
},
126+
],
127+
},
128+
{
129+
title: t('console-app~Network'),
130+
charts: [
131+
{
132+
title: t('console-app~Network Utilisation (Bytes Receive/Transmit)'),
133+
queries: [
134+
`instance:node_network_receive_bytes:rate:sum{instance='${nodeName}'}`,
135+
`instance:node_network_transmit_bytes:rate:sum{instance='${nodeName}'}`,
136+
],
137+
units: 'Bps',
138+
isStack: true,
139+
},
140+
{
141+
title: t('console-app~Network Saturation (Drops Receive/Transmit)'),
142+
queries: [
143+
`sum(rate(node_network_receive_drop_total{instance='${nodeName}'}[5m]))`,
144+
`sum(rate(node_network_transmit_drop_total{instance='${nodeName}'}[5m]))`,
145+
],
146+
isStack: true,
147+
},
148+
],
149+
},
150+
{
151+
title: t('console-app~Disk IO'),
152+
charts: [
153+
{
154+
title: t('console-app~Disk IO Utilisation'),
155+
queries: [
156+
`sum(rate(node_disk_read_bytes_total{instance='${nodeName}'}[5m]))`,
157+
`sum(rate(node_disk_written_bytes_total{instance='${nodeName}'}[5m]))`,
158+
],
159+
units: 'Bps',
160+
isStack: true,
161+
},
162+
{
163+
title: t('console-app~Disk IO Saturation'),
164+
queries: [
165+
`sum(rate(node_disk_read_time_seconds_total{instance='${nodeName}'}[5m]))`,
166+
`sum(rate(node_disk_write_time_seconds_total{instance='${nodeName}'}[5m]))`,
167+
],
168+
isStack: true,
169+
},
170+
],
171+
},
172+
],
173+
[nodeName, t],
174+
);
175+
176+
return (
177+
<PaneBody>
178+
<SectionHeading text={t('console-app~Performance')} />
179+
<Flex
180+
className="pf-v6-u-px-md"
181+
direction={{ default: 'column' }}
182+
spaceItems={{ default: 'spaceItemsLg' }}
183+
>
184+
{rows.map((row) => (
185+
<FlexItem key={row.title}>
186+
<PerformanceRow row={row} />
187+
</FlexItem>
188+
))}
189+
</Flex>
190+
</PaneBody>
191+
);
192+
};
193+
194+
export default NodePerformance;

frontend/packages/console-dynamic-plugin-sdk/docs/console-extensions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,7 @@ This extension can be used to add a tab on the sub-tabs for a Nodes details tab.
10721072
| Name | Value Type | Optional | Description |
10731073
| ---- | ---------- | -------- | ----------- |
10741074
| `parentTab` | `'configuration' \| 'health' \| 'workloads'` | no | Which detail tab to add the sub-tab to. |
1075-
| `page` | `{ tabId: string; name: string; priority: number; }` | no | The page to be show in node sub tabs. It takes tab name as name and priority of the tab.<br/>Note: Tabs are shown in priority order from highest to lowest. Current node tab priorities are:<br/>configuration:<br/> Storage: 70<br/> Operating system: 50<br/> Machine: 40<br/> High availability: 30<br/> workloads:<br/> Pods: 30 |
1075+
| `page` | `{ tabId: string; name: string; priority: number; }` | no | The page to be show in node sub tabs. It takes tab name as name and priority of the tab.<br/>Note: Tabs are shown in priority order from highest to lowest. Current node tab priorities are:<br/>configuration:<br/> Storage: 70<br/> Operating system: 50<br/> Machine: 40<br/> High availability: 30<br/> health:<br/> Performance: 70<br/> Logs: 30<br/> workloads:<br/> Pods: 30 |
10761076
| `component` | `CodeRef<ComponentType<SubPageComponentProps<K8sResourceCommon>>>` | no | The component to be rendered when the route matches. |
10771077

10781078
---

frontend/packages/console-dynamic-plugin-sdk/src/extensions/node-subnav-tabs.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export type NodeSubNavTab = Extension<
1919
* Operating system: 50
2020
* Machine: 40
2121
* High availability: 30
22+
* health:
23+
* Performance: 70
24+
* Logs: 30
2225
* workloads:
2326
* Pods: 30
2427
*/

0 commit comments

Comments
 (0)