Skip to content

Commit

Permalink
Merge pull request #6123 from gnehapk/disk-list
Browse files Browse the repository at this point in the history
Added disk list for BM/Attached devices OCS install
  • Loading branch information
openshift-merge-robot committed Jul 29, 2020
2 parents 6e6798c + 87fc007 commit 72234eb
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 64 deletions.
Expand Up @@ -57,3 +57,20 @@
height: var(--pf-global--LineHeight--sm);
margin: 0 var(--pf-global--spacer--sm);
}

.ceph-ocs-install__disk-list-btn {
padding-top: 0;
padding-left: 0;
cursor: pointer;
}

.ceph-ocs-install__disks-modal {
max-height: 75%;
width: 50%;
}

// inner class of modal for modifying margin
.pf-c-modal-box__body {
margin-top: var(--pf-global--spacer--md);
margin-bottom: var(--pf-global--spacer--md);
}
Expand Up @@ -17,8 +17,11 @@ import { getDiscoveryRequestData } from '@console/local-storage-operator-plugin/
import {
LOCAL_STORAGE_NAMESPACE,
DISCOVERY_CR_NAME,
HOSTNAME_LABEL_KEY,
LABEL_OPERATOR,
AUTO_DISCOVER_ERR_MSG,
} from '@console/local-storage-operator-plugin/src/constants';
import { getNodes } from '@console/local-storage-operator-plugin/src/utils';
import { getNodes, getLabelIndex } from '@console/local-storage-operator-plugin/src/utils';
import {
DiskMechanicalProperty,
DiskType,
Expand Down Expand Up @@ -46,20 +49,33 @@ const makeAutoDiscoveryCall = (

fetchK8s(LocalVolumeDiscovery, DISCOVERY_CR_NAME, LOCAL_STORAGE_NAMESPACE)
.then((discoveryRes: K8sResourceKind) => {
const nodes = new Set(
discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms?.[0]?.matchExpressions?.[0]?.values,
);
selectedNodes.forEach((name) => nodes.add(name));
const patch = [
{
op: 'replace',
path: `/spec/nodeSelector/nodeSelectorTerms/0/matchExpressions/0/values`,
value: Array.from(nodes),
},
];
return k8sPatch(LocalVolumeDiscovery, discoveryRes, patch);
const nodeSelectorTerms = discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms;
const [selectorIndex, expIndex] = nodeSelectorTerms
? getLabelIndex(nodeSelectorTerms, HOSTNAME_LABEL_KEY, LABEL_OPERATOR)
: [-1, -1];
if (selectorIndex !== -1 && expIndex !== -1) {
const nodes = new Set(
discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms?.[selectorIndex]?.matchExpressions?.[
expIndex
]?.values,
);
selectedNodes.forEach((name) => nodes.add(name));
const patch = [
{
op: 'replace',
path: `/spec/nodeSelector/nodeSelectorTerms/${selectorIndex}/matchExpressions/${expIndex}/values`,
value: Array.from(nodes),
},
];
return k8sPatch(LocalVolumeDiscovery, discoveryRes, patch);
}
throw new Error(AUTO_DISCOVER_ERR_MSG);
})
.catch(() => {
.catch((err) => {
// handle AUTO_DISCOVER_ERR_MSG and throw to next catch block to show the message
if (err.message === AUTO_DISCOVER_ERR_MSG) {
throw err;
}
const requestData = getDiscoveryRequestData(state);
return k8sCreate(LocalVolumeDiscovery, requestData);
})
Expand Down
Expand Up @@ -26,6 +26,7 @@ export const initialState: State = {
chartDataUnit: '',
showConfirmModal: false,
finalStep: false,
showDiskList: false,

// common states
isLoading: false,
Expand Down Expand Up @@ -75,6 +76,7 @@ export type State = {
onNextClick: () => void;
filteredDiscoveries: Discoveries[];
finalStep: boolean;
showDiskList: boolean;
};

export type Action =
Expand Down Expand Up @@ -102,7 +104,8 @@ export type Action =
| { type: 'setShowConfirmModal'; value: boolean }
| { type: 'setOnNextClick'; value: OnNextClick }
| { type: 'setFilteredDiscoveries'; value: Discoveries[] }
| { type: 'setFinalStep'; value: boolean };
| { type: 'setFinalStep'; value: boolean }
| { type: 'setShowDiskList'; value: boolean };

export const reducer = (state: State, action: Action) => {
switch (action.type) {
Expand Down Expand Up @@ -154,6 +157,8 @@ export const reducer = (state: State, action: Action) => {
return Object.assign({}, state, { filteredDiscoveries: action.value });
case 'setFinalStep':
return Object.assign({}, state, { finalStep: action.value });
case 'setShowDiskList':
return Object.assign({}, state, { showDiskList: action.value });
default:
return initialState;
}
Expand Down
Empty file.
Expand Up @@ -85,25 +85,23 @@ const ConfirmationModal = ({ state, dispatch }) => {
};

return (
<>
<Modal
title="Create Storage Class"
isOpen={state.showConfirmModal}
onClose={cancel}
variant="small"
actions={[
<Button key="confirm" variant="primary" onClick={makeLVSCall}>
Yes
</Button>,
<Button key="cancel" variant="link" onClick={cancel}>
Cancel
</Button>,
]}
>
{
"After the volume set and storage class are created you won't be able to go back to this step. Are you sure you want to continue?"
}
</Modal>
</>
<Modal
title="Create Storage Class"
isOpen={state.showConfirmModal}
onClose={cancel}
variant="small"
actions={[
<Button key="confirm" variant="primary" onClick={makeLVSCall}>
Yes
</Button>,
<Button key="cancel" variant="link" onClick={cancel}>
Cancel
</Button>,
]}
>
{
"After the volume set and storage class are created you won't be able to go back to this step. Are you sure you want to continue?"
}
</Modal>
);
};
@@ -0,0 +1,102 @@
import * as React from 'react';
import * as cx from 'classnames';
import { Table, TableRow, TableData, RowFunction } from '@console/internal/components/factory';
import { sortable, SortByDirection } from '@patternfly/react-table';
import { Button, Modal } from '@patternfly/react-core';
import { humanizeBinaryBytes } from '@console/internal/components/utils';
import { Discoveries, State, Action } from '../state';

const tableColumnClasses = [
'',
'',
cx('pf-m-hidden', 'pf-m-visible-on-xl'),
cx('pf-m-hidden', 'pf-m-visible-on-2xl'),
cx('pf-m-hidden', 'pf-m-visible-on-lg'),
];

export const DiskHeader = () => {
return [
{
title: 'Name',
sortField: 'path',
transforms: [sortable],
props: { className: tableColumnClasses[0] },
},
{
title: 'Node',
sortField: 'node',
transforms: [sortable],
props: { className: tableColumnClasses[1] },
},
{
title: 'Type',
sortField: 'type',
transforms: [sortable],
props: { className: tableColumnClasses[2] },
},
{
title: 'Model',
sortField: 'model',
transforms: [sortable],
props: { className: tableColumnClasses[3] },
},
{
title: 'Capacity',
sortField: 'size',
transforms: [sortable],
props: { className: tableColumnClasses[4] },
},
];
};

const DiskRow: RowFunction<Discoveries> = ({ obj, index, key, style }) => {
return (
<TableRow id={obj.deviceID} index={index} trKey={key} style={style}>
<TableData className={tableColumnClasses[0]}>{obj.path}</TableData>
<TableData className={tableColumnClasses[1]}>{obj.node}</TableData>
<TableData className={tableColumnClasses[2]}>{obj.type || '-'}</TableData>
<TableData className={cx(tableColumnClasses[3], 'co-break-word')}>
{obj.model || '-'}
</TableData>
<TableData className={tableColumnClasses[4]}>
{humanizeBinaryBytes(obj.size).string || '-'}
</TableData>
</TableRow>
);
};

export const DiskListModal: React.FC<DiskListModalProps> = ({ state, dispatch }) => {
const cancel = () => {
dispatch({ type: 'setShowDiskList', value: false });
};

return (
<Modal
title="Selected Disks"
isOpen={state.showDiskList}
onClose={cancel}
className="ceph-ocs-install__disks-modal"
actions={[
<Button key="confirm" variant="primary" onClick={cancel}>
Close
</Button>,
]}
>
<Table
data={state.filteredDiscoveries}
defaultSortField="node"
defaultSortOrder={SortByDirection.asc}
aria-label="Disk List"
Header={DiskHeader}
Row={DiskRow}
loaded={!!state.filteredDiscoveries}
virtualize
/>
</Modal>
);
};

type DiskListModalProps = {
state: State;
dispatch: React.Dispatch<Action>;
};
@@ -1,11 +1,15 @@
import * as React from 'react';
import { Button } from '@patternfly/react-core';
import { ChartDonut, ChartLabel } from '@patternfly/react-charts';

import { calculateRadius } from '@console/shared';
import { pluralize, convertToBaseValue } from '@console/internal/components/utils/';
import { getNodes } from '@console/local-storage-operator-plugin/src/utils';
import { DiskListModal } from './disk-list';
import { State, Action, Discoveries } from '../state';
import { dropdownUnits } from '../../../../../constants';
import { getTotalDeviceCapacity } from '../../../../../utils/install';
import '../../attached-devices.scss';

export const DiscoveryDonutChart: React.FC<DiscoveryDonutChartProps> = ({ state, dispatch }) => {
const [availableCapacityStr, setAvailableCapacityStr] = React.useState('');
Expand Down Expand Up @@ -63,7 +67,16 @@ export const DiscoveryDonutChart: React.FC<DiscoveryDonutChartProps> = ({ state,
<div className="ceph-ocs-install__stats">
<div>{pluralize(nodes.length, 'Node')}</div>
<div className="ceph-ocs-install_stats--divider" />
<div>{pluralize(state.filteredDiscoveries.length, 'Disk')}</div>
<div>
<Button
component="a"
variant="link"
onClick={() => dispatch({ type: 'setShowDiskList', value: true })}
className="ceph-ocs-install__disk-list-btn"
>
{pluralize(state.filteredDiscoveries.length, 'Disk')}
</Button>
</div>
</div>
<ChartDonut
ariaDesc="Selected versus Available Capacity"
Expand All @@ -81,6 +94,7 @@ export const DiscoveryDonutChart: React.FC<DiscoveryDonutChartProps> = ({ state,
<ChartLabel dy={5} style={{ fill: `var(--pf-global--palette--black-500)` }} />
}
/>
<DiskListModal state={state} dispatch={dispatch} />
</div>
);
};
Expand Down
Expand Up @@ -11,14 +11,20 @@ import {
} from '@console/internal/components/utils';
import { history } from '@console/internal/components/utils/router';
import { k8sCreate, k8sPatch, referenceFor, K8sResourceKind } from '@console/internal/module/k8s';
import { fetchK8s } from '@console/internal/graphql/client';
import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager';
import { getNodes } from '../../utils';
import { getNodes, getLabelIndex } from '../../utils';
import { AutoDetectVolumeInner, AutoDetectVolumeHeader } from './auto-detect-volume-inner';
import { getDiscoveryRequestData } from './discovery-request-data';
import { LocalVolumeDiscovery as AutoDetectVolumeModel } from '../../models';
import { initialState, reducer } from './state';
import { DISCOVERY_CR_NAME, LOCAL_STORAGE_NAMESPACE } from '../../constants';
import { fetchK8s } from '@console/internal/graphql/client';
import {
DISCOVERY_CR_NAME,
LOCAL_STORAGE_NAMESPACE,
HOSTNAME_LABEL_KEY,
AUTO_DISCOVER_ERR_MSG,
LABEL_OPERATOR,
} from '../../constants';

const AutoDetectVolume: React.FC = withHandlePromise<AutoDetectVolumeProps & HandlePromiseProps>(
(props) => {
Expand All @@ -32,31 +38,43 @@ const AutoDetectVolume: React.FC = withHandlePromise<AutoDetectVolumeProps & Han
handlePromise(
fetchK8s(AutoDetectVolumeModel, DISCOVERY_CR_NAME, LOCAL_STORAGE_NAMESPACE)
.then((discoveryRes: K8sResourceKind) => {
const nodes = new Set(
discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms?.[0]?.matchExpressions?.[0]?.values,
);
const selectedNodes = getNodes(
state.showNodesListOnADV,
state.allNodeNamesOnADV,
state.nodeNamesForLVS,
);
selectedNodes.forEach((name) => nodes.add(name));
const patch = [
{
op: 'replace',
path: `/spec/nodeSelector/nodeSelectorTerms/0/matchExpressions/0/values`,
value: Array.from(nodes),
},
];
return k8sPatch(AutoDetectVolumeModel, discoveryRes, patch);
const nodeSelectorTerms = discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms;
const [selectorIndex, expIndex] = nodeSelectorTerms
? getLabelIndex(nodeSelectorTerms, HOSTNAME_LABEL_KEY, LABEL_OPERATOR)
: [-1, -1];
if (selectorIndex !== -1 && expIndex !== -1) {
const nodes = new Set(
discoveryRes?.spec?.nodeSelector?.nodeSelectorTerms?.[
selectorIndex
]?.matchExpressions?.[expIndex]?.values,
);
const selectedNodes = getNodes(
state.showNodesListOnADV,
state.allNodeNamesOnADV,
state.nodeNamesForLVS,
);
selectedNodes.forEach((name) => nodes.add(name));
const patch = [
{
op: 'replace',
path: `/spec/nodeSelector/nodeSelectorTerms/${selectorIndex}/matchExpressions/${expIndex}/values`,
value: Array.from(nodes),
},
];
return k8sPatch(AutoDetectVolumeModel, discoveryRes, patch);
}
throw new Error(AUTO_DISCOVER_ERR_MSG);
})
.catch(() => {
.catch((err) => {
// handle AUTO_DISCOVER_ERR_MSG and throw to next catch block to show the message
if (err.message === AUTO_DISCOVER_ERR_MSG) {
throw err;
}
const requestData = getDiscoveryRequestData(state);

return k8sCreate(AutoDetectVolumeModel, requestData);
})
.then((resource) => history.push(resourceObjPath(resource, referenceFor(resource))))
.catch(() => null),
// eslint-disable-next-line promise/catch-or-return
.then((resource) => history.push(resourceObjPath(resource, referenceFor(resource)))),
);
};

Expand Down
@@ -0,0 +1,5 @@
import { MatchExpression } from '@console/internal/module/k8s';

export type NodeAffinityTerm = {
matchExpressions?: MatchExpression[];
};

0 comments on commit 72234eb

Please sign in to comment.