Skip to content

Commit

Permalink
Adds Local Volume Set Creation Form and enables LSO Plugin
Browse files Browse the repository at this point in the history
- Local Volume Set is the new CR to be introduced in Local Stoarge Operator.
- Local Volume Set will allow to filter a set of storage volumes, group them and create a dedicated storage class to consume storage for them.
  • Loading branch information
Afreen Rahman committed Apr 13, 2020
1 parent 70ae2b7 commit 019839b
Show file tree
Hide file tree
Showing 11 changed files with 552 additions and 10 deletions.
1 change: 1 addition & 0 deletions frontend/packages/console-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@console/internal": "0.0.0-fixed",
"@console/knative-plugin": "0.0.0-fixed",
"@console/kubevirt-plugin": "0.0.0-fixed",
"@console/local-storage-operator-plugin": "0.0.0-fixed",
"@console/metal3-plugin": "0.0.0-fixed",
"@console/network-attachment-definition-plugin": "0.0.0-fixed",
"@console/noobaa-storage-plugin": "0.0.0-fixed",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.lso-create-lvs__all-nodes-radio--padding {
padding-bottom: var(--pf-global--spacer--sm);
}

.lso-create-lvs__filter-volumes-text--margin {
margin: 0;
}

.lso-create-lvs__max-volume-limit-help-text--margin {
margin-top: 0;
}

.lso-create-lvs__node-selection-table--margin {
margin-top: 0;
}

.lso-create-lvs__volume-size-form-group--margin {
margin: var(--pf-global--spacer--lg) 0;
}

.lso-create-lvs__volume-size-form-group-div {
display: flex;
align-items: flex-end;
justify-content: space-between;
width: 22em;
}

