Skip to content

Commit

Permalink
feat(xo-web/backup): ability to migrate legacy to NG (#2801)
Browse files Browse the repository at this point in the history
Fixes #2711
  • Loading branch information
badrAZ authored and julien-f committed Mar 28, 2018
1 parent 7a6e7ec commit 8a2fbe3
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 51 deletions.
10 changes: 10 additions & 0 deletions packages/xo-server/src/api/backup-ng.js
Expand Up @@ -32,6 +32,16 @@ createJob.params = {
},
}

export function migrateLegacyJob ({ id }) {
return this.migrateLegacyBackupJob(id)
}
migrateLegacyJob.permission = 'admin'
migrateLegacyJob.params = {
id: {
type: 'string',
},
}

export function deleteJob ({ id }) {
return this.deleteBackupNgJob(id)
}
Expand Down
18 changes: 15 additions & 3 deletions packages/xo-server/src/xo-mixins/backups-ng/index.js
Expand Up @@ -8,7 +8,7 @@ import { basename, dirname } from 'path'
import { isEmpty, last, mapValues, noop, values } from 'lodash'
import { timeout as pTimeout } from 'promise-toolbox'

import { type Executor, type Job } from '../jobs'
import { type CallJob, type Executor, type Job } from '../jobs'
import { type Schedule } from '../scheduling'

import type RemoteHandler from '../../remote-handlers/abstract'
Expand All @@ -31,6 +31,8 @@ import {
readVhdMetadata,
} from '../../vhd-merge'

import { translateLegacyJob } from './migration'

type Mode = 'full' | 'delta'

type Settings = {|
Expand Down Expand Up @@ -305,8 +307,10 @@ export default class BackupNg {
getAllSchedules: () => Promise<Schedule[]>,
getRemoteHandler: (id: string) => Promise<RemoteHandler>,
getXapi: (id: string) => Xapi,
getJob: (id: string, 'backup') => Promise<BackupJob>,
updateJob: ($Shape<BackupJob>) => Promise<BackupJob>,
getJob: ((id: string, 'backup') => Promise<BackupJob>) &
((id: string, 'call') => Promise<CallJob>),
updateJob: (($Shape<BackupJob>, ?boolean) => Promise<BackupJob>) &
(($Shape<CallJob>, ?boolean) => Promise<CallJob>),
removeJob: (id: string) => Promise<void>,
worker: $Dict<any>,
}
Expand Down Expand Up @@ -540,6 +544,14 @@ export default class BackupNg {
return backupsByVmByRemote
}

async migrateLegacyBackupJob (jobId: string) {
const [job, schedules] = await Promise.all([
this._app.getJob(jobId, 'call'),
this._app.getAllSchedules(),
])
await this._app.updateJob(translateLegacyJob(job, schedules), false)
}

// High:
// - [ ] clones of replicated VMs should not be garbage collected
// - if storing uuids in source VM, how to detect them if the source is
Expand Down
54 changes: 23 additions & 31 deletions packages/xo-server/src/xo-mixins/backups-ng/migration.js
Expand Up @@ -89,7 +89,7 @@ const methods = {
}),
}

