diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index 2800d05f9..9ecf5349b 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -441,6 +441,9 @@ "Only showing PVCs that are being mounted on an active pod": "Only showing PVCs that are being mounted on an active pod", "This card shows the requested capacity for different Kubernetes resources. The figures shown represent the usable storage, meaning that data replication is not taken into consideration.": "This card shows the requested capacity for different Kubernetes resources. The figures shown represent the usable storage, meaning that data replication is not taken into consideration.", "Internal": "Internal", + "Disaster recovery optimisation": "Disaster recovery optimisation", + "Configure cluster for Regional-DR?": "Configure cluster for Regional-DR?", + "<0>Optimise the cluster for a Regional-DR setup by migrating OSDs. Migration may take sometime depending on several factors. To learn more about OSDs migration best practices and its consequences refer to the documentation": "<0>Optimise the cluster for a Regional-DR setup by migrating OSDs. Migration may take sometime depending on several factors. To learn more about OSDs migration best practices and its consequences refer to the documentation", "Raw capacity is the absolute total disk space available to the array subsystem.": "Raw capacity is the absolute total disk space available to the array subsystem.", "Troubleshoot": "Troubleshoot", "Active health checks": "Active health checks", diff --git a/packages/ocs/constants/common.ts b/packages/ocs/constants/common.ts index 85e8824d2..7d176d980 100644 --- a/packages/ocs/constants/common.ts +++ b/packages/ocs/constants/common.ts @@ -42,3 +42,12 @@ export enum CLUSTER_STATUS { } export const CEPH_INTERNAL_CR_NAME = 'ocs-storagecluster-cephcluster'; + +export const DISASTER_RECOVERY_TARGET_ANNOTATION = + 'ocs.openshift.io/clusterIsDisasterRecoveryTarget'; + +export enum DRSetupStatus { + InProgress = 'In Progress', + Pending = 'Pending', + Completed = 'Completed', +} diff --git a/packages/ocs/dashboards/persistent-external/details-card.tsx b/packages/ocs/dashboards/persistent-external/details-card.tsx index e1b12427b..562b598a7 100644 --- a/packages/ocs/dashboards/persistent-external/details-card.tsx +++ b/packages/ocs/dashboards/persistent-external/details-card.tsx @@ -60,6 +60,7 @@ export const DetailsCard: React.FC = () => { : t('Disabled'); const ocsName = getName(resourcesObj['ocs'].data?.[0]); + //const disasterRecoveryStatus = getDRsetupStatus(); const [csv, csvLoaded, csvError] = useFetchCsv({ specName: !isODF ? OCS_OPERATOR : ODF_OPERATOR, diff --git a/packages/ocs/dashboards/persistent-internal/details-card.tsx b/packages/ocs/dashboards/persistent-internal/details-card.tsx index e634b49df..164923d11 100644 --- a/packages/ocs/dashboards/persistent-internal/details-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/details-card.tsx @@ -5,32 +5,113 @@ import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; import { useFetchCsv } from '@odf/shared/hooks/use-fetch-csv'; import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { + CephClusterModel, ClusterServiceVersionModel, InfrastructureModel, } from '@odf/shared/models'; -import { getName } from '@odf/shared/selectors'; -import { K8sResourceKind, StorageClusterKind } from '@odf/shared/types'; +import { getAnnotations, getName } from '@odf/shared/selectors'; +import { + CephClusterKind, + K8sResourceKind, + StorageClusterKind, +} from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { getInfrastructurePlatform, resourcePathFromModel, } from '@odf/shared/utils'; +import { k8sUpdate } from '@openshift-console/dynamic-plugin-sdk'; import { DetailsBody } from '@openshift-console/dynamic-plugin-sdk-internal'; import { OverviewDetailItem as DetailItem } from '@openshift-console/plugin-shared'; +import { Trans } from 'react-i18next'; import { Link } from 'react-router-dom'; import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; +import { Modal, ModalVariant, Button, Alert } from '@patternfly/react-core'; import { CEPH_NS } from '../../constants'; +import { + DISASTER_RECOVERY_TARGET_ANNOTATION, + DRSetupStatus, +} from '../../constants'; import { StorageClusterModel } from '../../models'; import { getNetworkEncryption } from '../../utils'; const DetailsCard: React.FC = () => { const { t } = useCustomTranslation(); + const [data, loaded, loadError] = useK8sList( + CephClusterModel, + CEPH_NS + ); + + const [isOpen, setOpen] = React.useState(false); + const [updateError, setUpdateError] = React.useState(''); + + const openModal = () => { + setOpen(true); + }; + + const closeModal = () => { + setOpen(false); + }; + + const handleOptimize = () => { + const updatedResource = { ...data[0] }; + + updatedResource.metadata.annotations = { + ...getAnnotations(updatedResource, {}), + [DISASTER_RECOVERY_TARGET_ANNOTATION]: 'true', + }; + + const updateData = { + model: CephClusterModel, + data: updatedResource, + }; + + k8sUpdate(updateData) + .then(() => { + closeModal(); + }) + .catch((err) => { + setUpdateError(err.message); + }); + }; + + const getType = () => { + if (data && data.length > 0) { + const bluestoreCount = + data[0]?.status?.storage?.osd?.storeType?.['bluestore']; + const bluestoreRdrCount = + data[0]?.status?.storage?.osd?.storeType?.['bluestore-rdr']; + + const isDisasterRecoveryTarget = + data[0]?.metadata?.annotations?.[ + DISASTER_RECOVERY_TARGET_ANNOTATION + ] === 'true'; + + if (bluestoreCount > 0) { + if (bluestoreRdrCount > 0) { + return DRSetupStatus.InProgress; + } else if (isDisasterRecoveryTarget) { + return DRSetupStatus.InProgress; + } else { + return DRSetupStatus.Pending; + } + } else if (bluestoreRdrCount > 0) { + return 'Completed'; + } + + return ''; + } + }; + + const dRSetupStatus = getType(); + const [infrastructure, infrastructureLoaded, infrastructureError] = useK8sGet(InfrastructureModel, 'cluster'); const [ocsData, ocsLoaded, ocsError] = useK8sList( StorageClusterModel, CEPH_NS ); + const [csv, csvLoaded, csvError] = useFetchCsv({ specName: ODF_OPERATOR, namespace: CEPH_STORAGE_NAMESPACE, @@ -100,6 +181,61 @@ const DetailsCard: React.FC = () => { > {inTransitEncryptionStatus} + + {loaded && !loadError ? ( +
+
+

{dRSetupStatus}

+ {dRSetupStatus === DRSetupStatus.Pending && ( + Optimize + )} +
+ + Close + , + , + ]} + > + +

+ Optimise the cluster for a Regional-DR setup by migrating + OSDs. Migration may take sometime depending on several + factors. To learn more about OSDs migration best practices + and its consequences refer to the documentation +

+
+
+
+ ) : ( + '' + )} + {updateError && ( + + {updateError} + + )} +
diff --git a/packages/shared/src/types/storage.ts b/packages/shared/src/types/storage.ts index 4bc1f2f36..d67fa3093 100644 --- a/packages/shared/src/types/storage.ts +++ b/packages/shared/src/types/storage.ts @@ -105,6 +105,12 @@ type CephDeviceClass = { export type CephClusterKind = K8sResourceCommon & { status?: { storage: { + osd: { + storeType: { + bluestore: number; + 'bluestore-rdr': number; + }; + }; deviceClasses: CephDeviceClass[]; }; ceph?: {