/** * ClusterStatusCard — reusable component showing Rook-Ceph cluster health. * Displays CephCluster health, phase, capacity, version, and daemon pod counts. */ import { NameValueTable, PercentageBar, SectionBox, StatusLabel, } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import React from 'react'; import type { CephCluster, RookCephPod } from '../api/k8s'; import { formatAge, formatBytes, getPodImage, getPodRestarts, healthToStatus, isPodReady, phaseToStatus } from '../api/k8s'; interface ClusterStatusCardProps { cephClusters: CephCluster[]; operatorPods: RookCephPod[]; monPods: RookCephPod[]; osdPods: RookCephPod[]; mgrPods: RookCephPod[]; csiRbdPods: RookCephPod[]; csiCephfsPods: RookCephPod[]; } function PodStatusBadge({ pod }: { pod: RookCephPod }) { const ready = isPodReady(pod); const phase = pod.status?.phase ?? 'Unknown'; return ( {phase} ); } function PodSummaryRow({ pods, label }: { pods: RookCephPod[]; label: string }) { const ready = pods.filter(isPodReady).length; const total = pods.length; const status = total === 0 ? 'error' : ready === total ? 'success' : ready > 0 ? 'warning' : 'error'; return { name: label, value: ( {total === 0 ? 'None found' : `${ready}/${total} ready`} ), }; } export default function ClusterStatusCard({ cephClusters, operatorPods, monPods, osdPods, mgrPods, csiRbdPods, csiCephfsPods, }: ClusterStatusCardProps) { return ( <> {cephClusters.map(cluster => { const health = cluster.status?.ceph?.health; const phase = cluster.status?.phase; const capacity = cluster.status?.ceph?.capacity; const version = cluster.status?.version?.version ?? '—'; const bytesTotal = capacity?.bytesTotal ?? 0; const bytesUsed = capacity?.bytesUsed ?? 0; const bytesAvail = capacity?.bytesAvailable ?? 0; const usedPct = bytesTotal > 0 ? Math.round((bytesUsed / bytesTotal) * 100) : 0; return ( {health ?? 'Unknown'} ), }, { name: 'Phase', value: ( {phase ?? 'Unknown'} ), }, ...(cluster.status?.message ? [{ name: 'Message', value: cluster.status.message }] : []), { name: 'Ceph Version', value: version }, { name: 'Namespace', value: cluster.metadata.namespace ?? '—' }, { name: 'Age', value: formatAge(cluster.metadata.creationTimestamp) }, ]} /> {bytesTotal > 0 && (
80 ? '#f44336' : '#1976d2' }, { name: 'Free', value: bytesAvail, fill: '#e0e0e0' }, ]} total={bytesTotal} />
)}
); })} ); } export function PodDetailRows({ pods, label }: { pods: RookCephPod[]; label: string }) { if (pods.length === 0) { return ( No pods found }]} /> ); } return ( {pods.map(pod => ( }, { name: 'Restarts', value: String(getPodRestarts(pod)) }, { name: 'Image', value: getPodImage(pod) }, { name: 'Age', value: formatAge(pod.metadata.creationTimestamp) }, ]} /> ))} ); }