const parseParamsVector = vector => {
const parseParamsVector = (vector: any) => {
assert.strictEqual(vector.type, 'crossProduct')
const { items } = vector
assert.strictEqual(items.length, 2)
Expand Down Expand Up @@ -120,34 +120,26 @@ const parseParamsVector = vector => {
return { ...params, vms }
}

export const translateOldJobs = async (app: any): Promise<Array<BackupJob>> => {
const backupJobs: Array<BackupJob> = []
const [jobs, schedules] = await Promise.all([
app.getAllJobs('call'),
app.getAllSchedules(),
])
jobs.forEach(job => {
try {
const { id } = job
let method, schedule
if (
job.type === 'call' &&
(method = methods[job.method]) !== undefined &&
(schedule = schedules.find(_ => _.jobId === id)) !== undefined
) {
const params = parseParamsVector(job.paramsVector)
backupJobs.push({
id,
name: params.tag || job.name,
type: 'backup',
userId: job.userId,
// $FlowFixMe `method` is initialized but Flow fails to see this
...method(job, params, schedule),
})
}
} catch (error) {
console.warn('translateOldJobs', job, error)
}
})
return backupJobs
export const translateLegacyJob = (
job: CallJob,
schedules: Schedule[]
): BackupJob => {
const { id } = job
let method, schedule
if (
job.type !== 'call' ||
(method = methods[job.method]) === undefined ||
(schedule = schedules.find(_ => _.jobId === id)) === undefined
) {
throw new Error(`cannot convert job ${job.id}`)
}
const params = parseParamsVector(job.paramsVector)
return {
id,
name: params.tag || job.name,
type: 'backup',
userId: job.userId,
// $FlowFixMe `method` is initialized but Flow fails to see this
...method(job, params, schedule),
}
}
25 changes: 14 additions & 11 deletions packages/xo-server/src/xo-mixins/jobs/index.js
Expand Up @@ -21,40 +21,40 @@ export type Job = {
id: string,
name: string,
type: string,
userId: string
userId: string,
}

type ParamsVector =
| {|
items: Array<Object>,
type: 'crossProduct'
type: 'crossProduct',
|}
| {|
mapping: Object,
type: 'extractProperties',
value: Object
value: Object,
|}
| {|
pattern: Pattern,
type: 'fetchObjects'
type: 'fetchObjects',
|}
| {|
collection: Object,
iteratee: Function,
paramName?: string,
type: 'map'
type: 'map',
|}
| {|
type: 'set',
values: any
values: any,
|}

export type CallJob = {|
...$Exact<Job>,
method: string,
paramsVector: ParamsVector,
timeout?: number,
type: 'call'
type: 'call',
|}

export type Executor = ({|
Expand All @@ -64,7 +64,7 @@ export type Executor = ({|
logger: Logger,
runJobId: string,
schedule?: Schedule,
session: Object
session: Object,
|}) => Promise<any>

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -180,9 +180,12 @@ export default class Jobs {
return this._jobs.create(job)
}

async updateJob ({ id, ...props }: $Shape<Job>) {
const job = await this.getJob(id)
patch(job, props)
async updateJob (job: $Shape<Job>, merge: boolean = true) {
if (merge) {
const { id, ...props } = job
job = await this.getJob(id)
patch(job, props)
}
return /* await */ this._jobs.save(job)
}

Expand Down
3 changes: 3 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Expand Up @@ -315,6 +315,9 @@ const messages = {
noMatchingVms: 'There are no matching VMs!',
allMatchingVms: '{icon} See the matching VMs ({nMatchingVms, number})',
backupOwner: 'Backup owner',
migrateBackupSchedule: 'Migrate to backup NG',
migrateBackupScheduleMessage:
'This will migrate this backup to a backup NG. This operation is not reversible. Do you want to continue?',

// ------ New backup -----
newBackupSelection: 'Select your backup type:',
Expand Down
6 changes: 6 additions & 0 deletions packages/xo-web/src/common/xo/index.js
Expand Up @@ -1598,6 +1598,12 @@ export const deleteBackupSchedule = async schedule => {
subscribeJobs.forceRefresh()
}

export const migrateBackupSchedule = id =>
confirm({
title: _('migrateBackupSchedule'),
body: _('migrateBackupScheduleMessage'),
}).then(() => _call('backupNg.migrateLegacyJob', { id: resolveId(id) }))

export const deleteSchedule = schedule =>
_call('schedule.delete', { id: resolveId(schedule) })::tap(
subscribeSchedules.forceRefresh
Expand Down
4 changes: 4 additions & 0 deletions packages/xo-web/src/icons.scss
Expand Up @@ -285,6 +285,10 @@
@extend .fa;
@extend .fa-cogs;
}
&-migrate-job {
@extend .fa;
@extend .fa-share;
}

// VM
&-vm {
Expand Down
17 changes: 11 additions & 6 deletions packages/xo-web/src/xo-app/backup-ng/new/index.js
Expand Up @@ -29,13 +29,18 @@ import { FormGroup, getRandomId, Input, Ul, Li } from './utils'

const normaliseTagValues = values => resolveIds(values).map(value => [value])

const constructPattern = values => ({
id: {
__or: resolveIds(values),
},
})
const constructPattern = values =>
values.length === 1
? {
id: resolveId(values[0]),
}
: {
id: {
__or: resolveIds(values),
},
}

const destructPattern = pattern => pattern.id.__or
const destructPattern = pattern => pattern.id.__or || [pattern.id]

const destructVmsPattern = pattern =>
pattern.id === undefined
Expand Down
7 changes: 7 additions & 0 deletions packages/xo-web/src/xo-app/backup/overview/index.js
Expand Up @@ -19,6 +19,7 @@ import {
deleteBackupSchedule,
disableSchedule,
enableSchedule,
migrateBackupSchedule,
runJob,
subscribeJobs,
subscribeSchedules,
Expand Down Expand Up @@ -115,6 +116,12 @@ const JOB_COLUMNS = [
handler={runJob}
handlerParam={schedule.jobId}
/>
<ActionRowButton
icon='migrate-job'
btnStyle='danger'
handler={migrateBackupSchedule}
handlerParam={schedule.jobId}
/>
</ButtonGroup>
</fieldset>
),
Expand Down

0 comments on commit 8a2fbe3

Please sign in to comment.