Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat): Add OCS kebab actions in the LSO disk inventory related to the day2 operations #6124

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import {
ModalTitle,
ModalBody,
ModalSubmitFooter,
createModalLauncher,
ModalComponentProps,
} from '@console/internal/components/factory';
import {
k8sGet,
k8sCreate,
TemplateKind,
TemplateInstanceKind,
apiVersionForModel,
SecretKind,
} from '@console/internal/module/k8s';
afreen23 marked this conversation as resolved.
Show resolved Hide resolved
import { TemplateModel, TemplateInstanceModel, SecretModel } from '@console/internal/models';
import { CEPH_STORAGE_NAMESPACE } from '../../../constants';

const OSD_REMOVAL_TEMPLATE = 'ocs-osd-removal';

const createTemplateSecret = async (template: TemplateKind, osdId: string) => {
const parametersSecret: SecretKind = {
apiVersion: SecretModel.apiVersion,
kind: SecretModel.kind,
metadata: {
name: `${OSD_REMOVAL_TEMPLATE}-parameters`,
namespace: CEPH_STORAGE_NAMESPACE,
},
stringData: {
[template.parameters[0].name]: osdId,
},
};
return k8sCreate(SecretModel, parametersSecret);
};

const createTemplateInstance = async (parametersSecret: SecretKind, template: TemplateKind) => {
const templateInstance: TemplateInstanceKind = {
apiVersion: apiVersionForModel(TemplateInstanceModel),
kind: TemplateInstanceModel.kind,
metadata: {
name: `${OSD_REMOVAL_TEMPLATE}-template-instance`,
namespace: CEPH_STORAGE_NAMESPACE,
},
spec: {
secret: {
name: parametersSecret.metadata.name,
},
template,
},
};
return k8sCreate(TemplateInstanceModel, templateInstance);
};

const instantiateTemplate = async (osdId: string) => {
const osdRemovalTemplate = await k8sGet(
TemplateModel,
OSD_REMOVAL_TEMPLATE,
CEPH_STORAGE_NAMESPACE,
);
const templateSecret = await createTemplateSecret(osdRemovalTemplate, osdId);
await createTemplateInstance(templateSecret, osdRemovalTemplate);
};

const DiskReplacementAction = (props: DiskReplacementActionProps) => {
const { diskName, osdId, cancel, close } = props;

const [inProgress, setProgress] = React.useState(false);
const [errorMessage, setError] = React.useState('');

const handleSubmit = (event) => {
event.preventDefault();
setProgress(true);
/*
* TODO:(Afreen) Add validations based on ocs status (part of followup PR)
*/
try {
instantiateTemplate(osdId);
close();
} catch (err) {
setError(err.message);
} finally {
setProgress(false);
}
};

return (
<form onSubmit={handleSubmit} name="form" className="modal-content">
<ModalTitle>Disk Replacement</ModalTitle>
<ModalBody>
<p>This action will start preparing the disk for replacement.</p>
<p>
Are you sure you want to replace <strong>{diskName}</strong> ?
</p>
</ModalBody>
<ModalSubmitFooter
errorMessage={errorMessage}
inProgress={inProgress}
submitText="Replace"
cancel={cancel}
/>
</form>
);
};

export const diskReplacementModal = createModalLauncher(DiskReplacementAction);

export type DiskReplacementActionProps = {
diskName: string;
osdId: string;
} & ModalComponentProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from 'react';
import { TableData } from '@console/internal/components/factory';
import { KebabOption, Kebab } from '@console/internal/components/utils';
import { diskReplacementModal } from './disk-replacement-modal';

const startDiskReplacementAction = (diskName: string, osdId: string): KebabOption => ({
label: 'Start Disk Replacement',
callback: () =>
diskReplacementModal({
diskName,
osdId,
}),
});

export const OCSKebabOptions: React.FC<OCSKebabOptionsProps> = ({ diskName, diskOsdMap }) => {
const osdId: string = diskOsdMap.get(diskName);
const kebabOptions: KebabOption[] = [startDiskReplacementAction(diskName, osdId)];

return (
<TableData className={Kebab.columnClass}>
{/* Disable options for non OCS based disks */}
<Kebab options={kebabOptions} isDisabled={!!osdId} />
</TableData>
);
};

type OCSKebabOptionsProps = { diskName: string; diskOsdMap: Map<string, string> };
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import {
MultiListPage,
} from '@console/internal/components/factory';
import { sortable } from '@patternfly/react-table';
import { FirehoseResult, humanizeBinaryBytes } from '@console/internal/components/utils';
import { FirehoseResult, humanizeBinaryBytes, Kebab } from '@console/internal/components/utils';
import { referenceForModel, NodeKind, K8sResourceCommon } from '@console/internal/module/k8s';
import { RowFilter } from '@console/internal/components/filter-toolbar';
import { useFlag } from '@console/shared/src/hooks/flag';
import { OCS_ATTACHED_DEVICES_FLAG } from '@console/ceph-storage-plugin/src/features';
import { OCSKebabOptions } from '@console/ceph-storage-plugin/src/components/attached-devices-mode/lso-disk-inventory/ocs-kebab-options';
afreen23 marked this conversation as resolved.
Show resolved Hide resolved
import { LocalVolumeDiscoveryResult } from '../../models';
import { LABEL_SELECTOR } from '../../constants/disks-list';

