parent
0215c19d1d
commit
a71740d49a
@ -11,6 +11,7 @@
|
||||
- [Backup NG Overview] Display transferred and merged data size for backup jobs [#3340](https://github.com/vatesfr/xen-orchestra/issues/3340) (PR [#3408](https://github.com/vatesfr/xen-orchestra/pull/3408))
|
||||
- [VM] Display the PVHVM status [#3014](https://github.com/vatesfr/xen-orchestra/issues/3014) (PR [#3418](https://github.com/vatesfr/xen-orchestra/pull/3418))
|
||||
- [Backup reports] Ability to test the plugin (PR [#3421](https://github.com/vatesfr/xen-orchestra/pull/3421))
|
||||
- [Backup NG] Ability to restart failed VMs' backup [#3339](https://github.com/vatesfr/xen-orchestra/issues/3339) (PR [#3420](https://github.com/vatesfr/xen-orchestra/pull/3420))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
@ -118,8 +118,13 @@ getJob.params = {
|
||||
},
|
||||
}
|
||||
|
||||
export async function runJob ({ id, schedule, vm }) {
|
||||
return this.runJobSequence([id], await this.getSchedule(schedule), vm)
|
||||
export async function runJob ({
|
||||
id,
|
||||
schedule,
|
||||
vm,
|
||||
vms = vm !== undefined ? [vm] : undefined,
|
||||
}) {
|
||||
return this.runJobSequence([id], await this.getSchedule(schedule), vms)
|
||||
}
|
||||
|
||||
runJob.permission = 'admin'
|
||||
@ -135,6 +140,13 @@ runJob.params = {
|
||||
type: 'string',
|
||||
optional: true,
|
||||
},
|
||||
vms: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -433,7 +433,7 @@ export default class BackupNg {
|
||||
app.on('start', () => {
|
||||
const executor: Executor = async ({
|
||||
cancelToken,
|
||||
data: vmId,
|
||||
data: vmsId,
|
||||
job: job_,
|
||||
logger,
|
||||
runJobId,
|
||||
@ -445,17 +445,20 @@ export default class BackupNg {
|
||||
|
||||
const job: BackupJob = (job_: any)
|
||||
|
||||
let vms: $Dict<Vm> | void
|
||||
if (vmId === undefined) {
|
||||
vms = app.getObjects({
|
||||
filter: createPredicate({
|
||||
type: 'VM',
|
||||
...job.vms,
|
||||
}),
|
||||
})
|
||||
if (isEmpty(vms)) {
|
||||
throw new Error('no VMs match this pattern')
|
||||
}
|
||||
const vms: $Dict<Vm> = app.getObjects({
|
||||
filter: createPredicate({
|
||||
type: 'VM',
|
||||
...(vmsId !== undefined
|
||||
? {
|
||||
id: {
|
||||
__or: vmsId,
|
||||
},
|
||||
}
|
||||
: job.vms),
|
||||
}),
|
||||
})
|
||||
if (isEmpty(vms)) {
|
||||
throw new Error('no VMs match this pattern')
|
||||
}
|
||||
const jobId = job.id
|
||||
const srs = unboxIds(job.srs).map(id => {
|
||||
@ -541,10 +544,6 @@ export default class BackupNg {
|
||||
}
|
||||
}
|
||||
|
||||
if (vms === undefined) {
|
||||
return handleVm(await app.getObject(vmId))
|
||||
}
|
||||
|
||||
const concurrency: number = getSetting(job.settings, 'concurrency', [
|
||||
'',
|
||||
])
|
||||
|
@ -1775,6 +1775,7 @@ const messages = {
|
||||
reportBug: 'Report a bug',
|
||||
unhealthyVdiChainError: 'Job canceled to protect the VDI chain',
|
||||
backupRestartVm: "Restart VM's backup",
|
||||
backupRestartFailedVms: "Restart failed VMs' backup",
|
||||
clickForMoreInformation: 'Click for more information',
|
||||
|
||||
// ----- IPs ------
|
||||
|
@ -1,4 +1,5 @@
|
||||
import _ from 'intl'
|
||||
import ActionButton from 'action-button'
|
||||
import addSubscriptions from 'add-subscriptions'
|
||||
import Button from 'button'
|
||||
import ButtonGroup from 'button-group'
|
||||
@ -9,7 +10,10 @@ import ReportBugButton, { CAN_REPORT_BUG } from 'report-bug-button'
|
||||
import Tooltip from 'tooltip'
|
||||
import { get } from 'xo-defined'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
import { subscribeBackupNgLogs } from 'xo'
|
||||
import { runBackupNgJob, subscribeBackupNgLogs } from 'xo'
|
||||
|
||||
const isFailureTask = ({ status }) =>
|
||||
status !== 'success' && status !== 'pending'
|
||||
|
||||
export default [
|
||||
addSubscriptions(({ id }) => ({
|
||||
@ -19,12 +23,26 @@ export default [
|
||||
}),
|
||||
})),
|
||||
provideState({
|
||||
effects: {
|
||||
restartFailedVms: () => async (
|
||||
_,
|
||||
{ log: { jobId: id, scheduleId: schedule, tasks } }
|
||||
) => {
|
||||
await runBackupNgJob({
|
||||
id,
|
||||
schedule,
|
||||
vms:
|
||||
tasks && tasks.filter(isFailureTask).map(vmTask => vmTask.data.id),
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
formattedLog: (_, { log }) => JSON.stringify(log, null, 2),
|
||||
jobFailed: (_, { log }) => log !== undefined && isFailureTask(log),
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ state, log = {}, jobs }) => (
|
||||
({ state, effects, log = {}, jobs }) => (
|
||||
<span>
|
||||
{get(() => jobs[log.jobId].name) || 'Job'} (
|
||||
{get(() => log.jobId.slice(4, 8))}){' '}
|
||||
@ -46,6 +64,15 @@ export default [
|
||||
title='Backup job failed'
|
||||
/>
|
||||
)}
|
||||
{state.jobFailed &&
|
||||
log.scheduleId !== undefined && (
|
||||
<ActionButton
|
||||
handler={effects.restartFailedVms}
|
||||
icon='run'
|
||||
size='small'
|
||||
tooltip={_('backupRestartFailedVms')}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
</span>
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user