diff --git a/src/components/BackupLabelInput/BackupLabelInput.js b/src/components/BackupLabelInput/BackupLabelInput.js index c7cbe8946..2ebdfb1cf 100644 --- a/src/components/BackupLabelInput/BackupLabelInput.js +++ b/src/components/BackupLabelInput/BackupLabelInput.js @@ -37,64 +37,62 @@ class BackupLabelInput extends React.Component { render() { const { getFieldDecorator, getFieldValue } = this.props.form - const formItemLayoutWithOutLabel = { - wrapperCol: { - xs: { span: 24, offset: 0 }, - sm: { span: 16, offset: 4 }, - }, + const formItemLayout = { + labelCol: { span: 6 }, + wrapperCol: { span: 18 }, } getFieldDecorator('keys', { initialValue: [] }) const keys = getFieldValue('keys') const formItems = keys.map((k, index) => ( -
- - {getFieldDecorator(`key[${k}]`, { - validateTrigger: ['onChange', 'onBlur'], - rules: [ - { - required: true, - whitespace: true, - message: 'key is required', - }, - ], - })()} - - - {getFieldDecorator(`value[${k}]`, { - validateTrigger: ['onChange', 'onBlur'], - rules: [ - { - required: true, - whitespace: true, - message: 'value is required', - }, - ], - })()} - {keys.length > 0 ? ( - this.remove(k)} - />) : null} +
+ + {getFieldDecorator(`key[${k}]`, { + validateTrigger: ['onChange', 'onBlur'], + rules: [ + { + required: true, + whitespace: true, + message: 'key is required', + }, + ], + })()} + + + {getFieldDecorator(`value[${k}]`, { + validateTrigger: ['onChange', 'onBlur'], + rules: [ + { + required: true, + whitespace: true, + message: 'value is required', + }, + ], + })()} + {keys.length > 0 ? ( + this.remove(k)} + />) : null}
)) return (
- {formItems} - + + {formItems} ) } diff --git a/src/models/backupTarget.js b/src/models/backupTarget.js index 438ad98cf..2a8e2c7b5 100644 --- a/src/models/backupTarget.js +++ b/src/models/backupTarget.js @@ -1,6 +1,5 @@ import { queryBackupTarget, createBackupTarget, deleteBackupTarget, updateBackupTarget } from '../services/backupTarget' import { message } from 'antd' -// import { delay } from 'dva/saga' import { wsChanges, updateState } from '../utils/websocket' import queryString from 'query-string' import { enableQueryData } from '../utils/dataDependency' @@ -12,14 +11,6 @@ export default { data: [], resourceType: 'backupTarget', selectedRows: [], - // createBackingImageModalVisible: false, - // createBackingImageModalKey: Math.random(), - // diskStateMapDetailModalVisible: false, - // diskStateMapDetailModalKey: Math.random(), - // diskStateMapDeleteDisabled: true, - // diskStateMapDeleteLoading: false, - // selectedDiskStateMapRows: [], - // selectedDiskStateMapRowKeys: [], socketStatus: 'closed', }, subscriptions: { @@ -39,8 +30,6 @@ export default { payload, }, { call, put }) { const data = yield call(queryBackupTarget, payload) - console.log('🚀 ~ data:', data) - console.log('🚀 ~ backupTarget query data:', data) if (payload && payload.field && payload.keyword && data.data) { data.data = data.data.filter(item => item[payload.field] && item[payload.field].indexOf(payload.keyword.trim()) > -1) } @@ -54,7 +43,6 @@ export default { payload, }, { call, put }) { const resp = yield call(createBackupTarget, payload) - // console.log('🚀 ~ create resp:', resp) if (resp && resp.status === 200) { message.success(`Successfully create backup target ${payload.name}.`) } @@ -69,7 +57,6 @@ export default { *edit({ payload, }, { call, put }) { - // console.log('🚀 ~edit payload:', payload) yield call(updateBackupTarget, payload) yield put({ type: 'query' }) }, @@ -85,7 +72,6 @@ export default { payload, }, { select }) { let ws = yield select(state => state.backupTarget.ws) - console.log('🚀 ~ backupTarget startWS ws:', ws) if (ws) { ws.open() } else { @@ -118,38 +104,6 @@ export default { updateBackground(state, action) { return updateState(state, action) }, - // showCreateBackingImageModal(state, action) { - // return { ...state, ...action.payload, createBackingImageModalVisible: true, createBackingImageModalKey: Math.random() } - // }, - // hideCreateBackingImageModal(state) { - // return { ...state, createBackingImageModalVisible: false } - // }, - // showDiskStateMapDetailModal(state, action) { - // return { - // ...state, - // selected: action.payload.record, - // diskStateMapDetailModalVisible: true, - // diskStateMapDetailModalKey: Math.random(), - // } - // }, - // hideDiskStateMapDetailModal(state) { - // return { ...state, diskStateMapDetailModalVisible: false, diskStateMapDetailModalKey: Math.random() } - // }, - // disableDiskStateMapDelete(state) { - // return { ...state, diskStateMapDeleteDisabled: true } - // }, - // enableDiskStateMapDelete(state) { - // return { ...state, diskStateMapDeleteDisabled: false } - // }, - // disableDiskStateMapDeleteLoading(state) { - // return { ...state, diskStateMapDeleteLoading: false } - // }, - // enableDiskStateMapDeleteLoading(state) { - // return { ...state, diskStateMapDeleteLoading: true } - // }, - changeDiskStateMapSelection(state, action) { - return { ...state, ...action.payload } - }, updateSocketStatus(state, action) { return { ...state, socketStatus: action.payload } }, diff --git a/src/models/snapshot.js b/src/models/snapshot.js index a1d74c102..c26c6495b 100644 --- a/src/models/snapshot.js +++ b/src/models/snapshot.js @@ -204,9 +204,9 @@ export default (namespace) => { yield put({ type: 'setLoading', payload: true }) const snapshot = yield call(execAction, payload.snapshotCreateUrl, {}) if (Object.getOwnPropertyNames(payload.labels).length === 0) { - yield call(execAction, payload.snapshotBackupUrl, { name: snapshot.name }) + yield call(execAction, payload.snapshotBackupUrl, { name: snapshot.name, backupTargetName: payload.backupTargetName }) } else { - yield call(execAction, payload.snapshotBackupUrl, { name: snapshot.name, labels: payload.labels, backupTargetName: 'andy-minio-backupstore' }) + yield call(execAction, payload.snapshotBackupUrl, { name: snapshot.name, labels: payload.labels, backupTargetName: payload.backupTargetName }) } yield put({ type: 'querySnapShot', payload: { url: payload.querySnapShotUrl } }) yield put({ type: 'setLoading', payload: false }) @@ -216,9 +216,9 @@ export default (namespace) => { }, { call, put }) { yield put({ type: 'setLoading', payload: true }) if (Object.getOwnPropertyNames(payload.labels).length === 0) { - yield call(execAction, payload.snapshotBackupUrl, { name: payload.snapshotName }) + yield call(execAction, payload.snapshotBackupUrl, { name: payload.snapshotName, backupTargetName: payload.backupTargetName }) } else { - yield call(execAction, payload.snapshotBackupUrl, { name: payload.snapshotName, labels: payload.labels }) + yield call(execAction, payload.snapshotBackupUrl, { name: payload.snapshotName, labels: payload.labels, backupTargetName: payload.backupTargetName }) } yield put({ type: 'querySnapShot', payload: { url: payload.querySnapShotUrl } }) yield put({ type: 'setLoading', payload: false }) diff --git a/src/routes/backupTarget/BackupTargetList.js b/src/routes/backupTarget/BackupTargetList.js index 81f8a3d86..2463a7aa0 100644 --- a/src/routes/backupTarget/BackupTargetList.js +++ b/src/routes/backupTarget/BackupTargetList.js @@ -69,7 +69,7 @@ function list({ loading, dataSource, deleteBackupTarget, editBackupTarget, rowSe sorter: (a, b) => a.default - b.default, render: (text) => { return ( -
{text.toString().firstUpperCase()}
+
{text.toString().firstUpperCase()}
) }, }, { @@ -81,7 +81,7 @@ function list({ loading, dataSource, deleteBackupTarget, editBackupTarget, rowSe render: (text) => { return (
-
{text.toString().firstUpperCase()}
+
{text.toString().firstUpperCase()}
{text === false && ( diff --git a/src/routes/recurringJob/CreateRecurringJob.js b/src/routes/recurringJob/CreateRecurringJob.js index 424a20673..a9e16e172 100644 --- a/src/routes/recurringJob/CreateRecurringJob.js +++ b/src/routes/recurringJob/CreateRecurringJob.js @@ -46,6 +46,7 @@ const noRetain = (val) => { const modal = ({ item, + availBackupTargets, visible, isEdit, onCancel, @@ -107,7 +108,6 @@ const modal = ({ delete data.keysForlabels } delete data.defaultGroup - onOk(data) }) } @@ -174,6 +174,10 @@ const modal = ({ return getFieldValue('task') === 'backup' || getFieldValue('task') === 'snapshot' } + const showBackupTargetDropdown = () => { + return getFieldValue('task') === 'backup' + } + // init params getFieldDecorator('keys', { initialValue: isEdit && item.groups && item.groups.length > 0 ? item.groups.map((group, index) => { return { initialValue: group, index } }) : [{ index: 0, initialValue: '' }] }) getFieldDecorator('keysForlabels', { initialValue: isEdit && item.labels ? Object.keys(item.labels).map((_, index) => index) : [0] }) @@ -310,6 +314,20 @@ const modal = ({ }
+
+ {showBackupTargetDropdown() + && + {getFieldDecorator('backupTargetName', { + // eslint-disable-next-line no-nested-ternary + initialValue: isEdit ? item.backupTarget : availBackupTargets.length > 0 ? availBackupTargets[0].name : '', + })( + + )} + + } +
{getFieldDecorator('retain', { initialValue: isEdit ? item.retain : 1, @@ -373,6 +391,7 @@ const modal = ({ modal.propTypes = { form: PropTypes.object.isRequired, + availBackupTargets: PropTypes.array, visible: PropTypes.bool, onCancel: PropTypes.func, item: PropTypes.object, diff --git a/src/routes/recurringJob/index.js b/src/routes/recurringJob/index.js index 28bb3a20a..d179b18c1 100644 --- a/src/routes/recurringJob/index.js +++ b/src/routes/recurringJob/index.js @@ -91,7 +91,8 @@ class RecurringJob extends React.Component { render() { const me = this - const { dispatch, loading, location } = this.props + const { dispatch, loading, location, backupTarget } = this.props + const availAndWritableBackupTarget = backupTarget.data.filter((item) => item.available && !item.readOnly) const { data } = this.props.recurringJob const { field, value } = queryString.parse(this.props.location.search) // Front-end filtering @@ -125,6 +126,7 @@ class RecurringJob extends React.Component { item: this.state.selected, visible: this.state.createRecurringJobModalVisible, isEdit: this.state.isEdit, + availBackupTargets: availAndWritableBackupTarget, onOk(newRecurringJob) { me.setState({ ...me.state, @@ -265,9 +267,10 @@ class RecurringJob extends React.Component { RecurringJob.propTypes = { recurringJob: PropTypes.object, + backupTarget: PropTypes.object, loading: PropTypes.bool, location: PropTypes.object, dispatch: PropTypes.func, } -export default connect(({ recurringJob, loading }) => ({ recurringJob, loading: loading.models.recurringJob }))(RecurringJob) +export default connect(({ recurringJob, backupTarget, loading }) => ({ recurringJob, backupTarget, loading: loading.models.recurringJob }))(RecurringJob) diff --git a/src/routes/volume/detail/CreateBackupModal.js b/src/routes/volume/detail/CreateBackupModal.js index 34f4b27bb..fcbbf92b8 100644 --- a/src/routes/volume/detail/CreateBackupModal.js +++ b/src/routes/volume/detail/CreateBackupModal.js @@ -1,13 +1,31 @@ import React from 'react' import PropTypes from 'prop-types' -import { Form, Icon } from 'antd' +import { Form, Select, Alert } from 'antd' import { ModalBlur } from '../../../components' import { BackupLabelInput } from '../../../components' +const FormItem = Form.Item +const Option = Select.Option + +const formItemLayout = { + labelCol: { span: 6 }, + wrapperCol: { span: 18 }, +} +const getLabels = (getFieldsValue) => { + const labels = {} + if (getFieldsValue().keys && getFieldsValue().key && getFieldsValue().value) { + getFieldsValue().keys.forEach((item) => { + labels[getFieldsValue().key[item]] = getFieldsValue().value[item] + }) + } + return labels +} + const modal = ({ visible, onCancel, onOk, + availBackupTargets, form: { getFieldDecorator, validateFields, @@ -18,14 +36,12 @@ const modal = ({ }) => { function handleOk() { validateFields((errors) => { - if (errors) { - return - } - let data = {} - if (getFieldsValue().keys && getFieldsValue().key && getFieldsValue().value) { - getFieldsValue().keys.forEach((item) => { - data[getFieldsValue().key[item]] = getFieldsValue().value[item] - }) + if (errors) return + + const labels = getLabels(getFieldsValue) + const data = { + labels, + backupTargetName: getFieldValue('backupTargetName'), } onOk(data) }) @@ -46,15 +62,31 @@ const modal = ({ setFieldsValue, } + const onChangeTask = () => { + + } + return ( -

This could take a while depending on the actual size of the volume and network bandwidth.

- + +
+ + {getFieldDecorator('backupTargetName', { + initialValue: availBackupTargets.length > 0 ? availBackupTargets[0].name : '', + })( + + )} + + +
) } modal.propTypes = { + availBackupTargets: PropTypes.array, form: PropTypes.object.isRequired, visible: PropTypes.bool, onCancel: PropTypes.func, diff --git a/src/routes/volume/detail/Snapshots.js b/src/routes/volume/detail/Snapshots.js index fb39d3709..bed6066ee 100644 --- a/src/routes/volume/detail/Snapshots.js +++ b/src/routes/volume/detail/Snapshots.js @@ -11,13 +11,14 @@ class Snapshots extends React.Component { super(props) this.state = { createBackModalKey: Math.random(), - createBackBySnapsotModalKey: Math.random(), - createBackModalVisible: false, - createBackBySnapsotModalVisible: false, + createBackBySnapshotModalKey: Math.random(), + createBackModalVisible: false, // click create backup button + createBackBySnapshotModalVisible: false, // click backup button in snapshot dropdown currentSnapshotName: '', snapshotBackupUrl: '', snapshotListUrl: '', } + this.onAction = (action) => { if (action.type === 'backup') { this.setState({ @@ -37,7 +38,7 @@ class Snapshots extends React.Component { if (action.type === 'snapshotBackup') { this.setState({ ...this.state, - createBackBySnapsotModalVisible: true, + createBackBySnapshotModalVisible: true, currentSnapshotName: action.payload && action.payload.snapshot && action.payload.snapshot.name ? action.payload.snapshot.name : '', snapshotBackupUrl: action.payload && action.payload.volume && action.payload.volume.actions && action.payload.volume.actions.snapshotBackup ? action.payload.volume.actions.snapshotBackup : '', snapshotListUrl: action.payload && action.payload.volume && action.payload.volume.actions && action.payload.volume.actions.snapshotList ? action.payload.volume.actions.snapshotList : '', @@ -139,15 +140,16 @@ class Snapshots extends React.Component { item: { frontend: 'iscsi', }, + availBackupTargets: me.props.availBackupTargets, visible: me.state.createBackModalVisible, - onOk(data) { + onOk(params) { me.props.dispatch({ type: 'snapshotModal/backup', payload: { snapshotCreateUrl: me.props.volume.actions.snapshotCreate, snapshotBackupUrl: me.props.volume.actions.snapshotBackup, querySnapShotUrl: me.props.volume.actions.snapshotList, - labels: data, + ...params, }, }) me.setState({ @@ -166,14 +168,15 @@ class Snapshots extends React.Component { } } - createBackupBySnapsotModal = () => { + createBackupBySnapshotModal = () => { let me = this return { item: { frontend: 'iscsi', }, - visible: me.state.createBackBySnapsotModalVisible, - onOk(data) { + availBackupTargets: me.props.availBackupTargets, + visible: me.state.createBackBySnapshotModalVisible, + onOk(params) { if (me.state.snapshotBackupUrl && me.state.currentSnapshotName && me.state.snapshotListUrl) { me.props.dispatch({ type: 'snapshotModal/createBackupBySnapshot', @@ -181,13 +184,13 @@ class Snapshots extends React.Component { snapshotBackupUrl: me.state.snapshotBackupUrl, snapshotName: me.state.currentSnapshotName, querySnapShotUrl: me.state.snapshotListUrl, - labels: data, + ...params, }, }) me.setState({ ...me.state, - createBackBySnapsotModalKey: Math.random(), - createBackBySnapsotModalVisible: false, + createBackBySnapshotModalKey: Math.random(), + createBackBySnapshotModalVisible: false, currentSnapshotName: '', snapshotBackupUrl: '', }) @@ -196,8 +199,8 @@ class Snapshots extends React.Component { onCancel() { me.setState({ ...me.state, - createBackBySnapsotModalKey: Math.random(), - createBackBySnapsotModalVisible: false, + createBackBySnapshotModalKey: Math.random(), + createBackBySnapshotModalVisible: false, currentSnapshotName: '', snapshotBackupUrl: '', }) @@ -210,6 +213,8 @@ class Snapshots extends React.Component { return null } + // console.log('🚀 ~ Snapshots ~ render ~ createBackModalVisible:', this.state.createBackModalVisible) + // console.log('🚀 ~ Snapshots ~ render ~ createBackBySnapshotModalVisible:', this.state.createBackBySnapshotModalVisible) const isRestoring = () => { if (this.props.volume.restoreStatus && this.props.volume.restoreStatus.length > 0) { let flag = this.props.volume.restoreStatus.every((item) => { @@ -308,7 +313,7 @@ class Snapshots extends React.Component { Show System Hidden:   { this.onAction({ type: 'toggleShowRemoved' }) }} checked={this.props.showRemoved} />
{this.state.createBackModalVisible ? : ''} - {this.state.createBackBySnapsotModalVisible ? : ''} + {this.state.createBackBySnapshotModalVisible ? : ''} ) } @@ -326,6 +331,7 @@ Snapshots.propTypes = { backupTargetAvailable: PropTypes.bool, backupTargetMessage: PropTypes.string, volumeHead: PropTypes.object, + availBackupTargets: PropTypes.array, } export default Snapshots diff --git a/src/routes/volume/detail/index.js b/src/routes/volume/detail/index.js index 5b7cd2988..e45ecfc2e 100644 --- a/src/routes/volume/detail/index.js +++ b/src/routes/volume/detail/index.js @@ -47,7 +47,7 @@ import { const confirm = Modal.confirm -function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, eventlog, host, volume, volumeId, setting, loading, backingImage, recurringJob }) { +function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, backupTarget, eventlog, host, volume, volumeId, setting, loading, backingImage, recurringJob }) { const { data, attachHostModalVisible, @@ -96,6 +96,7 @@ function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, eventlog, const engineImages = engineimage.data const selectedVolume = data.find(item => item.id === volumeId) const currentBackingImage = selectedVolume && selectedVolume.backingImage && backingImage.data ? backingImage.data.find(item => item.name === selectedVolume.backingImage) : null + const availBackupTargets = backupTarget.data.filter(item => item.available && !item.readOnly) const settings = setting.data const defaultDataLocalitySetting = settings.find(s => s.id === 'default-data-locality') const defaultSnapshotDataIntegritySetting = settings.find(s => s.id === 'snapshot-data-integrity') @@ -494,6 +495,7 @@ function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, eventlog, volume: selectedVolume, volumeId, dispatch, + availBackupTargets, backupTargetAvailable, backupTargetMessage, volumeHead: snapshotData.find(d => d.name === 'volume-head'), @@ -684,6 +686,7 @@ VolumeDetail.propTypes = { host: PropTypes.object, engineimage: PropTypes.object, volumeId: PropTypes.string, + backupTarget: PropTypes.object, loading: PropTypes.bool, snapshotModal: PropTypes.object, eventlog: PropTypes.object, @@ -692,4 +695,4 @@ VolumeDetail.propTypes = { recurringJob: PropTypes.object, } -export default connect(({ snapshotModal, backup, host, engineimage, volume, loading, eventlog, setting, backingImage, recurringJob }, { match }) => ({ snapshotModal, backup, host, volume, engineimage, loading: loading.models.volume, volumeId: match.params.id, eventlog, setting, backingImage, recurringJob }))(VolumeDetail) +export default connect(({ snapshotModal, backup, host, backupTarget, engineimage, volume, loading, eventlog, setting, backingImage, recurringJob }, { match }) => ({ snapshotModal, backup, host, volume, backupTarget, engineimage, loading: loading.models.volume, volumeId: match.params.id, eventlog, setting, backingImage, recurringJob }))(VolumeDetail) diff --git a/src/services/recurringJob.js b/src/services/recurringJob.js index 51dd3fe6d..b93801f61 100644 --- a/src/services/recurringJob.js +++ b/src/services/recurringJob.js @@ -14,7 +14,6 @@ export async function create(params) { method: 'post', data: { ...params, - backupTargetName: 'default', }, }) } diff --git a/src/utils/dataDependency.js b/src/utils/dataDependency.js index eecefdec1..5ca5875bb 100644 --- a/src/utils/dataDependency.js +++ b/src/utils/dataDependency.js @@ -45,6 +45,9 @@ const dependency = { }, { ns: 'recurringJob', key: 'recurringjobs', + }, { + ns: 'backupTarget', + key: 'backuptargets', }], }, engineimage: { @@ -59,6 +62,9 @@ const dependency = { runWs: [{ ns: 'recurringJob', key: 'recurringjobs', + }, { + ns: 'backupTarget', + key: 'backuptargets', }], }, backupTarget: { @@ -164,9 +170,9 @@ const list = [{ const httpDataDependency = { '/dashboard': ['volume', 'host', 'eventlog'], '/node': ['volume', 'host', 'setting'], - '/volume': ['volume', 'host', 'setting', 'backingImage', 'engineimage', 'recurringJob', 'backup'], + '/volume': ['volume', 'host', 'setting', 'backupTarget', 'backingImage', 'engineimage', 'recurringJob', 'backup'], '/engineimage': ['engineimage'], - '/recurringJob': ['recurringJob'], + '/recurringJob': ['recurringJob', 'backupTarget'], '/backingImage': ['volume', 'backingImage'], '/backupTarget': ['backupTarget'], '/setting': ['setting'],