summaryrefslogtreecommitdiffstats
path: root/nova/virt
diff options
context:
space:
mode:
authorRenuka Apte <renuka.apte@citrix.com>2012-07-12 16:22:26 -0700
committerRenuka Apte <renuka.apte@citrix.com>2012-08-14 16:25:29 -0700
commita7d0934a97ead360ecb378de7e29dbea513a6b30 (patch)
tree93141a17267c124d032fb2ab3b75d7cdf92df9ac /nova/virt
parent0eb53c035ab290c9574c1388bde59f7d05f64efc (diff)
xenapi: Support live migration without pools
Implement blueprint xenapi-live-block-migration. Add ability to live migrate VMs to hosts that are not a part of a host aggregate (block migration). This requires XenServer 6.1/later, or XCP 1.6/later. As of this change, we still do not support instances with (nova/cinder) volumes attached. External kernels are also not supported. Change-Id: I5feb6756d78804aa37780a7d0cda1600f7060afe
Diffstat (limited to 'nova/virt')
-rw-r--r--nova/virt/driver.py5
-rw-r--r--nova/virt/fake.py6
-rw-r--r--nova/virt/libvirt/driver.py4
-rw-r--r--nova/virt/xenapi/driver.py25
-rw-r--r--nova/virt/xenapi/fake.py26
-rw-r--r--nova/virt/xenapi/vmops.py96
6 files changed, 136 insertions, 26 deletions
diff --git a/nova/virt/driver.py b/nova/virt/driver.py
index dafb83568..53b37ada6 100644
--- a/nova/virt/driver.py
+++ b/nova/virt/driver.py
@@ -337,7 +337,8 @@ class ComputeDriver(object):
raise NotImplementedError()
def live_migration(self, ctxt, instance_ref, dest,
- post_method, recover_method, block_migration=False):
+ post_method, recover_method, block_migration=False,
+ migrate_data=None):
"""Live migration of an instance to another host.
:params ctxt: security context
@@ -352,6 +353,8 @@ class ComputeDriver(object):
recovery method when any exception occurs.
expected nova.compute.manager.recover_live_migration.
:params block_migration: if true, migrate VM disk.
+ :params migrate_data: implementation specific params.
+
"""
raise NotImplementedError()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 823a6e310..2d4672876 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -257,12 +257,11 @@ class FakeDriver(driver.ComputeDriver):
raise NotImplementedError('This method is supported only by libvirt.')
def get_instance_disk_info(self, instance_name):
- """This method is supported only by libvirt."""
return
def live_migration(self, context, instance_ref, dest,
- post_method, recover_method, block_migration=False):
- """This method is supported only by libvirt."""
+ post_method, recover_method, block_migration=False,
+ migrate_data=None):
return
def finish_migration(self, context, migration, instance, disk_info,
@@ -274,7 +273,6 @@ class FakeDriver(driver.ComputeDriver):
def pre_live_migration(self, context, instance_ref, block_device_info,
network_info):
- """This method is supported only by libvirt."""
return
def unfilter_instance(self, instance_ref, network_info):
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index e521fca59..2ca5c2edf 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -2399,7 +2399,8 @@ class LibvirtDriver(driver.ComputeDriver):
self.firewall_driver.filter_defer_apply_off()
def live_migration(self, ctxt, instance_ref, dest,
- post_method, recover_method, block_migration=False):
+ post_method, recover_method, block_migration=False,
+ migrate_data=None):
"""Spawning live_migration operation for distributing high-load.
:params ctxt: security context
@@ -2415,6 +2416,7 @@ class LibvirtDriver(driver.ComputeDriver):
recovery method when any exception occurs.
expected nova.compute.manager.recover_live_migration.
:params block_migration: if true, do block migration.
+ :params migrate_data: implementation specific params
"""
diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py
index 6af06a283..2f472fc7b 100644
--- a/nova/virt/xenapi/driver.py
+++ b/nova/virt/xenapi/driver.py
@@ -430,8 +430,10 @@ class XenAPIDriver(driver.ComputeDriver):
:param disk_over_commit: if true, allow disk over commit
"""
- self._vmops.check_can_live_migrate_destination(ctxt, instance_ref,
- block_migration, disk_over_commit)
+ return self._vmops.check_can_live_migrate_destination(ctxt,
+ instance_ref,
+ block_migration,
+ disk_over_commit)
def check_can_live_migrate_destination_cleanup(self, ctxt,
dest_check_data):
@@ -452,11 +454,25 @@ class XenAPIDriver(driver.ComputeDriver):
:param context: security context
:param instance_ref: nova.db.sqlalchemy.models.Instance
:param dest_check_data: result of check_can_live_migrate_destination
+ includes the block_migration flag
"""
+ self._vmops.check_can_live_migrate_source(ctxt, instance_ref,
+ dest_check_data)
+
+ def get_instance_disk_info(self, instance_name):
+ """Used by libvirt for live migration. We rely on xenapi
+ checks to do this for us."""
+ pass
+
+ def pre_block_migration(self, ctxt, instance_ref, disk_info_json):
+ """Used by libvirt for live migration. We rely on xenapi
+ checks to do this for us. May be used in the future to
+ populate the vdi/vif maps"""
pass
def live_migration(self, ctxt, instance_ref, dest,
- post_method, recover_method, block_migration=False):
+ post_method, recover_method, block_migration=False,
+ migrate_data=None):
"""Performs the live migration of the specified instance.
:params ctxt: security context
@@ -471,9 +487,10 @@ class XenAPIDriver(driver.ComputeDriver):
recovery method when any exception occurs.
expected nova.compute.manager.recover_live_migration.
:params block_migration: if true, migrate VM disk.
+ :params migrate_data: implementation specific params
"""
self._vmops.live_migrate(ctxt, instance_ref, dest, post_method,
- recover_method, block_migration)
+ recover_method, block_migration, migrate_data)
def pre_live_migration(self, context, instance_ref, block_device_info,
network_info):
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index cbfa72413..0fcd30ebe 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -292,7 +292,8 @@ def _create_local_pif(host_ref):
'physical': True,
'VLAN': -1,
'device': 'fake0',
- 'host_uuid': host_ref})
+ 'host_uuid': host_ref,
+ 'network': ''})
return pif_ref
@@ -491,6 +492,10 @@ class SessionBase(object):
def VM_pool_migrate(self, _1, vm_ref, host_ref, options):
pass
+ def VM_migrate_send(self, vmref, migrate_data, islive, vdi_map,
+ vif_map, options):
+ pass
+
def VDI_remove_from_other_config(self, _1, vdi_ref, key):
db_ref = _db_content['VDI'][vdi_ref]
if not 'other_config' in db_ref:
@@ -599,6 +604,25 @@ class SessionBase(object):
def pool_set_name_label(self, session, pool_ref, name):
pass
+ def host_migrate_receive(self, session, destref, nwref, options):
+ # The dictionary below represents the true keys, as
+ # returned by a destination host, but fake values.
+ return {'xenops': 'http://localhost/services/xenops?'
+ 'session_id=OpaqueRef:81d00b97-b205-b34d-924e-6f9597854cc0',
+ 'host': 'OpaqueRef:5e4a3dd1-b71c-74ba-bbc6-58ee9ff6a889',
+ 'master': 'http://localhost/',
+ 'session_id': 'OpaqueRef:81d00b97-b205-b34d-924e-6f9597854cc0',
+ 'SM': 'http://localhost/services/SM?'
+ 'session_id=OpaqueRef:81d00b97-b205-b34d-924e-6f9597854cc0'}
+
+ def VM_assert_can_migrate(self, session, vmref, migrate_data, live,
+ vdi_map, vif_map, options):
+ pass
+
+ def VM_migrate_send(self, session, mref, migrate_data, live, vdi_map,
+ vif_map, options):
+ pass
+
def network_get_all_records_where(self, _1, filter):
return self.xenapi.network.get_all_records()
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index dbff55d1f..c74c19773 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -542,6 +542,7 @@ class VMOps(object):
'weight', str(vcpu_weight))
def _get_vm_opaque_ref(self, instance):
+ """Get xapi OpaqueRef from a db record."""
vm_ref = vm_utils.lookup(self._session, instance['name'])
if vm_ref is None:
raise exception.NotFound(_('Could not find VM with name %s') %
@@ -1482,6 +1483,30 @@ class VMOps(object):
host_uuid = self._get_host_uuid_from_aggregate(context, hostname)
return self._session.call_xenapi("host.get_by_uuid", host_uuid)
+ def _migrate_receive(self, ctxt):
+ destref = self._session.get_xenapi_host()
+ # Get the network to for migrate.
+ # This is the one associated with the pif marked management. From cli:
+ # uuid=`xe pif-list --minimal management=true`
+ # xe pif-param-get param-name=network-uuid uuid=$uuid
+ expr = 'field "management" = "true"'
+ pifs = self._session.call_xenapi('PIF.get_all_records_where',
+ expr)
+ if len(pifs) != 1:
+ raise exception.MigrationError('No suitable network for migrate')
+
+ nwref = pifs[pifs.keys()[0]]['network']
+ try:
+ options = {}
+ migrate_data = self._session.call_xenapi("host.migrate_receive",
+ destref,
+ nwref,
+ options)
+ except self._session.XenAPI.Failure as exc:
+ LOG.exception(exc)
+ raise exception.MigrationError(_('Migrate Receive failed'))
+ return migrate_data
+
def check_can_live_migrate_destination(self, ctxt, instance_ref,
block_migration=False,
disk_over_commit=False):
@@ -1494,30 +1519,71 @@ class VMOps(object):
"""
if block_migration:
- #TODO(johngarbutt): XenServer feature coming soon fixes this
- raise NotImplementedError()
+ migrate_data = self._migrate_receive(ctxt)
+ dest_check_data = {}
+ dest_check_data["block_migration"] = block_migration
+ dest_check_data["migrate_data"] = migrate_data
+ return dest_check_data
else:
src = instance_ref['host']
self._ensure_host_in_aggregate(ctxt, src)
# TODO(johngarbutt) we currently assume
# instance is on a SR shared with other destination
# block migration work will be able to resolve this
+ return None
- def live_migrate(self, context, instance, destination_hostname,
- post_method, recover_method, block_migration):
- if block_migration:
- #TODO(johngarbutt): see above
- raise NotImplementedError()
- else:
+ def check_can_live_migrate_source(self, ctxt, instance_ref,
+ dest_check_data):
+ """ Check if it is possible to execute live migration
+ on the source side.
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+ :param dest_check_data: data returned by the check on the
+ destination, includes block_migration flag
+
+ """
+ if dest_check_data and 'migrate_data' in dest_check_data:
+ vmref = self._get_vm_opaque_ref(instance_ref)
+ migrate_data = dest_check_data['migrate_data']
try:
- vm_ref = self._get_vm_opaque_ref(instance)
+ vdi_map = {}
+ vif_map = {}
+ options = {}
+ self._session.call_xenapi("VM.assert_can_migrate", vmref,
+ migrate_data, True, vdi_map, vif_map,
+ options)
+ except self._session.XenAPI.Failure as exc:
+ LOG.exception(exc)
+ raise exception.MigrationError(_('VM.assert_can_migrate'
+ 'failed'))
+
+ def live_migrate(self, context, instance, destination_hostname,
+ post_method, recover_method, block_migration,
+ migrate_data=None):
+ try:
+ vm_ref = self._get_vm_opaque_ref(instance)
+ if block_migration:
+ if not migrate_data:
+ raise exception.InvalidParameterValue('Block Migration '
+ 'requires migrate data from destination')
+ try:
+ vdi_map = {}
+ vif_map = {}
+ options = {}
+ self._session.call_xenapi("VM.migrate_send", vm_ref,
+ migrate_data, True,
+ vdi_map, vif_map, options)
+ except self._session.XenAPI.Failure as exc:
+ LOG.exception(exc)
+ raise exception.MigrationError(_('Migrate Send failed'))
+ else:
host_ref = self._get_host_opaque_ref(context,
destination_hostname)
self._session.call_xenapi("VM.pool_migrate", vm_ref,
host_ref, {})
- post_method(context, instance, destination_hostname,
- block_migration)
- except Exception:
- with excutils.save_and_reraise_exception():
- recover_method(context, instance, destination_hostname,
- block_migration)
+ post_method(context, instance, destination_hostname,
+ block_migration)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ recover_method(context, instance, destination_hostname,
+ block_migration)