.lso-create-lvs__volume-size-form-group-max-min-input {
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
import * as React from 'react';
import { match as RouterMatch } from 'react-router';
import {
ActionGroup,
Button,
Form,
FormGroup,
TextInput,
Radio,
Expandable,
TextInputTypes,
Text,
TextVariants,
} from '@patternfly/react-core';
import {
resourcePathFromModel,
BreadCrumbs,
Dropdown,
resourceObjPath,
withHandlePromise,
HandlePromiseProps,
ButtonBar,
} from '@console/internal/components/utils';
import { history } from '@console/internal/components/utils/router';
import { ListPage } from '@console/internal/components/factory';
import { k8sCreate, referenceFor } from '@console/internal/module/k8s';
import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager';
import { LocalVolumeSetModel, LocalVolumeModel } from '../../models';
import { NodesSelectionList } from './nodes-selection-list';
import { RowUIDMap, LocalVolumeSetKind } from './types';
import { getSelectedNodeUIDs } from './utils';
import './create-local-volume-set.scss';

const volumeTypeOptions = Object.freeze({
'SSD/NVMe': 'SSD/NVMe',
HDD: 'HDD',
});
const volumeModeOptions = Object.freeze({
Block: 'Block',
Filesystem: 'Filesystem',
});
const volumeSizeUnitOptions = Object.freeze({
TiB: 'TiB',
GiB: 'GiB',
});

const CreateLocalVolumeSet: React.FC = withHandlePromise((props: CreateLocalVolumeSetProps) => {
const { match, handlePromise, inProgress, errorMessage } = props;
const [volumeSetName, setVolumeSetName] = React.useState('');
const [storageClassName, setStorageClassName] = React.useState('');
const [showNodesList, setShowNodesList] = React.useState(false);
const [volumeType, setVolumeType] = React.useState(volumeTypeOptions['SSD/NVMe']);
const [volumeMode, setVolumeMode] = React.useState(volumeModeOptions.Block);
const [minVolumeSize, setMinVolumeSize] = React.useState('0');
const [maxVolumeSize, setMaxVolumeSize] = React.useState('');
const [volumeSizeUnit, setVolumeSizeUnit] = React.useState(volumeSizeUnitOptions.TiB);
const [maxVolumeLimit, setMaxVolumeLimit] = React.useState('');
const [rows, setRows] = React.useState<RowUIDMap>({});
const [allSelected, setAllSelected] = React.useState(null);

const { ns, appName } = match.params;
const modelName = LocalVolumeSetModel.label;

const toggleShowNodesList = () => {
setShowNodesList(!showNodesList);
};

const onSubmit = (event: React.FormEvent<EventTarget>) => {
event.preventDefault();
const requestData: LocalVolumeSetKind = {
apiVersion: LocalVolumeSetModel.apiVersion,
kind: LocalVolumeSetModel.kind,
metadata: { name: volumeSetName },
spec: {
storageClassName,
volumeMode,
},
// @TODO: more fields will be added
};
if (showNodesList) {
const selectedNodesUID = getSelectedNodeUIDs(rows);
const selectedNodes = selectedNodesUID.map((uid) => rows[uid].props.data.metadata.name);
requestData.spec.nodeSelector = {
nodeSelectorTerms: [
{
matchExpressions: [
{ key: 'kubernetes.io/hostname', operator: 'In', values: [...selectedNodes] },
],
},
],
};
}
handlePromise(k8sCreate(LocalVolumeModel, requestData))
.then((resource) => {
history.push(resourceObjPath(resource, referenceFor(resource)));
})
.catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
};

const onCancel = () => {
history.goBack();
};

return (
<>
<div className="co-create-operand__header">
<div className="co-create-operand__header-buttons">
<BreadCrumbs
breadcrumbs={[
{
name: 'Local Storage',
path: resourcePathFromModel(ClusterServiceVersionModel, appName, ns),
},
{ name: `Create ${modelName}`, path: '' },
]}
/>
</div>
<h1 className="co-create-operand__header-text">{`Create ${modelName}`}</h1>
<p className="help-block">
A {modelName} allows you to filter a set of storage volumes, group them and create a
dedicated storage class to consume storage for them.
</p>
</div>
<Form className="co-m-pane__body co-m-pane__form" onSubmit={onSubmit}>
<FormGroup label="Volume Set Name" isRequired fieldId="create-lvs--volume-set-name">
<TextInput
type={TextInputTypes.text}
id="create-lvs--volume-set-name"
value={volumeSetName}
onChange={setVolumeSetName}
isRequired
/>
</FormGroup>
<FormGroup label="Storage Class Name" fieldId="create-lvs--storage-class-name">
<TextInput
type={TextInputTypes.text}
id="create-lvs--storage-class-name"
value={storageClassName}
onChange={setStorageClassName}
/>
</FormGroup>
<Text component={TextVariants.h3} className="lso-create-lvs__filter-volumes-text--margin">
Filter Volumes
</Text>
<FormGroup label="Node Selector" fieldId="create-lvs--radio-group-node-selector">
<div id="create-lvs--radio-group-node-selector">
<Radio
label="All nodes"
name="nodes-selection"
id="create-lvs--radio-all-nodes"
className="lso-create-lvs__all-nodes-radio--padding"
value="allNodes"
onChange={toggleShowNodesList}
description="Selecting all nodes will search for available volume storage on all nodes."
defaultChecked
/>
<Radio
label="Select nodes"
name="nodes-selection"
id="create-lvs--radio-select-nodes"
value="selectedNodes"
onChange={toggleShowNodesList}
description="Selecting nodes allow you to limit the search for available volumes to specific nodes."
/>
</div>
</FormGroup>
{showNodesList && (
<ListPage
customData={{ rows, setRows, allSelected, setAllSelected }}
showTitle={false}
kind="Node"
ListComponent={NodesSelectionList}
/>
)}
<FormGroup label="Volume Type" fieldId="create-lvs--volume-type-dropdown">
<Dropdown
id="create-lvs--volume-type-dropdown"
dropDownClassName="dropdown--full-width"
items={volumeTypeOptions}
title={volumeType}
selectedKey={volumeType}
onChange={setVolumeType}
/>
</FormGroup>
<Expandable toggleText="Advanced">
<FormGroup label="Volume Mode" fieldId="create--lso-volume-mode-dropdown">
<Dropdown
id="create-lso--volume-mode-dropdown"
dropDownClassName="dropdown--full-width"
items={volumeModeOptions}
title={volumeMode}
selectedKey={volumeMode}
onChange={setVolumeMode}
/>
</FormGroup>
<FormGroup
label="Volume Size"
fieldId="create-lvs--volume-size"
className="lso-create-lvs__volume-size-form-group--margin"
>
<div
id="create-lvs--volume-size"
className="lso-create-lvs__volume-size-form-group-div"
>
<FormGroup
label="Min"
fieldId="create-lvs--min-volume-size"
className="lso-create-lvs__volume-size-form-group-max-min-input"
>
<TextInput
type={TextInputTypes.number}
id="create-lvs--min-volume-size"
value={minVolumeSize}
onChange={setMinVolumeSize}
/>
</FormGroup>
<div>-</div>
<FormGroup
label="Max"
fieldId="create-lvs--max-volume-size"
className="lso-create-lvs__volume-size-form-group-max-min-input"
>
<TextInput
type={TextInputTypes.number}
id="create-lvs--max-volume-size"
value={maxVolumeSize}
onChange={setMaxVolumeSize}
/>
</FormGroup>
<Dropdown
id="create-lvs--volume-size-unit-dropdown"
items={volumeSizeUnitOptions}
title={volumeSizeUnit}
selectedKey={volumeSizeUnit}
onChange={setVolumeSizeUnit}
/>
</div>
</FormGroup>
<FormGroup label="Max Volume Limit" fieldId="create-lvs--max-volume-limit">
<p className="help-block lso-create-lvs__max-volume-limit-help-text--margin">
Volume limit will set the maximum number of PVs to create on a node. If the field is
empty, will create PVs for all available volumes on the matching nodes.
</p>
<TextInput
type={TextInputTypes.number}
id="create-lvs--max-volume-limit"
value={maxVolumeLimit}
onChange={setMaxVolumeLimit}
/>
</FormGroup>
</Expandable>
<ButtonBar errorMessage={errorMessage} inProgress={inProgress}>
<ActionGroup>
<Button type="submit" variant="primary">
Create
</Button>
<Button type="button" variant="secondary" onClick={onCancel}>
Cancel
</Button>
</ActionGroup>
</ButtonBar>
</Form>
</>
);
});

type CreateLocalVolumeSetProps = {
match: RouterMatch<{ appName: string; ns: string }>;
} & HandlePromiseProps;

export default CreateLocalVolumeSet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.lso-node-selection-table__table--scroll {
max-height: 30rem;
overflow-y: auto;
}

0 comments on commit 019839b

Please sign in to comment.