Skip to content

Commit

Permalink
feat(xo-web/backup): backup job sequences (#8014)
Browse files Browse the repository at this point in the history
See #7985
  • Loading branch information
pdonias authored Sep 27, 2024
1 parent f7df1c4 commit d520e53
Show file tree
Hide file tree
Showing 12 changed files with 441 additions and 25 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- `rest get --output $file` now displays progress information during download
- `rest post` and `rest put` now accept `--input $file` to upload a file and display progress information
- [Backup] Detect invalid VDI exports that are incorrectly reported as successful by XAPI
- [Backup/HealthCheck] Improve error messages on health check timeout (PR [#8016](https://github.com/vatesfr/xen-orchestra/pull/8016))
- [Backup] Backup job sequences: configure lists of backup jobs to run in order one after the other (PRs [#7985](https://github.com/vatesfr/xen-orchestra/pull/7985), [#8014](https://github.com/vatesfr/xen-orchestra/pull/8014))

### Bug fixes

Expand Down Expand Up @@ -48,6 +48,6 @@
- xen-api minor
- xo-cli minor
- xo-server minor
- xo-web patch
- xo-web minor

<!--packages-end-->
5 changes: 5 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ const messages = {
xcpNg: 'XCP-ng',
noFileSelected: 'No file selected',
nRetriesVmBackupFailures: 'Number of retries if VM backup fails',
sequence: 'Sequence',
sequences: 'Sequences',

// ----- Modals -----
alertOk: 'OK',
Expand Down Expand Up @@ -531,7 +533,9 @@ const messages = {
scheduleAdd: 'Add a schedule',
scheduleDelete: 'Delete',
scheduleRun: 'Run schedule',
scheduleSequence: 'Schedule sequence',
unnamedSchedule: 'Unnamed schedule',
unnamedJob: 'Unnamed Job',
deleteSelectedSchedules: 'Delete selected schedules',
noScheduledJobs: 'No scheduled jobs.',
legacySnapshotsLink: 'You can delete all your legacy backup snapshots.',
Expand Down Expand Up @@ -1670,6 +1674,7 @@ const messages = {
missingVm: 'Missing VM',
missingVmInJob: 'This VM does not belong to this job',
missingSchedule: 'Missing schedule',
unknownSchedule: 'Unknown schedule',
noDetachedBackups: 'No backups',
noDuplicatedMacAddresses: 'No duplicated MAC addresses',
reason: 'Reason',
Expand Down
82 changes: 69 additions & 13 deletions packages/xo-web/src/common/render-xo-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CopyToClipboard from 'react-copy-to-clipboard'
import PropTypes from 'prop-types'
import React from 'react'
import { get } from '@xen-orchestra/defined'
import { injectState, provideState } from 'reaclette'
import find from 'lodash/find.js'
import isEmpty from 'lodash/isEmpty.js'

Expand All @@ -13,7 +14,16 @@ import Link from './link'
import Tooltip from './tooltip'
import { addSubscriptions, connectStore, formatSize, NumericDate, ShortDate } from './utils'
import { createGetObject, createSelector } from './selectors'
import { isSrWritable, subscribeBackupNgJobs, subscribeProxies, subscribeRemotes, subscribeUsers } from './xo'
import {
isSrWritable,
subscribeBackupNgJobs,
subscribeMetadataBackupJobs,
subscribeProxies,
subscribeRemotes,
subscribeSchedules,
subscribeUsers,
subscribeMirrorBackupJobs,
} from './xo'

// ===================================================================

Expand Down Expand Up @@ -493,6 +503,62 @@ BackupJob.defaultProps = {

// ===================================================================

export const Schedule = decorate([
addSubscriptions(({ id }) => ({
schedule: cb => subscribeSchedules(schedules => cb(schedules.find(schedule => schedule.id === id))),
backupJobs: subscribeBackupNgJobs,
metadataBackupJobs: subscribeMetadataBackupJobs,
mirrorBackupJobs: subscribeMirrorBackupJobs,
})),
provideState({
initialState: () => ({}),
computed: {
job: (_, { backupJobs = [], metadataBackupJobs = [], mirrorBackupJobs = [], schedule }) =>
schedule && [...backupJobs, ...metadataBackupJobs, ...mirrorBackupJobs].find(job => job.id === schedule.jobId),
},
}),
injectState,
({ id, schedule, showJob, showState, state: { job } }) => {
if (schedule === undefined) {
return unknowItem(id, 'schedule')
}

const isEnabled = schedule.enabled
const scheduleName = schedule.name.trim()
const jobName = job?.name.trim()

return (
<span>
{showState && (
<span className={`mr-1 tag tag-${isEnabled ? 'success' : 'danger'}`}>
{isEnabled ? _('stateEnabled') : _('stateDisabled')}
</span>
)}
<span>{scheduleName === '' ? <em>{_('unnamedSchedule')}</em> : scheduleName}</span>
{showJob && (
<span>
{' '}
({job ? jobName === '' ? <em>{_('unnamedJob')}</em> : jobName : unknowItem(schedule.jobId, 'job')})
</span>
)}
</span>
)
},
])

Schedule.propTypes = {
id: PropTypes.string.isRequired,
showState: PropTypes.bool,
showJob: PropTypes.bool,
}

Schedule.defaultProps = {
showState: true,
showJob: true,
}

// ===================================================================

export const Vgpu = connectStore(() => ({
vgpuType: createGetObject((_, props) => props.vgpu.vgpuType),
}))(({ vgpu, vgpuType }) => (
Expand Down Expand Up @@ -684,18 +750,8 @@ const xoItemToRender = {

PCI: props => <Pci {...props} self />,

schedule: schedule => {
const isEnabled = schedule.enabled
const scheduleName = schedule.name.trim()
return (
<span>
<span className={`mr-1 tag tag-${isEnabled ? 'success' : 'danger'}`}>
{isEnabled ? _('stateEnabled') : _('stateDisabled')}
</span>
<span>{scheduleName === '' ? <em>{_('unnamedSchedule')}</em> : scheduleName}</span>
</span>
)
},
schedule: props => <Schedule {...props} />,

job: job => <spans>{job.name}</spans>,
}

Expand Down
1 change: 1 addition & 0 deletions packages/xo-web/src/common/select-objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class GenericSelect extends React.Component {
: undefined,
memoryFree: option.xoItem.type === 'host' || undefined,
showNetwork: true,
...(this.props.optionProps ?? {}),
})}
</span>
)
Expand Down
4 changes: 4 additions & 0 deletions packages/xo-web/src/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,10 @@
@extend .fa;
@extend .fa-eye;
}
&-sequence {
@extend .fa;
@extend .fa-list-ol;
}
&-new {
@extend .fa;
@extend .fa-plus;
Expand Down
33 changes: 27 additions & 6 deletions packages/xo-web/src/xo-app/backup/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import Icon from 'icon'
import React from 'react'
import { injectState, provideState } from 'reaclette'
import { find, groupBy, keyBy } from 'lodash'
import { subscribeBackupNgJobs, subscribeMetadataBackupJobs, subscribeMirrorBackupJobs, subscribeSchedules } from 'xo'
import {
subscribeBackupNgJobs,
subscribeJobs,
subscribeMetadataBackupJobs,
subscribeMirrorBackupJobs,
subscribeSchedules,
} from 'xo'

import Metadata from './new/metadata'
import New from './new'
import NewMirrorBackup from './new/mirror'
import NewSequence from './new/sequence'

export default decorate([
addSubscriptions({
jobs: subscribeBackupNgJobs,
jobs: subscribeJobs,
backupJobs: subscribeBackupNgJobs,
metadataJobs: subscribeMetadataBackupJobs,
mirrorBackupJobs: subscribeMirrorBackupJobs,
schedulesByJob: cb =>
Expand All @@ -24,11 +32,20 @@ export default decorate([
}),
provideState({
computed: {
job: (_, { jobs, metadataJobs, mirrorBackupJobs, routeParams: { id } }) =>
defined(find(jobs, { id }), find(metadataJobs, { id }), find(mirrorBackupJobs, { id })),
job: (_, { backupJobs, jobs, metadataJobs, mirrorBackupJobs, routeParams: { id } }) =>
defined(
find(jobs, { id }),
find(backupJobs, { id }),
find(metadataJobs, { id }),
find(mirrorBackupJobs, { id })
),
schedules: (_, { schedulesByJob, routeParams: { id } }) => schedulesByJob && keyBy(schedulesByJob[id], 'id'),
loading: (_, props) =>
props.jobs === undefined || props.metadataJobs === undefined || props.schedulesByJob === undefined,
props.jobs === undefined ||
props.backupJobs === undefined ||
props.metadataJobs === undefined ||
props.mirrorBackupJobs === undefined ||
props.schedulesByJob === undefined,
},
}),
injectState,
Expand All @@ -43,7 +60,11 @@ export default decorate([
<New job={job} schedules={schedules} />
) : job.type === 'mirrorBackup' ? (
<NewMirrorBackup job={job} schedules={schedules} />
) : (
) : job.type === 'metadataBackup' ? (
<Metadata job={job} schedules={schedules} />
) : job.type === 'call' && job.method === 'schedule.runSequence' ? (
<NewSequence job={job} schedule={schedules[Object.keys(schedules)[0]]} />
) : (
'Unknown job type'
),
])
11 changes: 10 additions & 1 deletion packages/xo-web/src/xo-app/backup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import { subscribeBackupNgJobs, subscribeSchedules } from 'xo'
import Edit from './edit'
import FileRestore from './file-restore'
import Health from './health'
import NewVmBackup, { NewMetadataBackup, NewMirrorBackup } from './new'
import NewVmBackup, { NewMetadataBackup, NewMirrorBackup, NewSequence } from './new'
import Overview from './overview'
import Restore, { RestoreMetadata } from './restore'
import Sequences from './sequences'

import Page from '../page'

Expand Down Expand Up @@ -55,6 +56,9 @@ const HEADER = (
<NavLink to='/backup/overview'>
<Icon icon='menu-backup-overview' /> {_('backupOverviewPage')}
</NavLink>
<NavLink to='/backup/sequences'>
<Icon icon='menu-backup-sequence' /> {_('sequences')}
</NavLink>
<NavLink to='/backup/new'>
<Icon icon='menu-backup-new' /> {_('backupNewPage')}
</NavLink>
Expand Down Expand Up @@ -89,6 +93,9 @@ const ChooseBackupType = () => (
<p>
<ButtonLink to='backup/new/metadata'>
<Icon icon='database' /> {_('backupMetadata')}
</ButtonLink>{' '}
<ButtonLink to='backup/new/sequence'>
<Icon icon='menu-backup-sequence' /> {_('sequence')}
</ButtonLink>
</p>
</CardBlock>
Expand All @@ -104,7 +111,9 @@ export default routes('overview', {
'new/vms': NewVmBackup,
'new/mirror': NewMirrorBackup,
'new/metadata': NewMetadataBackup,
'new/sequence': NewSequence,
overview: Overview,
sequences: Sequences,
restore: Restore,
'restore/metadata': RestoreMetadata,
'file-restore': FileRestore,
Expand Down
1 change: 1 addition & 0 deletions packages/xo-web/src/xo-app/backup/new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { canDeltaBackup, constructPattern, destructPattern, FormFeedback, FormGr

export NewMetadataBackup from './metadata'
export NewMirrorBackup from './mirror'
export NewSequence from './sequence'

// ===================================================================

Expand Down
Loading

0 comments on commit d520e53

Please sign in to comment.