/** * OverviewPage — main dashboard for the Rook-Ceph plugin. * * Shows: cluster health, capacity overview, storage resource counts, * daemon pod summary, and non-Bound PVC alerts. */ import { Loader, NameValueTable, PercentageBar, SectionBox, SectionHeader, SimpleTable, StatusLabel, } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import React from 'react'; import { formatAge, formatBytes, healthToStatus, parseStorageToBytes, phaseToStatus, storageClassType, } from '../api/k8s'; import { useRookCephContext } from '../api/RookCephDataContext'; import ClusterStatusCard from './ClusterStatusCard'; export default function OverviewPage() { const { cephClusters, clusterInstalled, blockPools, filesystems, objectStores, storageClasses, persistentVolumes, persistentVolumeClaims, operatorPods, monPods, osdPods, mgrPods, csiRbdPods, csiCephfsPods, loading, error, refresh, } = useRookCephContext(); if (loading) { return ; } // Storage summary const rbdClasses = storageClasses.filter(sc => storageClassType(sc) === 'rbd'); const cephfsClasses = storageClasses.filter(sc => storageClassType(sc) === 'cephfs'); const totalCapacityBytes = persistentVolumes.reduce((sum, pv) => { const cap = pv.spec.capacity?.storage ?? '0'; return sum + parseStorageToBytes(cap); }, 0); const pvcStatusCounts = { Bound: 0, Pending: 0, Lost: 0, Other: 0 }; for (const pvc of persistentVolumeClaims) { const phase = pvc.status?.phase ?? 'Other'; if (phase === 'Bound') pvcStatusCounts.Bound++; else if (phase === 'Pending') pvcStatusCounts.Pending++; else if (phase === 'Lost') pvcStatusCounts.Lost++; else pvcStatusCounts.Other++; } const nonBoundPvcs = persistentVolumeClaims.filter(pvc => pvc.status?.phase !== 'Bound'); // Primary cluster health (first cluster) const primaryCluster = cephClusters[0]; const primaryHealth = primaryCluster?.status?.ceph?.health; return ( <>
{/* Cluster not detected */} {!clusterInstalled && !loading && ( No CephCluster found in namespace rook-ceph ), }, { name: 'Install', value: 'helm install rook-ceph rook-release/rook-ceph -n rook-ceph --create-namespace', }, { name: 'Docs', value: 'https://rook.io/docs/rook/latest/Getting-Started/quickstart/', }, ]} /> )} {/* Error state */} {error && ( {error} }]} /> )} {/* Quick health summary banner when cluster is installed */} {clusterInstalled && primaryHealth && ( {primaryHealth} ), }, { name: 'Clusters', value: String(cephClusters.length), }, ]} /> )} {/* Storage type distribution */} {storageClasses.length > 0 && (
StorageClass Type Distribution
0 ? [ { name: 'Block (RBD)', value: rbdClasses.length, fill: 'var(--mui-palette-primary-main, #1976d2)', }, ] : []), ...(cephfsClasses.length > 0 ? [ { name: 'Filesystem (CephFS)', value: cephfsClasses.length, fill: 'var(--mui-palette-secondary-main, #9c27b0)', }, ] : []), ]} total={storageClasses.length} />
{pvcStatusCounts.Bound}, }, ...(pvcStatusCounts.Pending > 0 ? [ { name: 'PVCs (Pending)', value: {pvcStatusCounts.Pending}, }, ] : []), ...(pvcStatusCounts.Lost > 0 ? [ { name: 'PVCs (Lost)', value: {pvcStatusCounts.Lost}, }, ] : []), ]} />
)} {/* Cluster status + capacity + daemon health */} {/* Block pools table */} {blockPools.length > 0 && ( p.metadata.name }, { label: 'Phase', getter: p => ( {p.status?.phase ?? 'Unknown'} ), }, { label: 'Replicas', getter: p => String(p.spec?.replicated?.size ?? '—') }, { label: 'Failure Domain', getter: p => p.spec?.failureDomain ?? '—' }, { label: 'Age', getter: p => formatAge(p.metadata.creationTimestamp) }, ]} data={blockPools} /> )} {/* Filesystems table */} {filesystems.length > 0 && ( f.metadata.name }, { label: 'Phase', getter: f => ( {f.status?.phase ?? 'Unknown'} ), }, { label: 'Active MDS', getter: f => String(f.spec?.metadataServer?.activeCount ?? '—'), }, { label: 'Age', getter: f => formatAge(f.metadata.creationTimestamp) }, ]} data={filesystems} /> )} {/* Object stores table */} {objectStores.length > 0 && ( o.metadata.name }, { label: 'Phase', getter: o => ( {o.status?.phase ?? 'Unknown'} ), }, { label: 'Gateway Port', getter: o => String(o.spec?.gateway?.port ?? '—') }, { label: 'Instances', getter: o => String(o.spec?.gateway?.instances ?? '—') }, { label: 'Age', getter: o => formatAge(o.metadata.creationTimestamp) }, ]} data={objectStores} /> )} {/* Non-bound PVCs warning */} {nonBoundPvcs.length > 0 && ( pvc.metadata.name }, { label: 'Namespace', getter: pvc => pvc.metadata.namespace ?? '—' }, { label: 'Status', getter: pvc => ( {pvc.status?.phase ?? 'Unknown'} ), }, { label: 'Age', getter: pvc => formatAge(pvc.metadata.creationTimestamp) }, ]} data={nonBoundPvcs} /> )} ); }