Expand Down Expand Up @@ -45,10 +48,11 @@ export const tableColumnClasses = [
cx('pf-m-hidden', 'pf-m-visible-on-2xl'),
cx('pf-m-hidden', 'pf-m-visible-on-lg'),
cx('pf-m-hidden', 'pf-m-visible-on-xl'),
Kebab.columnClass,
];

const DiskHeader = () => {
return [
const getDiskHeader = (isOCSAttachedDevices: boolean) => {
const headers = [
{
title: 'Name',
sortField: 'path',
Expand Down Expand Up @@ -86,9 +90,26 @@ const DiskHeader = () => {
props: { className: tableColumnClasses[5] },
},
];
if (isOCSAttachedDevices) {
headers.push({
title: '',
sortField: '',
transforms: [],
props: { className: tableColumnClasses[6] },
});
}
return () => headers;
};

const DiskRow: RowFunction<DiskMetadata> = ({ obj, index, key, style }) => {
const diskRow: RowFunction<DiskMetadata, OCSMetadata> = ({
obj,
index,
key,
style,
customData,
}) => {
const { isOCSAttachedDevices, diskOsdMap } = customData;
const diskName = obj.path;
return (
<TableRow id={obj.deviceID} index={index} trKey={key} style={style}>
<TableData className={tableColumnClasses[0]}>{obj.path}</TableData>
Expand All @@ -101,15 +122,18 @@ const DiskRow: RowFunction<DiskMetadata> = ({ obj, index, key, style }) => {
{humanizeBinaryBytes(obj.size).string || '-'}
</TableData>
<TableData className={tableColumnClasses[5]}>{obj.fstype || '-'}</TableData>
{isOCSAttachedDevices && <OCSKebabOptions diskName={diskName} diskOsdMap={diskOsdMap} />}
</TableRow>
);
};

const DisksList: React.FC<TableProps> = (props) => {
return <Table {...props} aria-label="Disks List" Header={DiskHeader} Row={DiskRow} virtualize />;
const diskHeader = getDiskHeader(props.customData.isOCSAttachedDevices);
return <Table {...props} aria-label="Disks List" Header={diskHeader} Row={diskRow} virtualize />;
};

const DisksListPage: React.FC<{ obj: NodeKind }> = (props) => {
const isOCSAttachedDevices = useFlag(OCS_ATTACHED_DEVICES_FLAG);
const nodeName = props.obj.metadata.name;
const propName = `lvdr-${nodeName}`;

Expand All @@ -121,7 +145,7 @@ const DisksListPage: React.FC<{ obj: NodeKind }> = (props) => {
textFilter="node-disk-name"
rowFilters={diskFilters}
flatten={(resource: FirehoseResult<LocalVolumeDiscoveryResult>) =>
resource[propName]?.data[0]?.status?.discoveredDevices ?? {}
resource[propName]?.data[0]?.status?.discoveredDevices ?? []
}
ListComponent={DisksList}
resources={[
Expand All @@ -131,13 +155,21 @@ const DisksListPage: React.FC<{ obj: NodeKind }> = (props) => {
selector: { [LABEL_SELECTOR]: nodeName },
},
]}
customData={{
diskOsdMap: new Map() /* TBD(Afreen) Will be changed to actual state with this https://issues.redhat.com/browse/RHSTOR-1194 */,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question - where are you exactly adding values inside diskOsdMap as you are getting the disk name from this Map?

Copy link
Contributor Author

@afreen23 afreen23 Jul 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add the local state to disk list page which will poll data and the pass it down the children.
Will do as part of mentioned issue, its part of another PR.

isOCSAttachedDevices,
}}
/>
);
};

export default DisksListPage;

type DiskMetadata = LocalVolumeDiscoveryResult['status']['discoveredDevices'];
type OCSMetadata = {
isOCSAttachedDevices: boolean;
diskOsdMap: Map<string, string>;
};

type LocalVolumeDiscoveryResult = K8sResourceCommon & {
spec: {
Expand Down
4 changes: 2 additions & 2 deletions frontend/public/module/k8s/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -920,9 +920,9 @@ export type GroupVersionKind = string;
export type K8sResourceKindReference = GroupVersionKind | string;

export type SecretKind = {
data: { [key: string]: string };
data?: { [key: string]: string };
stringData?: { [key: string]: string };
type: string;
type?: string;
} & K8sResourceCommon;

export type ServiceAccountKind = {
Expand Down