Live Migration RPC Calls
Let's consider the two hosts called:
- src (where inst runs)
- dest
The user call maps to:
- scheduler rpc: live_migration(block_migration, disk_over_commit, instance_id, dest)
Scheduler driver does the following:
- check instance exists
- check source alive
- check destination alive, and has enough memory
- check source + destination hypervisor type match and dest is same or newer version than src
- compute: check_can_live_migrate_on_dest (on dest) *TODO*
- updates instance db entry to "migrating"
- compute: live_migration (on src)
Compute:check_can_live_migrate_on_destination does the following (on dest): *TODO*
- calls compute_driver check_can_live_migration_on_dest
- which can call the delegate that calls check_can_live_migrate_on_src
Compute:check_can_live_migrate_on_src does the following (on src): *TODO*
- takes a dictionary from dest
- possibly throws an exception if there is an issue
Compute: live_migration does the following (on src):
- check_for_export with volume
- calls pre_live_migration on dest
- does rollback on exception
- calls driver
Compute_driver: live_migration (on src)
- on success calls manager's post_live_migration
- on failure calls manager's rollback_live_migration
Compute: post_live_migration (on src):
- updates floating ips
- deletes old traces of image
- calls post_live_migration_at_destination on dest
Compute: rollback_live_migration (on src)
- updates DB
- sorts out volumes and networks
- on block migration, calls rollback_live_migration_at_destination on dest
Compute: post_live_migration_at_destination (on dest)
- sets up networking
- sets task as complete
Compute: rollback_live_migration_at_destination (on dest)
- does some clean up
A. nova/api/openstack/compute/contrib/admin_actions.py:定义了若干管理员权限运行的管理虚拟机的操作
_migrate_live()函数:虚拟机动态迁移在Opentack Nova中的API请求处理函数
@wsgi.action('os-migrateLive')
def _migrate_live(self, req, id, body):
"""Permit admins to (live) migrate a server to a new host."""
context = req.environ["nova.context"]
authorize(context, 'migrateLive')
try:
block_migration = body["os-migrateLive"]["block_migration"]
disk_over_commit = body["os-migrateLive"]["disk_over_commit"]
host = body["os-migrateLive"]["host"]
except (TypeError, KeyError):
msg = _("host, block_migration and disk_over_commit must "
"be specified for live migration.")
raise exc.HTTPBadRequest(explanation=msg)
try:
instance = self.compute_api.get(context, id, want_objects=True)
self.compute_api.live_migrate(context, instance, block_migration,
disk_over_commit, host) #转B
except (exception.ComputeServiceUnavailable,
exception.InvalidHypervisorType,
exception.UnableToMigrateToSelf,
exception.DestinationHypervisorTooOld) as ex:
raise exc.HTTPBadRequest(explanation=ex.format_message())
except Exception:
if host is None:
msg = _("Live migration of instance %s to another host "
"failed") % id
else:
msg = _("Live migration of instance %(id)s to host %(host)s "
"failed") % {'id': id, 'host': host}
LOG.exception(msg)
# Return messages from scheduler
raise exc.HTTPBadRequest(explanation=msg)
return webob.Response(status_int=202)
B. nova/compute/api.py: 注意,此处在live_migrate返回之后,还调用check_instance_state对迁移后的vm进行状态检查
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE])
def live_migrate(self, context, instance, block_migration,
disk_over_commit, host_name):
"""Migrate a server lively to a new host."""
LOG.debug(_("Going to try to live migrate instance to %s"),
host_name or "another host", instance=instance)
instance.task_state = task_states.MIGRATING
instance.save(expected_task_state=None)
self.compute_task_api.live_migrate_instance(context, instance, #转C
host_name, block_migration=block_migration,
disk_over_commit=disk_over_commit)
C.nova/conductor/api.py: class ComputeTaskAPI()
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit):
scheduler_hint = {'host': host_name}
self.conductor_compute_rpcapi.migrate_server( #转D
context, instance, scheduler_hint, True, False, None,
block_migration, disk_over_commit, None)
D. nova/conductor/rpcapi.py: migrate_server()
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit,
reservations=None):
if self.client.can_send_version('1.6'):
version = '1.6'
else:
instance = jsonutils.to_primitive(
objects_base.obj_to_primitive(instance))
version = '1.4'
flavor_p = jsonutils.to_primitive(flavor)
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server', #转E
instance=instance, scheduler_hint=scheduler_hint,
live=live, rebuild=rebuild, flavor=flavor_p,
block_migration=block_migration,
disk_over_c