/**
* 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}
/>
)}
>
);
}