-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(xo-server): implement warm migration backend (#6549)
- Loading branch information
1 parent
d6192a4
commit 72c69d7
Showing
2 changed files
with
125 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { Backup } from '@xen-orchestra/backups/Backup.js' | ||
import { uuid } from 'uuid' | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
export default class MigrateVm { | ||
constructor(app) { | ||
this._app = app | ||
} | ||
|
||
// Backup should be reinstentiated each time | ||
#createWarmBackup(sourceVmId, srId, jobId) { | ||
const app = this._app | ||
const config = { | ||
snapshotNameLabelTpl: '[XO warm migration {job.name}] {vm.name_label}', | ||
} | ||
const job = { | ||
type: 'backup', | ||
id: jobId, | ||
mode: 'delta', | ||
vms: { id: sourceVmId }, | ||
name: `Warm migration`, | ||
srs: { id: srId }, | ||
settings: { | ||
'': { | ||
// mandatory for delta replication writer | ||
copyRetention: 1, | ||
}, | ||
}, | ||
} | ||
const schedule = { id: 'one-time' } | ||
|
||
// for now we only support this from the main OA, no proxy | ||
return new Backup({ | ||
config, | ||
job, | ||
schedule, | ||
getAdapter: async remoteId => app.getBackupsRemoteAdapter(await app.getRemoteWithCredentials(remoteId)), | ||
|
||
// `@xen-orchestra/backups/Backup` expect that `getConnectedRecord` returns a promise | ||
getConnectedRecord: async (xapiType, uuid) => app.getXapiObject(uuid), | ||
}) | ||
} | ||
|
||
async warmMigrateVm(sourceVmId, srId, startDestVm = true, deleteSource = false) { | ||
// we'll use a one time use continuous replication job with the VM to migrate | ||
const jobId = uuid.v4() | ||
const app = this._app | ||
const sourceVm = app.getXapiObject(sourceVmId) | ||
let backup = this.#createWarmBackup(sourceVmId, srId, jobId) | ||
await backup.run() | ||
const xapi = sourceVm.$xapi | ||
const ref = sourceVm.$ref | ||
|
||
// stop the source VM before | ||
try { | ||
await xapi.callAsync('VM.clean_shutdown', ref) | ||
} catch (error) { | ||
await xapi.callAsync('VM.hard_shutdown', ref) | ||
} | ||
// make it so it can't be restarted by error | ||
const message = | ||
'This VM has been migrated somewhere else and might not be up to date, check twice before starting it.' | ||
await sourceVm.update_blocked_operations({ | ||
start: message, | ||
start_on: message, | ||
}) | ||
|
||
// run the transfer again to transfer the changed parts | ||
// since the source is stopped, there won't be any new change after | ||
backup = this.#createWarmBackup(sourceVmId, srId) | ||
await backup.run() | ||
// find the destination Vm | ||
const targets = Object.keys( | ||
app.getObjects({ | ||
filter: obj => { | ||
return ( | ||
'other' in obj && | ||
obj.other['xo:backup:job'] === jobId && | ||
obj.other['xo:backup:sr'] === srId && | ||
obj.other['xo:backup:vm'] === sourceVm.uuid && | ||
'start' in obj.blockedOperations | ||
) | ||
}, | ||
}) | ||
) | ||
if (targets.length === 0) { | ||
throw new Error(`Vm target of warm migration not found for ${sourceVmId} on SR ${srId} `) | ||
} | ||
if (targets.length > 1) { | ||
throw new Error(`Multiple target of warm migration found for ${sourceVmId} on SR ${srId} `) | ||
} | ||
const targetVm = app.getXapiObject(targets[0]) | ||
|
||
// new vm is ready to start | ||
// delta replication writer as set this as blocked | ||
await targetVm.update_blocked_operations({ start: null, start_on: null }) | ||
|
||
if (startDestVm) { | ||
// boot it | ||
await targetVm.$xapi.startVm(targetVm.$ref) | ||
// wait for really started | ||
// delete source | ||
if (deleteSource) { | ||
sourceVm.$xapi.VM_destroy(sourceVm.$ref) | ||
} else { | ||
// @todo should we delete the snapshot if we keep the source vm ? | ||
} | ||
} | ||
} | ||
} |
This change causes
xo-server
to fail --