CrossPoolMigrationv3
This page describes a possible design for Cross-pool migration (which also works for within-pool migration with and without shared storage).
This design has the following features:
- disks are replicated between the two sites/SRs using a "replication service" which is aware of the underlying disk structure (e.g. in the case of .vhd it can use the sparseness information to speed up the copying)
- the mirror is made synchronous by using the *tapdisk* "mirror" plugin (the same as used by the existing disk caching feature)
- the pool-level VM metadata is export/imported by xapi
- the domain-level VM metadata is export/imported by xenopsd
See the following diagram:
Advantages
This design has the following advantages:
- by separating the act of mirroring the disks (like a storage array would do) from the act of copying a running memory image, we don't need to hack libxenguest. There is a clean division of responsibility between managing storage and managing running VMs.
- by creating a synchronous mirror, we don't increase the migration blackout time
- we can re-use the disk replication service to do efficient cross-site backup/restore (ie to make periodic VM snapshot and archive use an incremental archive)
Proposed APIs
The following APIs are proposed:
- VM.migrate_receive(Host host, SR sr, Map(String,String) options): Map(String, String)
- host: the host to move the running VM to
- sr: the SR to replicate the VM's disks to
- options: for future advanced options
- the return value should be considered an abstract token, identifying the receiver. The token should be passed to the transmitter.
- VM.migrate(VM vm, Map(String, String) dest, Bool live, Map(String, String) options)
- vm: the running VM to migrate
- dest: the result of a previous VM.migrate_receive call
- live: if true this is a "live" migration
- options: for future advanced options
A client will first authenticate to the receiver and call "VM.migrate_receive". The client will then authenticate to the sender and call "VM.migrate", passing along the data provided by the receiving pool. If used this way then only the client has credentials for both pools. The receiving pool should take care to only include data in the "dest" result that can be used for a migrate; it should not include (for example) username and password information or a session_id. This avoids granting the transmitting pool too much access to the receiving pool.
Usage
A client wishing to perform a storage migration could do something like this:
# 1. Log into the remote pool remote = xmlrpclib.Server("https://my.remote.pool/") remote_session_id = remote.session.login_with_password("root", "password")["Value"] # 2. Decide where you want the VM to go remote_sr = remote.SR.get_by_name_label(remote_session_id, "my favourite storage")["Value"] remote_host = remote.Host.get_by_name_label(remote_session_id, "my favourite host")["Value"] # 3. Generate a token representing this destination dest = remote.VM.migrate_receive(remote_session_id, host, sr, {})["Value"] # Note: We don't log out of the remote session because it would invalidate 'dest' # 4. Log into the local pool local = xmlrpclib.Server("https://my.local.pool/") local_session_id = local.session.login_with_password("root", "password")["Value"] # 5. Decide which VM we want to move local_vm = local.VM.get_by_name_label(local_session_id, "my lovely VM")["Value"] # 6. Migrate the VM: task = local.Async.VM.migrate(local_session_id, vm, dest, true, {})["Value"] # Monitor the task for success/failure # 7. Clean up remote.session.logout(remote_session_id) local.session.logout(local_session_id)