/** * OverviewPage — main dashboard for the Intel GPU plugin. * * Shows: plugin health, GPU node summary, resource allocation overview, * and pods requesting GPU resources. */ import { Loader, NameValueTable, PercentageBar, SectionBox, SectionHeader, SimpleTable, StatusLabel, } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import React from 'react'; import { useIntelGpuContext } from '../api/IntelGpuDataContext'; import { formatAge, getNodeGpuCount, getNodeGpuType, getPodGpuRequests, INTEL_GPU_RESOURCE, INTEL_GPU_RESOURCE_PREFIX, INTEL_GPU_XE_RESOURCE, isNodeReady, isPodReady, pluginStatusText, pluginStatusToStatus, } from '../api/k8s'; // --------------------------------------------------------------------------- // GPU type distribution chart // --------------------------------------------------------------------------- function gpuTypeChartData( discreteCount: number, integratedCount: number, unknownCount: number ): Array<{ name: string; value: number; fill: string }> { const data = []; if (discreteCount > 0) data.push({ name: 'Discrete', value: discreteCount, fill: '#0071c5' }); if (integratedCount > 0) data.push({ name: 'Integrated', value: integratedCount, fill: '#60a4dc' }); if (unknownCount > 0) data.push({ name: 'Unknown', value: unknownCount, fill: '#9e9e9e' }); return data; } // --------------------------------------------------------------------------- // Main component // --------------------------------------------------------------------------- export default function OverviewPage() { const { devicePlugins, pluginInstalled, gpuNodes, gpuPods, pluginPods, crdAvailable, loading, error, refresh, } = useIntelGpuContext(); if (loading) { return ; } // Node type breakdown let discreteCount = 0; let integratedCount = 0; let unknownCount = 0; let totalGpuCount = 0; let readyNodeCount = 0; for (const node of gpuNodes) { const type = getNodeGpuType(node); if (type === 'discrete') discreteCount++; else if (type === 'integrated') integratedCount++; else unknownCount++; totalGpuCount += getNodeGpuCount(node); if (isNodeReady(node)) readyNodeCount++; } // GPU allocation summary: sum capacity vs allocatable across all GPU nodes let totalCapacityGpus = 0; let totalAllocatableGpus = 0; let totalAllocatedGpus = 0; for (const node of gpuNodes) { const capacity = node.status?.capacity ?? {}; const allocatable = node.status?.allocatable ?? {}; for (const key of Object.keys(capacity)) { if (key === INTEL_GPU_RESOURCE || key === INTEL_GPU_XE_RESOURCE) { totalCapacityGpus += parseInt(capacity[key] ?? '0', 10); totalAllocatableGpus += parseInt(allocatable[key] ?? '0', 10); } } } // Count GPUs in use from pods for (const pod of gpuPods) { if (pod.status?.phase !== 'Running') continue; const requests = getPodGpuRequests(pod); for (const [key, value] of Object.entries(requests)) { if (key === INTEL_GPU_RESOURCE || key === INTEL_GPU_XE_RESOURCE) { totalAllocatedGpus += parseInt(value, 10) || 0; } } } const gpuUtilizationPct = totalCapacityGpus > 0 ? Math.round((totalAllocatedGpus / totalCapacityGpus) * 100) : 0; const chartData = gpuTypeChartData(discreteCount, integratedCount, unknownCount); const totalGpuNodes = gpuNodes.length; // Pod phase breakdown const podPhaseCounts = { Running: 0, Pending: 0, Succeeded: 0, Failed: 0, Other: 0 }; for (const pod of gpuPods) { const phase = pod.status?.phase ?? 'Other'; if (phase in podPhaseCounts) { podPhaseCounts[phase as keyof typeof podPhaseCounts]++; } else { podPhaseCounts.Other++; } } return ( <>
{/* Error state */} {error && ( {error} }]} /> )} {/* Plugin not detected */} {!pluginInstalled && !loading && ( Intel GPU device plugin not found on this cluster ), }, { name: 'Install (Helm)', value: 'helm repo add intel https://intel.github.io/helm-charts && ' + 'helm install intel-device-plugins-operator intel/intel-device-plugins-operator', }, { name: 'Documentation', value: 'https://intel.github.io/intel-device-plugins-for-kubernetes/', }, ]} /> )} {/* CRD not available notice */} {!crdAvailable && pluginInstalled && ( GpuDevicePlugin CRD not found — limited visibility available ), }, { name: 'Note', value: 'Plugin pods detected via DaemonSet labels. Install the Intel Device Plugins Operator for full CRD-based management.', }, ]} /> )} {/* Device Plugin status — only shown when CRDs exist */} {crdAvailable && devicePlugins.length > 0 && ( p.metadata.name }, { label: 'Status', getter: p => ( {pluginStatusText(p)} ), }, { label: 'Monitoring', getter: p => p.spec.enableMonitoring ? ( Enabled ) : ( Disabled ), }, { label: 'Shared/Node', getter: p => String(p.spec.sharedDevNum ?? 1) }, { label: 'Policy', getter: p => p.spec.preferredAllocationPolicy ?? '—' }, { label: 'Age', getter: p => formatAge(p.metadata.creationTimestamp) }, ]} data={devicePlugins} /> )} {/* Plugin daemon pods (shown when no CRD, or always as supplemental) */} {pluginPods.length > 0 && ( p.metadata.name }, { label: 'Namespace', getter: p => p.metadata.namespace ?? '—' }, { label: 'Node', getter: p => p.spec?.nodeName ?? '—' }, { label: 'Status', getter: p => ( {isPodReady(p) ? 'Ready' : p.status?.phase ?? 'Unknown'} ), }, { label: 'Age', getter: p => formatAge(p.metadata.creationTimestamp) }, ]} data={pluginPods} /> )} {/* GPU Node summary */} {totalGpuNodes > 0 && chartData.length > 0 && (
GPU Type Distribution
)} 0 ? 'success' : 'warning'}> {totalGpuNodes} ), }, { name: 'Ready Nodes', value: String(readyNodeCount) }, ...(discreteCount > 0 ? [{ name: 'Discrete GPU Nodes', value: String(discreteCount) }] : []), ...(integratedCount > 0 ? [{ name: 'Integrated GPU Nodes', value: String(integratedCount) }] : []), ...(totalGpuCount > 0 ? [{ name: 'Total GPU Devices', value: String(totalGpuCount) }] : []), ]} />
{/* GPU allocation summary */} {totalCapacityGpus > 0 && (
GPU Utilization ({gpuUtilizationPct}%)
0 ? 'success' : 'warning'} > {totalAllocatableGpus - totalAllocatedGpus} ), }, ]} />
)} {/* GPU workloads summary */} 0 ? [ { name: 'Running', value: {podPhaseCounts.Running}, }, ] : []), ...(podPhaseCounts.Pending > 0 ? [ { name: 'Pending', value: {podPhaseCounts.Pending}, }, ] : []), ...(podPhaseCounts.Failed > 0 ? [ { name: 'Failed', value: {podPhaseCounts.Failed}, }, ] : []), ]} /> {/* Active GPU pods list (running only, trimmed to top 10) */} {gpuPods.filter(p => p.status?.phase === 'Running').length > 0 && ( p.metadata.name }, { label: 'Namespace', getter: p => p.metadata.namespace ?? '—' }, { label: 'Node', getter: p => p.spec?.nodeName ?? '—' }, { label: 'GPU Request', getter: p => { const reqs = getPodGpuRequests(p); const parts: string[] = []; for (const [key, val] of Object.entries(reqs)) { const shortKey = key.replace(INTEL_GPU_RESOURCE_PREFIX, ''); parts.push(`${shortKey}: ${val}`); } return parts.join(', ') || '—'; }, }, { label: 'Age', getter: p => formatAge(p.metadata.creationTimestamp) }, ]} data={gpuPods.filter(p => p.status?.phase === 'Running').slice(0, 10)} /> )} ); }