Skip to content

Commit

Permalink
Allow user to choose backup target when backup backing image
Browse files Browse the repository at this point in the history
Signed-off-by: andy.lee <[email protected]>
  • Loading branch information
a110605 committed Jun 18, 2024
1 parent da9a084 commit 5b0a119
Show file tree
Hide file tree
Showing 26 changed files with 215 additions and 97 deletions.
1 change: 1 addition & 0 deletions src/assets/images/read-only.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 11 additions & 2 deletions src/models/backingImage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { create, deleteBackingImage, query, deleteDisksOnBackingImage, uploadChunk, download, bulkDownload } from '../services/backingImage'
import { create, deleteBackingImage, query, execAction, deleteDisksOnBackingImage, uploadChunk, download, bulkDownload } from '../services/backingImage'
import { message, notification } from 'antd'
import { delay } from 'dva/saga'
import { wsChanges, updateState } from '../utils/websocket'
Expand Down Expand Up @@ -89,6 +89,16 @@ export default {
payload.sourceType === 'upload' && notification.destroy()
}
},
*createBackingImageBackup({
url,
payload,
}, { call, put }) {
const resp = yield call(execAction, url, payload)
if (resp && resp.status === 200) {
message.success(`Successfully backup backing image ${payload.backingImageName}`, 5)
}
yield put({ type: 'query' })
},
*delete({
payload,
}, { call, put }) {
Expand Down Expand Up @@ -155,7 +165,6 @@ export default {
*startWS({
payload,
}, { select }) {
// console.log('🚀 ~ backing images payload:', payload)
let ws = yield select(state => state.backingImage.ws)
if (ws) {
ws.open()
Expand Down
2 changes: 1 addition & 1 deletion src/models/backup.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export default {
if (volumeName && action.payload && action.payload.data) {
let backupData = action.payload.data.filter((item) => {
if (item.backupTargetName) {
// after support multiple backup targets feature volumeName is composed by ${volumeName}-${backupTargetName}
// after implement multiple backup targets feature, backup volume name in backup page is composed by ${volumeName}-${backupTargetName}
return volumeName === `${item.volumeName}-${item.backupTargetName}`
} else {
return item.volumeName === volumeName
Expand Down
1 change: 0 additions & 1 deletion src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ const Routers = function ({ history, app }) {
<Route exact path={`${path}instanceManager`} component={instanceManager} />
<Route exact path={`${path}backingImage`} component={backingImage} />
<Route exact path={`${path}backupTarget`} component={backupTarget} />
{/* <Route exact path={`${path}recurringJob`} component={recurringJob} /> */}
<Route exact path={`${path}recurringJob`} component={recurringJob} />
<Route path={`${path}engineimage/:id`} component={engineimageDetail} />
<Route path={`${path}orphanedData`} component={orphanedData} />
Expand Down
11 changes: 9 additions & 2 deletions src/routes/backingImage/BackingImageActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import PropTypes from 'prop-types'
import { Modal } from 'antd'
import { DropOption } from '../../components'
import { hasReadyBackingDisk } from '../../utils/status'

const confirm = Modal.confirm

function actions({ selected, deleteBackingImage, downloadBackingImage }) {
function actions({ selected, deleteBackingImage, downloadBackingImage, openBackupBackingImageModal }) {
const handleMenuClick = (event, record) => {
event.domEvent?.stopPropagation?.()
switch (event.key) {
Expand All @@ -20,15 +21,20 @@ function actions({ selected, deleteBackingImage, downloadBackingImage }) {
case 'download':
downloadBackingImage(record)
break
case 'backup': {
openBackupBackingImageModal(record)
break
}
default:
}
}

const disableDownloadAction = !hasReadyBackingDisk(selected)

const availableActions = [
{ key: 'delete', name: 'Delete' },
{ key: 'download', name: 'Download', disabled: disableDownloadAction, tooltip: disableDownloadAction ? 'Missing disk with ready state' : '' },
{ key: 'backup', name: 'Backup' },
{ key: 'delete', name: 'Delete' },
]

return (
Expand All @@ -42,6 +48,7 @@ actions.propTypes = {
selected: PropTypes.object,
deleteBackingImage: PropTypes.func,
downloadBackingImage: PropTypes.func,
openBackupBackingImageModal: PropTypes.func,
}

export default actions
4 changes: 3 additions & 1 deletion src/routes/backingImage/BackingImageList.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import BackingImageActions from './BackingImageActions'
import { pagination } from '../../utils/page'
import { formatMib } from '../../utils/formatter'

function list({ loading, dataSource, deleteBackingImage, showDiskStateMapDetail, rowSelection, downloadBackingImage, height }) {
function list({ loading, dataSource, openBackupBackingImageModal, deleteBackingImage, showDiskStateMapDetail, rowSelection, downloadBackingImage, height }) {
const backingImageActionsProps = {
deleteBackingImage,
downloadBackingImage,
openBackupBackingImageModal,
}
const state = (record) => {
if (record.deletionTimestamp) {
Expand Down Expand Up @@ -107,6 +108,7 @@ list.propTypes = {
dataSource: PropTypes.array,
deleteBackingImage: PropTypes.func,
showDiskStateMapDetail: PropTypes.func,
openBackupBackingImageModal: PropTypes.func,
rowSelection: PropTypes.object,
height: PropTypes.number,
}
Expand Down
80 changes: 80 additions & 0 deletions src/routes/backingImage/CreateBackupBackingImageModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Select, Icon } from 'antd'
import { ModalBlur } from '../../components'

const FormItem = Form.Item
const Option = Select.Option

const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 15,
},
}

const modal = ({
backingImage,
availBackupTargets,
visible,
onCancel,
onOk,
form: {
getFieldDecorator,
getFieldValue,
},
}) => {
function handleOk() {
const backupTarget = availBackupTargets.find(bkTarget => bkTarget.name === getFieldValue('backupTargetName'))
if (backupTarget) {
const url = backingImage.actions?.backupBackingImageCreate
const payload = {
...backingImage,
backingImageName: backingImage.name,
backupTargetName: backupTarget.name,
backupTargetURL: backupTarget.backupTargetURL,
}
onOk(url, payload)
}
}

const modalOpts = {
title: 'Create Backup Backing Image',
visible,
onCancel,
onOk: handleOk,
}

return (
<ModalBlur {...modalOpts}>
<p type="warning">
<Icon style={{ marginRight: '10px' }} type="exclamation-circle" />Choose a backup target to backup <strong>{backingImage.name}</strong> backing image
</p>
<div style={{ display: 'flex' }}>
<FormItem label="Backup Target" style={{ width: '100%' }} {...formItemLayout}>
{getFieldDecorator('backupTargetName', {
initialValue: availBackupTargets.length > 0 ? availBackupTargets[0].name : '',
})(
<Select style={{ width: '100%' }}>
{availBackupTargets.map(bkTarget => <Option key={bkTarget.name} value={bkTarget.name}>{bkTarget.name}</Option>)}
</Select>
)}
</FormItem>
</div>
</ModalBlur>
)
}

modal.propTypes = {
backingImage: PropTypes.object,
availBackupTargets: PropTypes.array,
form: PropTypes.object.isRequired,
visible: PropTypes.bool,
onCancel: PropTypes.func,
item: PropTypes.object,
onOk: PropTypes.func,
}

export default Form.create()(modal)
49 changes: 46 additions & 3 deletions src/routes/backingImage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import { Row, Col, Button, Progress, notification } from 'antd'
import CreateBackingImage from './CreateBackingImage'
import BackingImageList from './BackingImageList'
import DiskStateMapDetail from './DiskStateMapDetail'
import CreateBackupBackingImageModal from './CreateBackupBackingImageModal'
import { Filter } from '../../components/index'
import BackingImageBulkActions from './BackingImageBulkActions'
import queryString from 'query-string'
import { getAvailBackupTargets } from '../../utils/backupTarget'
import style from './BackingImage.less'
import C from '../../utils/constants'

Expand All @@ -18,6 +20,8 @@ class BackingImage extends React.Component {
this.state = {
height: 300,
message: null,
backupBackingImageModalVisible: false,
selectedBackingImage: {},
}
}

Expand All @@ -37,6 +41,22 @@ class BackingImage extends React.Component {
})
}

handleBackupBackingImageModalOpen = (record) => {
this.setState({
...this.state,
backupBackingImageModalVisible: true,
selectedBackingImage: record,
})
}

handleBackupBackingImageModalClose = () => {
this.setState({
...this.state,
backupBackingImageModalVisible: false,
selectedBackingImage: {},
})
}

uploadFile = (file, record) => {
let totalSize = file.size
this.props.dispatch({
Expand Down Expand Up @@ -65,8 +85,9 @@ class BackingImage extends React.Component {
}

render() {
const { dispatch, loading, location } = this.props
const { uploadFile } = this
const { dispatch, loading, location, backupTarget } = this.props
const { uploadFile, handleBackupBackingImageModalOpen, handleBackupBackingImageModalClose } = this
const { backupBackingImageModalVisible, selectedBackingImage } = this.state
const { data: volumeData } = this.props.volume
const { data, selected, createBackingImageModalVisible, createBackingImageModalKey, diskStateMapDetailModalVisible, diskStateMapDetailModalKey, diskStateMapDeleteDisabled, diskStateMapDeleteLoading, selectedDiskStateMapRows, selectedDiskStateMapRowKeys, selectedRows } = this.props.backingImage
const { backingImageUploadPercent, backingImageUploadStarted } = this.props.app
Expand Down Expand Up @@ -103,6 +124,9 @@ class BackingImage extends React.Component {
payload: record,
})
},
openBackupBackingImageModal: (record) => {
handleBackupBackingImageModalOpen(record)
},
downloadBackingImage(record) {
dispatch({
type: 'backingImage/downloadBackingImage',
Expand All @@ -128,6 +152,23 @@ class BackingImage extends React.Component {
},
}

const createBackupBackingImageModalProps = {
backingImage: selectedBackingImage,
availBackupTargets: getAvailBackupTargets(backupTarget),
visible: backupBackingImageModalVisible,
onOk(url, payload) {
dispatch({
type: 'backingImage/createBackingImageBackup',
url,
payload,
})
handleBackupBackingImageModalClose()
},
onCancel() {
handleBackupBackingImageModalClose()
},
}

const addBackingImage = () => {
dispatch({
type: 'backingImage/showCreateBackingImageModal',
Expand Down Expand Up @@ -315,6 +356,7 @@ class BackingImage extends React.Component {
<BackingImageList {...backingImageListProps} />
{ createBackingImageModalVisible ? <CreateBackingImage key={createBackingImageModalKey} {...createBackingImageModalProps} /> : ''}
{ diskStateMapDetailModalVisible ? <DiskStateMapDetail key={diskStateMapDetailModalKey} {...diskStateMapDetailModalProps} /> : ''}
<CreateBackupBackingImageModal {...createBackupBackingImageModalProps} />
</div>
)
}
Expand All @@ -323,10 +365,11 @@ class BackingImage extends React.Component {
BackingImage.propTypes = {
app: PropTypes.object,
backingImage: PropTypes.object,
backupTarget: PropTypes.object,
loading: PropTypes.bool,
location: PropTypes.object,
volume: PropTypes.object,
dispatch: PropTypes.func,
}

export default connect(({ app, volume, backingImage, loading }) => ({ app, volume, backingImage, loading: loading.models.backingImage }))(BackingImage)
export default connect(({ app, volume, backupTarget, backingImage, loading }) => ({ app, volume, backupTarget, backingImage, loading: loading.models.backingImage }))(BackingImage)
49 changes: 0 additions & 49 deletions src/routes/backupTarget/BackupTarget.less

This file was deleted.

4 changes: 3 additions & 1 deletion src/routes/backupTarget/BackupTargetActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ function actions({ selected, deleteBackupTarget, editBackupTarget }) {
case 'delete':
confirm({
width: 'fit-content',
okText: 'Delete',
okType: 'danger',
title: <p>Are you sure you want to delete <strong>{record.name}</strong> backup target ?</p>,
onOk() {
deleteBackupTarget(record)
Expand All @@ -26,7 +28,7 @@ function actions({ selected, deleteBackupTarget, editBackupTarget }) {

const availableActions = [
{ key: 'edit', name: 'Edit' },
{ key: 'delete', name: 'Delete' },
{ key: 'delete', name: 'Delete', disabled: selected.default === true, tooltip: selected.default === true ? 'Default backup target can not be deleted' : '' },
]

return (
Expand Down
4 changes: 3 additions & 1 deletion src/routes/backupTarget/BackupTargetBulkActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ function bulkActions({ selectedRows, bulkDeleteBackupTargets }) {
case 'delete':
confirm({
width: 'fit-content',
okText: 'Delete',
okType: 'danger',
title: (<>
<p>Are you sure to you want to delete below {count} Backup {count === 1 ? 'Target' : 'Targets' } ?</p>
<p>Are you sure to you want to delete below {count} backup {count === 1 ? 'target' : 'targets' } ?</p>
<ul>
{selectedRows.map(item => <li key={item.name}>{item.name}</li>)}
</ul>
Expand Down
Loading

0 comments on commit 5b0a119

Please sign in to comment.