Skip to content

Commit

Permalink
Bug 1957756: Missing already replaced condition in disk modal
Browse files Browse the repository at this point in the history
The PR 8880 added checking for a replaced disk in the list page but we missed out adding in disk replacemnet modal as well.
When more templates are present for same disk then disk name cant be used, hence removed disk name usage in replacementMap

(cherry picked from commit c1d4d0e)
  • Loading branch information
Afreen Rahman committed May 31, 2021
1 parent 601b383 commit b4da6c8
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 10,987 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"replacement disallowed: disk {{diskName}} is {{replacingDiskStatus}}": "replacement disallowed: disk {{diskName}} is {{replacingDiskStatus}}",
"replacement disallowed: disk {{diskName}} is {{replacementStatus}}": "replacement disallowed: disk {{diskName}} is {{replacementStatus}}",
"Disk Replacement": "Disk Replacement",
"This action will start preparing the disk for replacement.": "This action will start preparing the disk for replacement.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ import {
SecretKind,
} from '@console/internal/module/k8s';
import { TemplateModel, TemplateInstanceModel, SecretModel } from '@console/internal/models';
import { DiskMetadata } from '@console/local-storage-operator-plugin/src/components/disks-list/types';
import { CEPH_STORAGE_NAMESPACE, OSD_REMOVAL_TEMPLATE, DASHBOARD_LINK } from '../../../constants';
import { OCSDiskList, OCSColumnStateAction, ActionType, Status } from './state-reducer';
import {
OCSColumnStateAction,
ActionType,
Status,
ReplacedDisk,
OCSColumnState,
} from './state-reducer';

