Skip to content

Commit

Permalink
query vol snapshot only when user choose data source volume
Browse files Browse the repository at this point in the history
Signed-off-by: andy.lee <andy.lee@suse.com>
  • Loading branch information
a110605 committed Jul 2, 2024
1 parent a85cde3 commit b9d1f54
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 85 deletions.
46 changes: 21 additions & 25 deletions src/models/volume.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { routerRedux } from 'dva/router'
import { getSorter, saveSorter } from '../utils/store'
import queryString from 'query-string'
import { enableQueryData } from '../utils/dataDependency'
import { sortSnapshots } from '../utils/sort'

export default {
namespace: 'volume',
Expand All @@ -16,8 +15,7 @@ export default {
data: [],
resourceType: 'volume',
cloneVolumeType: 'volume', // volume or snapshot
snapshotsOptions: {},
snapshotLoading: true,
snapshotsOptions: [],
selected: null,
selectSnapshot: null,
selectedRows: [],
Expand Down Expand Up @@ -254,34 +252,32 @@ export default {
yield put({ type: 'changeTagsLoading', payload: { tagsLoading: false } })
}
},
*showCreateVolumeModalBefore({
payload,
}, { call, put, all }) {
*showCreateVolumeModalBefore({ payload }, { call, put }) {
yield put({ type: 'showCreateVolumeModal' })
// TODO: longhorn manager should have an API to get all volume's snapshots or add snapshotList array in GET /v1/volumes
const snapshotListRequests = payload.filter(item => item.actions.snapshotList).map(item => call(execAction, item.actions.snapshotList))
const snapshotResp = yield all(snapshotListRequests)
if (snapshotResp && snapshotResp.length > 0 && snapshotResp.every(resp => resp.status === 200)) {
// construct snapshots data by volume name
const snapshotsOptions = {}
for (const resp of snapshotResp) {
if (resp?.links?.self) {
const vol = resp?.links.self.split('/').pop()
const snapshots = resp.data.filter(d => d.name !== 'volume-head') // no include volume-head
sortSnapshots(snapshots)
snapshotsOptions[vol] = snapshots
}
}
yield put({ type: 'setSnapshotsData', payload: { snapshotsOptions, snapshotLoading: false } })
}
const nodeTags = yield call(getNodeTags)
const diskTags = yield call(getDiskTags)
const nodeTags = yield call(getNodeTags, payload)
const diskTags = yield call(getDiskTags, payload)
if (nodeTags.status === 200 && diskTags.status === 200) {
yield put({ type: 'changeTagsLoading', payload: { nodeTags: nodeTags.data, diskTags: diskTags.data, tagsLoading: false } })
} else {
yield put({ type: 'changeTagsLoading', payload: { tagsLoading: false } })
}
},
*getSingleVolumeSnapshots({
payload,
}, { call, put }) {
const url = payload.actions.snapshotList
if (!url) {
yield put({ type: 'setSnapshotsData', payload: { snapshotsOptions: [] } })
return
}
const resp = yield call(execAction, url)
if (resp?.status === 200 && resp.data) {
yield put({ type: 'setSnapshotsData', payload: { snapshotsOptions: resp.data } })
} else {
message.error(`Failed to get ${payload.name} snapshots`, 5)
yield put({ type: 'setSnapshotsData', payload: { snapshotsOptions: [] } })
}
},
*delete({
payload,
}, { call, put }) {
Expand Down Expand Up @@ -796,7 +792,7 @@ export default {
return { ...state, createPVAndPVCVisible: false, nameSpaceDisabled: false, previousChecked: false, createPVAndPVCModalKey: Math.random() }
},
hideCreateVolumeModal(state) {
return { ...state, createVolumeModalVisible: false, tagsLoading: true }
return { ...state, createVolumeModalVisible: false, tagsLoading: true, snapshotsOptions: [] }
},
showExpansionVolumeSizeModal(state, action) {
return { ...state, selected: action.payload, expansionVolumeSizeModalVisible: true, expansionVolumeSizeModalKey: Math.random() }
Expand Down
128 changes: 70 additions & 58 deletions src/routes/volume/CreateVolume.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ModalBlur } from '../../components'
import { frontends } from './helper/index'
import { formatSize } from '../../utils/formatter'
import { formatDate } from '../../utils/formatDate'
import { sortSnapshots } from '../../utils/sort'

const FormItem = Form.Item
const { Panel } = Collapse
Expand Down Expand Up @@ -95,11 +96,10 @@ const genOkData = (getFieldsValue, getFieldValue, volumeOptions) => {
return data
}


const modal = ({
item,
volumeOptions = [],
snapshotsOptions = {},
snapshotsOptions = [],
visible,
onCancel,
onOk,
Expand All @@ -111,9 +111,9 @@ const modal = ({
diskTags,
backingImageOptions,
tagsLoading,
snapshotLoading,
v1DataEngineEnabled,
v2DataEngineEnabled,
getSnapshot,
form: {
getFieldDecorator,
validateFields,
Expand Down Expand Up @@ -164,18 +164,32 @@ const modal = ({
}
}

const handleDataSourceTypeChange = () => {
setFieldsValue({
...getFieldsValue(),
dataSourceVolume: '',
})
}

const handleDataSourceVolumeChange = (value) => {
const dataSourceVol = volumeOptions.find(vol => vol.name === value)
if (dataSourceVol) {
// set size field according to the selected data source
if (getFieldValue('dataSourceType') === dataSourceOptions[1] && dataSourceVol) {
getSnapshot(dataSourceVol)
setFieldsValue({
...getFieldsValue(),
size: formatSize(dataSourceVol), // set size field according to the selected data source
dataSourceSnapshot: '',
})
} else {
setFieldsValue({
...getFieldsValue(),
size: formatSize(dataSourceVol),
})
}
}

const targetVolumeSnaps = snapshotsOptions[getFieldValue('dataSourceVolume')] || []
const volumeSnapshots = snapshotsOptions?.length > 0 ? snapshotsOptions.filter(d => d.name !== 'volume-head') : []// no include volume-head
sortSnapshots(volumeSnapshots)
const dataSourceAlertMsg = 'The volume size is set to the selected volume size. Mismatched size will cause create volume failed.'
return (
<ModalBlur {...modalOpts}>
Expand Down Expand Up @@ -245,7 +259,7 @@ const modal = ({
message: 'Please input the number of replicas',
},
{
validator: (rule, value, callback) => {
validator: (_rule, value, callback) => {
if (value === '' || typeof value !== 'number') {
callback()
return
Expand Down Expand Up @@ -297,58 +311,56 @@ const modal = ({
{ backingImageOptions.map(backingImage => <Option key={backingImage.name} value={backingImage.name}>{backingImage.name}</Option>) }
</Select>)}
</FormItem>
<Spin spinning={snapshotLoading}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<FormItem label={
<span>
Data Source
<span style={{
marginLeft: 4,
marginRight: 4,
}}>
<Tooltip
overlayStyle={{ width: 450 }}
title="Choose data source from existing volume or snapshot. Longhorn will clone the volume data from selected data source"
>
<Icon type="question-circle-o" />
</Tooltip>
</span>
</span>}
hasFeedback
{...formItemLayout}>
{getFieldDecorator('dataSourceType', { initialValue: '' })(
<Select allowClear>
{dataSourceOptions.map(value => <Option key={value} value={value}>{value}</Option>) }
</Select>
)}
</FormItem>
{getFieldValue('dataSourceType') && (<Popover placement="right"
visible={displayDataSourceAlert()}
content={
<div style={{ maxWidth: 300 }}>
<Alert message={dataSourceAlertMsg} type="warning" />
</div>
}
>
<FormItem label="Volume" hasFeedback {...formItemLayout}>
{getFieldDecorator('dataSourceVolume', { initialValue: '' })(
<Select allowClear onChange={handleDataSourceVolumeChange}>
{volumeOptions.map(vol => <Option key={vol.name} value={vol.name}>{vol.name}</Option>) }
</Select>
)}
</FormItem>
</Popover>
)}
</div>
{getFieldValue('dataSourceType') === dataSourceOptions[1] && <FormItem label="Snapshot" hasFeedback {...formItemLayout}>
{getFieldDecorator('dataSourceSnapshot', { initialValue: '' })(
<Select allowClear optionLabelProp="label" dropdownMenuStyle={{ width: 500 }}>
{targetVolumeSnaps.map(snap => <Option key={snap.name} value={snap.name} label={snap.name}>{`${snap.name} (created ${formatDate(snap.created, false)})`}</Option>)}
<div style={{ display: 'flex', flexDirection: 'column' }}>
<FormItem label={
<span>
Data Source
<span style={{
marginLeft: 4,
marginRight: 4,
}}>
<Tooltip
overlayStyle={{ width: 450 }}
title="Choose data source from existing volume or snapshot. Longhorn will clone the volume data from selected data source"
>
<Icon type="question-circle-o" />
</Tooltip>
</span>
</span>}
hasFeedback
{...formItemLayout}>
{getFieldDecorator('dataSourceType', { initialValue: '' })(
<Select allowClear onChange={handleDataSourceTypeChange}>
{dataSourceOptions.map(value => <Option key={value} value={value}>{value}</Option>) }
</Select>
)}
</FormItem>
{getFieldValue('dataSourceType') && (<Popover placement="right"
visible={displayDataSourceAlert()}
content={
<div style={{ maxWidth: 300 }}>
<Alert message={dataSourceAlertMsg} type="warning" />
</div>
}
>
<FormItem label="Volume" hasFeedback {...formItemLayout}>
{getFieldDecorator('dataSourceVolume', { initialValue: '' })(
<Select allowClear onChange={handleDataSourceVolumeChange}>
{volumeOptions.map(vol => <Option key={vol.name} value={vol.name}>{vol.name}</Option>) }
</Select>
)}
</FormItem>
}
</Spin>
</Popover>
)}
</div>
{getFieldValue('dataSourceType') === dataSourceOptions[1] && <FormItem label="Snapshot" hasFeedback {...formItemLayout}>
{getFieldDecorator('dataSourceSnapshot', { initialValue: '' })(
<Select allowClear optionLabelProp="label" dropdownMenuStyle={{ width: `${volumeSnapshots.length > 0 ? 'fit-content' : 'auto'}` }}>
{volumeSnapshots.map(snap => <Option key={snap.name} value={snap.name} label={snap.name}>{`${snap.name} (created ${formatDate(snap.created, false)})`}</Option>)}
</Select>
)}
</FormItem>
}
<FormItem label="Data Engine" hasFeedback {...formItemLayout}>
{getFieldDecorator('dataEngine', {
initialValue: item.dataEngine || 'v1',
Expand Down Expand Up @@ -541,17 +553,17 @@ const modal = ({
modal.propTypes = {
form: PropTypes.object.isRequired,
volumeOptions: PropTypes.array,
snapshotsOptions: PropTypes.object,
snapshotsOptions: PropTypes.array,
visible: PropTypes.bool,
onCancel: PropTypes.func,
item: PropTypes.object,
onOk: PropTypes.func,
getSnapshot: PropTypes.func,
nodeTags: PropTypes.array,
diskTags: PropTypes.array,
defaultDataLocalityOption: PropTypes.array,
defaultSnapshotDataIntegrityOption: PropTypes.array,
tagsLoading: PropTypes.bool,
snapshotLoading: PropTypes.bool,
defaultDataLocalityValue: PropTypes.string,
defaultRevisionCounterValue: PropTypes.bool,
v1DataEngineEnabled: PropTypes.bool,
Expand Down
8 changes: 6 additions & 2 deletions src/routes/volume/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ class Volume extends React.Component {
const {
selected,
snapshotsOptions,
snapshotLoading,
cloneVolumeType,
selectedRows,
data,
Expand Down Expand Up @@ -802,10 +801,15 @@ class Volume extends React.Component {
v2DataEngineEnabled,
diskTags,
backingImageOptions,
snapshotLoading,
tagsLoading,
hosts,
visible: createVolumeModalVisible,
getSnapshot: (volume) => {
dispatch({
type: 'volume/getSingleVolumeSnapshots',
payload: volume,
})
},
onOk(newVolume) {
dispatch({
type: 'volume/create',
Expand Down

0 comments on commit b9d1f54

Please sign in to comment.