Skip to content

Commit

Permalink
Disable create backup option if no available and writable backup target
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 Jun 19, 2024
1 parent 5b0a119 commit 9317a11
Show file tree
Hide file tree
Showing 14 changed files with 69 additions and 33 deletions.
13 changes: 9 additions & 4 deletions src/models/backup.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default {
lastBackupUrl: '',
volumeName: '',
backupTargetMessage: '',
backupTargetAvailable: false,
previousChecked: false,
tagsLoading: true,
size: '',
Expand All @@ -32,7 +33,6 @@ export default {
backupVolumesForBulkCreate: [],
search: {},
restoreBackupModalVisible: false,
backupTargetAvailable: false,
workloadDetailModalVisible: false,
createVolumeStandModalVisible: false,
bulkCreateVolumeStandModalVisible: false,
Expand Down Expand Up @@ -104,9 +104,14 @@ export default {
payload,
}, { call, put }) {
const resp = yield call(queryBackupTarget)
if (resp && resp.data && resp.data[0]) {
const backupTargetAvailable = resp.data.some(d => d.available === true)
const backupTargetMessage = backupTargetAvailable ? '' : 'No backup target available'
if (resp && resp.status === 200) {
const backupTargetAvailable = resp?.data?.some(d => d.available === true) || false
const backupTargetMessage = backupTargetAvailable ? '' : 'No backup target is available, please go to Setting -> Backup Target page to create one'
if (payload.history.location.pathname === '/backup' && !backupTargetAvailable) {
message.error(backupTargetMessage, 5)
} else {
message.destroy()
}
yield put({ type: 'setBackupTargetAvailable', payload: { backupTargetAvailable, backupTargetMessage } })
}
},
Expand Down
2 changes: 2 additions & 0 deletions src/models/backupTarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { message } from 'antd'
import { wsChanges, updateState } from '../utils/websocket'
import queryString from 'query-string'
import { enableQueryData } from '../utils/dataDependency'
import { delay } from 'dva/saga'

export default {
ws: null,
Expand Down Expand Up @@ -58,6 +59,7 @@ export default {
payload,
}, { call, put }) {
yield call(updateBackupTarget, payload)
yield delay(1000)
yield put({ type: 'query' })
},
*bulkDelete({
Expand Down
11 changes: 7 additions & 4 deletions src/routes/backingImage/BackingImageActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { hasReadyBackingDisk } from '../../utils/status'

const confirm = Modal.confirm

function actions({ selected, deleteBackingImage, downloadBackingImage, openBackupBackingImageModal }) {
function actions({ selected, deleteBackingImage, downloadBackingImage, openBackupBackingImageModal, hasWritableBackupTargets }) {
const handleMenuClick = (event, record) => {
event.domEvent?.stopPropagation?.()
switch (event.key) {
Expand All @@ -29,11 +29,14 @@ function actions({ selected, deleteBackingImage, downloadBackingImage, openBacku
}
}

const disableDownloadAction = !hasReadyBackingDisk(selected)
const disableAction = !hasReadyBackingDisk(selected)

const disabledBackupAction = !hasWritableBackupTargets || !hasReadyBackingDisk(selected)
const backupTargetMessageTooltip = !hasWritableBackupTargets ? 'No backup target is available and writable' : 'Missing disk with ready state'

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

Expand Down
14 changes: 13 additions & 1 deletion src/routes/backingImage/BackingImageList.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ import BackingImageActions from './BackingImageActions'
import { pagination } from '../../utils/page'
import { formatMib } from '../../utils/formatter'

function list({ loading, dataSource, openBackupBackingImageModal, deleteBackingImage, showDiskStateMapDetail, rowSelection, downloadBackingImage, height }) {
function list({
loading,
dataSource,
openBackupBackingImageModal,
deleteBackingImage,
showDiskStateMapDetail,
rowSelection,
downloadBackingImage,
height,
hasWritableBackupTargets,
}) {
const backingImageActionsProps = {
deleteBackingImage,
downloadBackingImage,
openBackupBackingImageModal,
hasWritableBackupTargets,
}
const state = (record) => {
if (record.deletionTimestamp) {
Expand Down Expand Up @@ -111,6 +122,7 @@ list.propTypes = {
openBackupBackingImageModal: PropTypes.func,
rowSelection: PropTypes.object,
height: PropTypes.number,
hasWritableBackupTargets: PropTypes.bool,
}

export default list
3 changes: 2 additions & 1 deletion src/routes/backingImage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CreateBackupBackingImageModal from './CreateBackupBackingImageModal'
import { Filter } from '../../components/index'
import BackingImageBulkActions from './BackingImageBulkActions'
import queryString from 'query-string'
import { getAvailBackupTargets } from '../../utils/backupTarget'
import { getAvailBackupTargets, hasWritableBackupTargets } from '../../utils/backupTarget'
import style from './BackingImage.less'
import C from '../../utils/constants'

Expand Down Expand Up @@ -117,6 +117,7 @@ class BackingImage extends React.Component {
const backingImageListProps = {
dataSource: backingImages,
height: this.state.height,
hasWritableBackupTargets: hasWritableBackupTargets(backupTarget),
loading,
deleteBackingImage(record) {
dispatch({
Expand Down
3 changes: 1 addition & 2 deletions src/routes/backupTarget/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class BackupTarget extends React.Component {
selectedEditRow: record,
editBackupTargetModalVisible: true,
})
this.handleEditModalOpen()
}

handleEditModalClose = () => {
Expand Down Expand Up @@ -216,7 +215,7 @@ class BackupTarget extends React.Component {
<Button className="out-container-button" size="large" type="primary" disabled={loading} onClick={() => this.handleCreateModalOpen()}>
Create Backup Target
</Button>
<BackupTargetList key={Math.random()} {...backupTargetListProps} />
<BackupTargetList {...backupTargetListProps} />
{createBackupTargetModalVisible && <CreateBackupTargetModal {...createBackupTargetModalProps} />}
{editBackupTargetModalVisible && <EditBackupTargetModal {...editBackupTargetModalProps} />}
</div>
Expand Down
11 changes: 6 additions & 5 deletions src/routes/recurringJob/CreateRecurringJob.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ const modal = ({
setFieldsValue,
},
}) => {
const isBackupTask = () => getFieldValue('task') === 'backup' || getFieldValue('task') === 'backup-force-create'

function handleOk() {
validateFields((errors) => {
if (errors) {
if (errors || (isBackupTask() && getFieldValue('backupTargetName') === '')) {
return
}
const data = {
Expand Down Expand Up @@ -108,6 +110,7 @@ const modal = ({
delete data.keysForlabels
}
delete data.defaultGroup

onOk(data)
})
}
Expand Down Expand Up @@ -174,9 +177,6 @@ const modal = ({
return getFieldValue('task') === 'backup' || getFieldValue('task') === 'snapshot'
}

const showBackupTargetDropdown = () => {
return getFieldValue('task') === 'backup' || getFieldValue('task') === 'backup-force-create'
}

// init params
getFieldDecorator('keys', { initialValue: isEdit && item.groups && item.groups.length > 0 ? item.groups.map((group, index) => { return { initialValue: group, index } }) : [{ index: 0, initialValue: '' }] })
Expand Down Expand Up @@ -315,14 +315,15 @@ const modal = ({
</Tooltip>}
</div>
<div>
{showBackupTargetDropdown()
{isBackupTask()
&& <FormItem label="Backup Target" {...formItemLayout}>
{getFieldDecorator('backupTargetName', {
// eslint-disable-next-line no-nested-ternary
initialValue: isEdit ? item.backupTargetName : availBackupTargets.length > 0 ? availBackupTargets[0].name : '',
rules: [
{
required: true,
message: 'Please select a backup target',
},
],
})(
Expand Down
4 changes: 3 additions & 1 deletion src/routes/systemBackups/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class SystemBackups extends React.Component {

const SystemBackupsBulkActionProps = {
selectedRows: this.state.selectedSystemBackupsRows,
backupTarget: this.props.backupTarget,
deleteSystemBackups() {
dispatch({
type: 'systemBackups/bulkDeleteSystemBackup',
Expand Down Expand Up @@ -264,6 +265,7 @@ SystemBackups.propTypes = {
dispatch: PropTypes.func,
systemBackups: PropTypes.object,
location: PropTypes.object,
backupTarget: PropTypes.object,
}

export default connect(({ systemBackups, loading }) => ({ systemBackups, loading: loading.models.systemBackups }))(SystemBackups)
export default connect(({ systemBackups, loading, backupTarget }) => ({ systemBackups, backupTarget, loading: loading.models.systemBackups }))(SystemBackups)
7 changes: 5 additions & 2 deletions src/routes/systemBackups/systemBackupsBulkActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Button, Modal } from 'antd'
import style from './systemBackupsBulkActions.less'
import { hasWritableBackupTargets } from '../../utils/backupTarget'

const confirm = Modal.confirm

function bulkActions({ selectedRows, deleteSystemBackups, createSystemBackup }) {
function bulkActions({ selectedRows, deleteSystemBackups, createSystemBackup, backupTarget }) {
const createBtnDisable = !hasWritableBackupTargets(backupTarget)
const handleClick = (action) => {
switch (action) {
case 'create':
Expand All @@ -23,7 +25,7 @@ function bulkActions({ selectedRows, deleteSystemBackups, createSystemBackup })
}
}
const allActions = [
{ key: 'create', name: 'Create' },
{ key: 'create', name: 'Create', disabled: createBtnDisable },
{ key: 'delete', name: 'Delete', disabled: selectedRows.length === 0 },
]

Expand All @@ -45,6 +47,7 @@ bulkActions.propTypes = {
selectedRows: PropTypes.array,
deleteSystemBackups: PropTypes.func,
createSystemBackup: PropTypes.func,
backupTarget: PropTypes.object,
}

export default bulkActions
4 changes: 2 additions & 2 deletions src/routes/volume/detail/CreateBackupModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ const modal = ({
function handleOk() {
validateFields((errors) => {
if (errors) return

const labels = getLabels(getFieldsValue)
const backupTargetName = getFieldValue('backupTargetName')
const data = {
labels,
backupTargetName: getFieldValue('backupTargetName'),
backupTargetName,
}
onOk(data)
})
Expand Down
12 changes: 6 additions & 6 deletions src/routes/volume/detail/Snapshots.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,13 @@ class Snapshots extends React.Component {
}
const upgradingEngine = () => this.props.volume.currentImage !== this.props.volume.image

const disableBackup = !this.props.volume.actions || !this.props.volume.actions.snapshotCreate || !this.props.state || this.props.volume.standby || isRestoring() || upgradingEngine() || !this.props.backupTargetAvailable
const disableBackup = !this.props.volume.actions || !this.props.volume.actions.snapshotCreate || !this.props.state || this.props.volume.standby || isRestoring() || upgradingEngine() || this.props.availBackupTargets.length === 0

const createSnapshotDisabled = disabledSnapshotAction(this.props.volume, this.props.state) || this.props.volume.standby || isRestoring() || upgradingEngine()

const createBackupTooltipMessage = () => {
if (!this.props.backupTargetAvailable) {
return this.props.backupTargetMessage
if (this.props.availBackupTargets.length === 0) {
return 'No backup target is available and writable.'
}
if (this.props.volume.standby) {
return 'Unable to create backup for DR volume.'
Expand Down Expand Up @@ -285,7 +287,7 @@ class Snapshots extends React.Component {
<div>Snapshots and Backups</div>
<div>
<Tooltip placement="top" title={this.props.volume.standby ? 'Unable to create snapshot for DR volume' : "Create a new snapshot. You can create a backup by clicking any snapshot below and selecting 'Backup'."}>
<Button disabled={disabledSnapshotAction(this.props.volume, this.props.state) || this.props.volume.standby || isRestoring() || upgradingEngine()}
<Button disabled={createSnapshotDisabled}
icon="scan"
onClick={() => { this.onAction({ type: 'snapshotCreate' }) }}
type="primary">
Expand Down Expand Up @@ -326,8 +328,6 @@ Snapshots.propTypes = {
snapshotTreeWithRemoved: PropTypes.array,
state: PropTypes.bool,
showRemoved: PropTypes.bool,
backupTargetAvailable: PropTypes.bool,
backupTargetMessage: PropTypes.string,
volumeHead: PropTypes.object,
availBackupTargets: PropTypes.array,
}
Expand Down
7 changes: 3 additions & 4 deletions src/routes/volume/detail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
getUpdateSnapshotMaxCountModalProps,
getUpdateSnapshotMaxSizeModalProps,
} from '../helper'
import { getAvailBackupTargets } from '../../../utils/backupTarget'

const confirm = Modal.confirm

Expand Down Expand Up @@ -89,14 +90,14 @@ function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, backupTarg
updateSnapshotMaxCountModalVisible,
updateSnapshotMaxSizeModalVisible,
} = volume
const { backupStatus, backupTargetAvailable, backupTargetMessage } = backup
const { backupStatus } = backup
const { data: snapshotData, state: snapshotModalState } = snapshotModal
const { data: recurringJobData } = recurringJob
const hosts = host.data
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 availBackupTargets = getAvailBackupTargets(backupTarget)
const settings = setting.data
const defaultDataLocalitySetting = settings.find(s => s.id === 'default-data-locality')
const defaultSnapshotDataIntegritySetting = settings.find(s => s.id === 'snapshot-data-integrity')
Expand Down Expand Up @@ -496,8 +497,6 @@ function VolumeDetail({ snapshotModal, dispatch, backup, engineimage, backupTarg
volumeId,
dispatch,
availBackupTargets,
backupTargetAvailable,
backupTargetMessage,
volumeHead: snapshotData.find(d => d.name === 'volume-head'),
}

Expand Down
6 changes: 6 additions & 0 deletions src/utils/backupTarget.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// get the available and writable backup targets
export function getAvailBackupTargets(backupTarget) {
if (!backupTarget || !backupTarget.data || !backupTarget.data.length) {
return []
}
return backupTarget.data.filter((item) => item.available && !item.readOnly)
}

// return true if there are available and writable backup targets
export function hasWritableBackupTargets(backupTarget) {
return getAvailBackupTargets(backupTarget).length > 0
}
5 changes: 4 additions & 1 deletion src/utils/dataDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ const dependency = {
runWs: [{
ns: 'systemBackups',
key: 'systemBackups',
}, {
ns: 'backupTarget',
key: 'backuptargets',
}],
},
}
Expand Down Expand Up @@ -182,7 +185,7 @@ const httpDataDependency = {
'/backup': ['host', 'setting', 'backingImage', 'backup'],
'/instanceManager': ['volume', 'instanceManager'],
'/orphanedData': ['orphanedData'],
'/systemBackups': ['systemBackups'],
'/systemBackups': ['systemBackups', 'backupTarget'],
}

export function getDataDependency(pathName) {
Expand Down

0 comments on commit 9317a11

Please sign in to comment.