const createTemplateSecret = async (template: TemplateKind, osdId: string) => {
const parametersSecret: SecretKind = {
Expand Down Expand Up @@ -82,18 +89,10 @@ const instantiateTemplate = async (osdId: string, nodeName: string, disk: DiskMe
const DiskReplacementAction = (props: DiskReplacementActionProps) => {
const { t } = useTranslation();

const {
disk,
alertsMap,
nodeName,
replacementMap,
isRebalancing,
dispatch,
cancel,
close,
} = props;
const { disk, nodeName, ocsState, dispatch, cancel, close } = props;

const { path: diskName } = disk;
const { path: diskName, deviceID: diskID, serial: diskSerial } = disk;
const { alertsMap, replacingDiskList, replacedDiskList, isRebalancing } = ocsState;

const [inProgress, setProgress] = React.useState(false);
const [errorMessage, setError] = React.useState('');
Expand All @@ -102,11 +101,38 @@ const DiskReplacementAction = (props: DiskReplacementActionProps) => {
event.preventDefault();
setProgress(true);
try {
const { osd } = alertsMap[diskName];
const replacementStatus = replacementMap[diskName]?.status;
let replacingDiskIndex = -1;
const replacingDiskStatus = Status.PreparingToReplace;
const { osd } = alertsMap?.[diskName] || { osd: '' };
const replacedDisk = replacedDiskList?.find((rd: ReplacedDisk) => {
const diskInfo = rd?.disk || { id: '', serial: '', path: '', node: '' };
return (
rd?.node === nodeName &&
diskInfo.path === diskName &&
diskInfo?.id === diskID &&
diskInfo?.serial === diskSerial
);
});
const { status: replacementStatus } = replacedDisk || {};

if (replacingDiskList.length)
replacingDiskIndex = replacingDiskList.findIndex(
(rd) => rd?.id === diskID && rd?.serial === diskSerial && rd?.path === diskName,
);

if (replacingDiskIndex !== -1) {
throw new Error(
t(
'ceph-storage-plugin~replacement disallowed: disk {{diskName}} is {{replacingDiskStatus}}',
{ diskName, replacingDiskStatus },
),
);
}

if (
replacementStatus === Status.PreparingToReplace ||
replacementStatus === Status.ReplacementReady
replacementStatus === Status.ReplacementReady ||
replacementStatus === Status.ReplacementFailed
)
throw new Error(
t(
Expand All @@ -117,8 +143,14 @@ const DiskReplacementAction = (props: DiskReplacementActionProps) => {
else {
instantiateTemplate(osd, nodeName, disk);
dispatch({
type: ActionType.SET_REPLACEMENT_MAP,
payload: { [diskName]: { osd, status: Status.PreparingToReplace } },
type: ActionType.SET_REPLACING_DISK_LIST,
payload: [
{
id: diskID,
serial: diskSerial,
path: diskName,
},
],
});
}
close();
Expand All @@ -134,7 +166,7 @@ const DiskReplacementAction = (props: DiskReplacementActionProps) => {
<ModalTitle>{t('ceph-storage-plugin~Disk Replacement')}</ModalTitle>
<ModalBody>
<p>{t('ceph-storage-plugin~This action will start preparing the disk for replacement.')}</p>
{isRebalancing && alertsMap[diskName].status !== Status.Offline && (
{isRebalancing && alertsMap?.[diskName]?.status !== Status.Offline && (
<Alert
variant="info"
className="co-alert"
Expand Down Expand Up @@ -166,9 +198,7 @@ export const diskReplacementModal = createModalLauncher(DiskReplacementAction);

export type DiskReplacementActionProps = {
disk: DiskMetadata;
isRebalancing: boolean;
alertsMap: OCSDiskList;
replacementMap: OCSDiskList;
nodeName: string;
ocsState: OCSColumnState;
dispatch: React.Dispatch<OCSColumnStateAction>;
} & ModalComponentProps;
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
OCSColumnStateAction,
Status,
OCSDiskStatus,
ReplacementMap,
ReplacedDisk,
} from './state-reducer';

const getTiBasedStatus = (status: string): OCSDiskStatus => {
Expand Down Expand Up @@ -110,6 +110,7 @@ const diskRow: RowFunction<DiskMetadata, OCSMetadata> = ({
<TableData className={tableColumnClasses[1]}>{obj.status.state}</TableData>
<OCSStatus
ocsState={ocsState}
nodeName={nodeName}
diskName={obj.path}
diskID={obj.deviceID}
diskSerial={obj.serial}
Expand All @@ -123,14 +124,7 @@ const diskRow: RowFunction<DiskMetadata, OCSMetadata> = ({
{humanizeBinaryBytes(obj.size).string || '-'}
</TableData>
<TableData className={tableColumnClasses[5]}>{obj.fstype || '-'}</TableData>
<OCSKebabOptions
disk={obj}
nodeName={nodeName}
alertsMap={ocsState.alertsMap}
replacementMap={ocsState.replacementMap}
isRebalancing={ocsState.isRebalancing}
dispatch={dispatch}
/>
<OCSKebabOptions disk={obj} nodeName={nodeName} ocsState={ocsState} dispatch={dispatch} />
</TableRow>
);
};
Expand Down Expand Up @@ -167,14 +161,15 @@ const OCSDisksList: React.FC<TableProps> = React.memo((props) => {
ocsDiskList[metric.device] = {
osd: metric.ceph_daemon,
status: Status.Online,
node: metric.exported_instance,
};
return ocsDiskList;
}, {});
const newAlertsMap: OCSDiskList = alertsData.reduce(
(ocsDiskList: OCSDiskList, alert: Alert) => {
const { rule, labels } = alert;
const status = getAlertsBasedStatus(rule.name);
if (status) ocsDiskList[labels.device] = { osd: labels.disk, status };
if (status) ocsDiskList[labels.device] = { osd: labels.disk, status, node: labels.host };
return ocsDiskList;
},
{},
Expand All @@ -193,24 +188,49 @@ const OCSDisksList: React.FC<TableProps> = React.memo((props) => {
}

if (tiLoaded && !tiLoadError && tiData.length) {
const newData: ReplacementMap = tiData.reduce((data: ReplacementMap, ti) => {
const newData: ReplacedDisk[] = tiData.reduce((data: ReplacedDisk[], ti) => {
const { devicePath, deviceID, deviceOsd, deviceNode, deviceSerial } =
getAnnotations(ti) || {};
if (devicePath && deviceOsd && deviceNode === nodeName) {
data[devicePath] = {
data.push({
osd: deviceOsd,
disk: {
id: deviceID,
path: devicePath,
serial: deviceSerial,
},
node: nodeName,
status: getTiBasedStatus(ti.status.conditions?.[0].type),
};
});
}
return data;
}, {});
if (!_.isEqual(newData, ocsState.replacementMap)) {
}, []);
if (!_.isEqual(newData, ocsState.replacedDiskList)) {
dispatch({
type: ActionType.SET_REPLACED_DISK_LIST,
payload: newData,
});
}
}

if (ocsState.replacingDiskList.length !== 0) {
const replacedDiskIndexList = ocsState.replacingDiskList.reduce((indexes, disk, index) => {
const hasReplaced = ocsState.replacedDiskList?.some((rd: ReplacedDisk) => {
const diskInfo = rd?.disk;
return (
diskInfo?.path === disk.path &&
diskInfo?.id === disk.id &&
diskInfo?.serial === disk.serial
);
});
if (hasReplaced) indexes.push(index);
return indexes;
}, []);
if (replacedDiskIndexList.length) {
const newData = [...ocsState.replacingDiskList];
replacedDiskIndexList.forEach((index) => newData.splice(index, 1));
dispatch({
type: ActionType.SET_REPLACEMENT_MAP,
type: ActionType.SET_REPLACING_DISK_LIST,
payload: newData,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { useTranslation } from 'react-i18next';
import { TableData } from '@console/internal/components/factory';
import { KebabOption, Kebab } from '@console/internal/components/utils';
import { diskReplacementModal } from './disk-replacement-modal';
import { OCSDiskList, OCSColumnStateAction } from './state-reducer';
import { OCSColumnState, OCSColumnStateAction } from './state-reducer';
import { DiskMetadata } from '@console/local-storage-operator-plugin/src/components/disks-list/types';

export const OCSKebabOptions: React.FC<OCSKebabOptionsProps> = React.memo(
({ nodeName, disk, alertsMap, replacementMap, isRebalancing, dispatch }) => {
({ nodeName, disk, ocsState, dispatch }) => {
const { t } = useTranslation();

const { alertsMap } = ocsState;

const kebabOptions: KebabOption[] = [
{
// t('ceph-storage-plugin~Start Disk Replacement')
Expand All @@ -18,9 +20,7 @@ export const OCSKebabOptions: React.FC<OCSKebabOptionsProps> = React.memo(
diskReplacementModal({
nodeName,
disk,
alertsMap,
replacementMap,
isRebalancing,
ocsState,
dispatch,
}),
},
Expand All @@ -29,7 +29,7 @@ export const OCSKebabOptions: React.FC<OCSKebabOptionsProps> = React.memo(
return (
<TableData className={Kebab.columnClass}>
{/* Enables the options for the disk with failures */}
<Kebab options={kebabOptions} isDisabled={!alertsMap[disk.path]} />
<Kebab options={kebabOptions} isDisabled={alertsMap?.[disk.path]?.node !== nodeName} />
</TableData>
);
},
Expand All @@ -38,8 +38,6 @@ export const OCSKebabOptions: React.FC<OCSKebabOptionsProps> = React.memo(
type OCSKebabOptionsProps = {
disk: DiskMetadata;
nodeName: string;
alertsMap: OCSDiskList;
replacementMap: OCSDiskList;
isRebalancing: boolean;
ocsState: OCSColumnState;
dispatch: React.Dispatch<OCSColumnStateAction>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { TFunction } from 'i18next';
import { Trans, useTranslation } from 'react-i18next';
import { OffIcon } from '@patternfly/react-icons';
import { TableData } from '@console/internal/components/factory';
import { OCSColumnState, Status, OCSDiskStatus, getOCSColumnStatus } from './state-reducer';
import {
Status,
OCSDiskStatus,
OCSColumnState,
getOCSColumnStatus,
ReplacedDisk,
} from './state-reducer';
import { ExternalLink } from '@console/internal/components/utils';
import {
ErrorStatus,
Expand All @@ -12,6 +18,7 @@ import {
ProgressStatus,
StatusIconAndText,
} from '@console/shared';

import './ocs-status-column.scss';

const getOCSStatusBody = (
Expand Down Expand Up @@ -73,21 +80,39 @@ export const OCSStatus: React.FC<{
diskName: string;
diskID: string;
diskSerial: string;
nodeName: string;
className: string;
}> = React.memo(({ ocsState, className, diskName, diskID, diskSerial }) => {
}> = React.memo(({ ocsState, className, diskName, diskID, diskSerial, nodeName }) => {
const { t } = useTranslation();

const { replacementMap, alertsMap, metricsMap } = ocsState;
const { id, serial } = replacementMap?.[diskName]?.disk || {};
const { replacedDiskList, alertsMap, metricsMap, replacingDiskList } = ocsState;
let status: OCSDiskStatus;
let replacingDiskIndex: number = -1;

const { status: replacementStatus } =
replacedDiskList?.find((rd: ReplacedDisk) => {
const diskInfo = rd?.disk || { id: '', serial: '', path: '' };
return (
diskInfo.path === diskName &&
diskInfo.id === diskID &&
diskInfo.serial === diskSerial &&
rd?.node === nodeName
);
}) || {};

if (replacingDiskList.length)
replacingDiskIndex = replacingDiskList.findIndex(
(disk) => disk?.id === diskID && disk?.serial === diskSerial && disk?.path === diskName,
);

// If device replacement is just triggered from modal and template status is not fetched
if (replacingDiskIndex !== -1) status = Status.PreparingToReplace;
// If device is already replaced then show the replacement status
if (replacementMap[diskName] && id === diskID && serial === diskSerial)
status = replacementMap[diskName].status;
else if (replacementStatus) status = replacementStatus;
// If device is failed then show the failure status
else if (alertsMap[diskName]) status = alertsMap[diskName].status;
else if (alertsMap[diskName]?.node === nodeName) status = alertsMap[diskName].status;
// If device is neither failed nor replaced then show underlying osd status
else if (metricsMap[diskName]) status = metricsMap[diskName].status;
else if (metricsMap[diskName]?.node === nodeName) status = metricsMap[diskName].status;

return (
<TableData className={className}>
Expand Down

0 comments on commit b4da6c8

Please sign in to comment.