/** * NodesPage — lists all nodes with Intel GPU capabilities. * * Shows GPU type, device count, resource allocation, and pod assignments * for each GPU-capable node in the cluster. */ import { Loader, NameValueTable, SectionBox, SectionHeader, SimpleTable, StatusLabel, } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import React from 'react'; import { useIntelGpuContext } from '../api/IntelGpuDataContext'; import { formatAge, formatGpuResourceName, formatGpuType, getGpuResources, getNodeGpuCount, getNodeGpuType, INTEL_GPU_RESOURCE, INTEL_GPU_XE_RESOURCE, IntelGpuNode, isNodeReady, } from '../api/k8s'; // --------------------------------------------------------------------------- // GPU allocation bar component // --------------------------------------------------------------------------- function GpuAllocationBar({ used, allocatable }: { used: number; allocatable: number }) { if (allocatable === 0) return ; const pct = Math.min(100, Math.round((used / allocatable) * 100)); const color = pct >= 90 ? '#d32f2f' : pct >= 70 ? '#f57c00' : '#0071c5'; return (
{`${used}/${allocatable} (${pct}%)`}
); } // --------------------------------------------------------------------------- // Node detail card // --------------------------------------------------------------------------- function NodeDetailCard({ node, podsByNode, }: { node: IntelGpuNode; podsByNode: Map; }) { const gpuType = getNodeGpuType(node); const gpuCount = getNodeGpuCount(node); const ready = isNodeReady(node); const capacityResources = getGpuResources(node.status?.capacity); const allocatableResources = getGpuResources(node.status?.allocatable); const podsOnNode = podsByNode.get(node.metadata.name) ?? []; return ( {ready ? 'Ready' : 'Not Ready'} ), }, { name: 'GPU Type', value: formatGpuType(gpuType), }, ...(gpuCount > 0 ? [{ name: 'GPU Devices (i915/xe)', value: String(gpuCount) }] : []), ...Object.entries(capacityResources).map(([key, cap]) => { const total = parseInt(cap, 10); return { name: `${formatGpuResourceName(key)} (capacity)`, value: String(total), }; }), ...Object.entries(allocatableResources).map(([key, value]) => { return { name: `${formatGpuResourceName(key)} (allocatable)`, value: value ?? '0', }; }), { name: 'GPU Workload Pods', value: podsOnNode.length > 0 ? podsOnNode.join(', ') : '—', }, { name: 'OS Image', value: node.status?.nodeInfo?.osImage ?? '—', }, { name: 'Kernel', value: node.status?.nodeInfo?.kernelVersion ?? '—', }, { name: 'Kubelet', value: node.status?.nodeInfo?.kubeletVersion ?? '—', }, { name: 'Age', value: formatAge(node.metadata.creationTimestamp), }, ]} /> ); } // --------------------------------------------------------------------------- // Main component // --------------------------------------------------------------------------- export default function NodesPage() { const { gpuNodes, gpuPods, loading, error, refresh } = useIntelGpuContext(); if (loading) { return ; } // Build map: nodeName → list of GPU pod names const podsByNode = new Map(); for (const pod of gpuPods) { if (!pod.spec?.nodeName) continue; const existing = podsByNode.get(pod.spec.nodeName) ?? []; existing.push(pod.metadata.name); podsByNode.set(pod.spec.nodeName, existing); } // Build table data for summary const tableData = gpuNodes.map(node => { const gpuType = getNodeGpuType(node); const gpuCount = getNodeGpuCount(node); const ready = isNodeReady(node); const capacity = node.status?.capacity ?? {}; const allocatable = node.status?.allocatable ?? {}; let totalCapacity = 0; let totalAllocatable = 0; for (const key of Object.keys(capacity)) { if (key === INTEL_GPU_RESOURCE || key === INTEL_GPU_XE_RESOURCE) { totalCapacity += parseInt(capacity[key] ?? '0', 10); totalAllocatable += parseInt(allocatable[key] ?? '0', 10); } } const podsOnNode = podsByNode.get(node.metadata.name) ?? []; return { node, gpuType, gpuCount, ready, totalCapacity, totalAllocatable, podsOnNode, }; }); return ( <>
{error && ( {error} }]} /> )} {gpuNodes.length === 0 && ( No nodes with Intel GPU resources or labels were found ), }, { name: 'Note', value: 'Nodes appear here when they have gpu.intel.com/* resources or Intel GPU node labels. ' + 'Ensure the Intel GPU device plugin and Node Feature Discovery are installed.', }, ]} /> )} {/* Summary table */} {gpuNodes.length > 0 && ( d.node.metadata.name }, { label: 'Ready', getter: d => ( {d.ready ? 'Ready' : 'Not Ready'} ), }, { label: 'GPU Type', getter: d => formatGpuType(d.gpuType) }, { label: 'GPU Devices', getter: d => String(d.gpuCount || '—') }, { label: 'Allocation', getter: d => ( ), }, { label: 'GPU Pods', getter: d => String(d.podsOnNode.length) }, { label: 'Age', getter: d => formatAge(d.node.metadata.creationTimestamp) }, ]} data={tableData} /> )} {/* Per-node detail cards */} {gpuNodes.map(node => ( ))} ); }