summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-01-04 01:07:15 +0000
committerGerrit Code Review <review@openstack.org>2013-01-04 01:07:15 +0000
commit3ff2b563c0f5aa39dbb424c5be27072314646558 (patch)
tree3da9e5f51a4620807d86e86f9a6fe128ef8d41f6
parente4d3ca6c5a8630bd3d848d0af2b375355847a4f1 (diff)
parent21ea2e6109831156592dd1e4f4f4caefdcedd04f (diff)
downloadnova-3ff2b563c0f5aa39dbb424c5be27072314646558.tar.gz
nova-3ff2b563c0f5aa39dbb424c5be27072314646558.tar.xz
nova-3ff2b563c0f5aa39dbb424c5be27072314646558.zip
Merge "xenapi: Avoid hotplugging volumes on resize."
-rw-r--r--nova/tests/test_xenapi.py2
-rw-r--r--nova/tests/virt/xenapi/test_volumeops.py75
-rw-r--r--nova/virt/xenapi/driver.py16
-rw-r--r--nova/virt/xenapi/fake.py7
-rw-r--r--nova/virt/xenapi/vmops.py23
-rw-r--r--nova/virt/xenapi/volumeops.py24
6 files changed, 119 insertions, 28 deletions
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index f2799b8f3..3ca69dc4c 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -911,7 +911,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
def __init__(self):
self.finish_revert_migration_called = False
- def finish_revert_migration(self, instance):
+ def finish_revert_migration(self, instance, block_info):
self.finish_revert_migration_called = True
conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False)
diff --git a/nova/tests/virt/xenapi/test_volumeops.py b/nova/tests/virt/xenapi/test_volumeops.py
index bc84386c4..5b5c38139 100644
--- a/nova/tests/virt/xenapi/test_volumeops.py
+++ b/nova/tests/virt/xenapi/test_volumeops.py
@@ -15,11 +15,12 @@
# under the License.
from nova import test
+from nova.tests.xenapi import stubs
from nova.virt.xenapi import volumeops
class VolumeAttachTestCase(test.TestCase):
- def test_connect_volume_call(self):
+ def test_attach_volume_call(self):
ops = volumeops.VolumeOps('session')
self.mox.StubOutWithMock(ops, 'connect_volume')
self.mox.StubOutWithMock(volumeops.vm_utils, 'vm_ref_or_raise')
@@ -31,9 +32,79 @@ class VolumeAttachTestCase(test.TestCase):
volumeops.volume_utils.get_device_number('mountpoint').AndReturn(
'devnumber')
- ops.connect_volume('conn_data', 'devnumber', 'instance_1', 'vmref')
+ ops.connect_volume(
+ 'conn_data', 'devnumber', 'instance_1', 'vmref', hotplug=True)
self.mox.ReplayAll()
ops.attach_volume(
dict(driver_volume_type='iscsi', data='conn_data'),
'instance_1', 'mountpoint')
+
+ def test_attach_volume_no_hotplug(self):
+ ops = volumeops.VolumeOps('session')
+ self.mox.StubOutWithMock(ops, 'connect_volume')
+ self.mox.StubOutWithMock(volumeops.vm_utils, 'vm_ref_or_raise')
+ self.mox.StubOutWithMock(volumeops.volume_utils, 'get_device_number')
+
+ volumeops.vm_utils.vm_ref_or_raise('session', 'instance_1').AndReturn(
+ 'vmref')
+
+ volumeops.volume_utils.get_device_number('mountpoint').AndReturn(
+ 'devnumber')
+
+ ops.connect_volume(
+ 'conn_data', 'devnumber', 'instance_1', 'vmref', hotplug=False)
+
+ self.mox.ReplayAll()
+ ops.attach_volume(
+ dict(driver_volume_type='iscsi', data='conn_data'),
+ 'instance_1', 'mountpoint', hotplug=False)
+
+ def test_connect_volume_no_hotplug(self):
+ session = stubs.FakeSessionForVolumeTests('fake_uri')
+ ops = volumeops.VolumeOps(session)
+ instance_name = 'instance_1'
+ sr_uuid = '1'
+ sr_label = 'Disk-for:%s' % instance_name
+ sr_params = ''
+ sr_ref = 'sr_ref'
+ vdi_uuid = '2'
+ vdi_ref = 'vdi_ref'
+ vbd_ref = 'vbd_ref'
+ connection_data = {'vdi_uuid': vdi_uuid}
+ vm_ref = 'vm_ref'
+ dev_number = 1
+
+ called = {'xenapi': False}
+
+ def fake_call_xenapi(self, *args, **kwargs):
+ # Only used for VBD.plug in this code path.
+ called['xenapi'] = True
+ raise Exception()
+
+ self.stubs.Set(ops._session, 'call_xenapi', fake_call_xenapi)
+
+ self.mox.StubOutWithMock(volumeops.volume_utils, 'parse_sr_info')
+ self.mox.StubOutWithMock(ops, 'introduce_sr')
+ self.mox.StubOutWithMock(volumeops.volume_utils, 'introduce_vdi')
+ self.mox.StubOutWithMock(volumeops.vm_utils, 'create_vbd')
+
+ volumeops.volume_utils.parse_sr_info(
+ connection_data, sr_label).AndReturn(
+ tuple([sr_uuid, sr_label, sr_params]))
+
+ ops.introduce_sr(sr_uuid, sr_label, sr_params).AndReturn(sr_ref)
+
+ volumeops.volume_utils.introduce_vdi(
+ session, sr_ref, vdi_uuid, None).AndReturn(vdi_ref)
+
+ volumeops.vm_utils.create_vbd(
+ session, vm_ref, vdi_ref, dev_number,
+ bootable=False, osvol=True).AndReturn(vbd_ref)
+
+ self.mox.ReplayAll()
+
+ ops.connect_volume(connection_data, dev_number, instance_name,
+ vm_ref, hotplug=False)
+
+ self.assertEquals(False, called['xenapi'])
diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py
index 7bf27b7e0..10bc99fef 100644
--- a/nova/virt/xenapi/driver.py
+++ b/nova/virt/xenapi/driver.py
@@ -178,25 +178,15 @@ class XenAPIDriver(driver.ComputeDriver):
block_device_info=None):
"""Finish reverting a resize, powering back on the instance"""
# NOTE(vish): Xen currently does not use network info.
- self._vmops.finish_revert_migration(instance)
- self._attach_mapped_block_devices(instance, block_device_info)
+ self._vmops.finish_revert_migration(instance, block_device_info)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False,
block_device_info=None):
"""Completes a resize, turning on the migrated instance"""
self._vmops.finish_migration(context, migration, instance, disk_info,
- network_info, image_meta, resize_instance)
- self._attach_mapped_block_devices(instance, block_device_info)
-
- def _attach_mapped_block_devices(self, instance, block_device_info):
- block_device_mapping = driver.block_device_info_get_mapping(
- block_device_info)
- for vol in block_device_mapping:
- connection_info = vol['connection_info']
- mount_device = vol['mount_device'].rpartition("/")[2]
- self.attach_volume(connection_info,
- instance['name'], mount_device)
+ network_info, image_meta, resize_instance,
+ block_device_info)
def snapshot(self, context, instance, image_id):
""" Create snapshot from a running VM instance """
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index 9af8a9f41..666e46754 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -727,6 +727,8 @@ class SessionBase(object):
return lambda *params: self._create(name, params)
elif self._is_destroy(name):
return lambda *params: self._destroy(name, params)
+ elif name == 'XenAPI':
+ return FakeXenAPI()
else:
return None
@@ -890,6 +892,11 @@ class SessionBase(object):
return result
+class FakeXenAPI(object):
+ def __init__(self):
+ self.Failure = Failure
+
+
# Based upon _Method from xmlrpclib.
class _Dispatcher:
def __init__(self, send, name):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 8d4687fe8..a96a90f0e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -39,11 +39,13 @@ from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova import utils
+from nova.virt import driver as virt_driver
from nova.virt import firewall
from nova.virt.xenapi import agent as xapi_agent
from nova.virt.xenapi import pool_states
from nova.virt.xenapi import vm_utils
from nova.virt.xenapi import volume_utils
+from nova.virt.xenapi import volumeops
LOG = logging.getLogger(__name__)
@@ -147,6 +149,7 @@ class VMOps(object):
self.compute_api = compute.API()
self._session = session
self._virtapi = virtapi
+ self._volumeops = volumeops.VolumeOps(self._session)
self.firewall_driver = firewall.load_driver(
DEFAULT_FIREWALL_DRIVER,
self._virtapi,
@@ -179,7 +182,20 @@ class VMOps(object):
vm_ref = vm_utils.lookup(self._session, name_label)
return self._destroy(instance, vm_ref, network_info)
- def finish_revert_migration(self, instance):
+ def _attach_mapped_block_devices(self, instance, block_device_info):
+ # We are attaching these volumes before start (no hotplugging)
+ # because some guests (windows) don't load PV drivers quickly
+ block_device_mapping = virt_driver.block_device_info_get_mapping(
+ block_device_info)
+ for vol in block_device_mapping:
+ connection_info = vol['connection_info']
+ mount_device = vol['mount_device'].rpartition("/")[2]
+ self._volumeops.attach_volume(connection_info,
+ instance['name'],
+ mount_device,
+ hotplug=False)
+
+ def finish_revert_migration(self, instance, block_device_info=None):
# NOTE(sirp): the original vm was suffixed with '-orig'; find it using
# the old suffix, remove the suffix, then power it back on.
name_label = self._get_orig_vm_name_label(instance)
@@ -190,6 +206,8 @@ class VMOps(object):
name_label = instance['name']
vm_utils.set_vm_name_label(self._session, vm_ref, name_label)
+ self._attach_mapped_block_devices(instance, block_device_info)
+
self._start(instance, vm_ref)
def finish_migration(self, context, migration, instance, disk_info,
@@ -221,6 +239,9 @@ class VMOps(object):
{'root': root_vdi},
disk_image_type, network_info, kernel_file,
ramdisk_file)
+
+ self._attach_mapped_block_devices(instance, block_device_info)
+
# 5. Start VM
self._start(instance, vm_ref=vm_ref)
self._update_instance_progress(context, instance,
diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py
index d17adeba6..056313478 100644
--- a/nova/virt/xenapi/volumeops.py
+++ b/nova/virt/xenapi/volumeops.py
@@ -105,7 +105,8 @@ class VolumeOps(object):
LOG.exception(exc)
raise exception.NovaException(_('Could not forget SR'))
- def attach_volume(self, connection_info, instance_name, mountpoint):
+ def attach_volume(self, connection_info, instance_name, mountpoint,
+ hotplug=True):
"""Attach volume storage to VM instance"""
vm_ref = vm_utils.vm_ref_or_raise(self._session, instance_name)
@@ -121,14 +122,14 @@ class VolumeOps(object):
connection_data = connection_info['data']
dev_number = volume_utils.get_device_number(mountpoint)
- self.connect_volume(
- connection_data, dev_number, instance_name, vm_ref)
+ self.connect_volume(connection_data, dev_number, instance_name,
+ vm_ref, hotplug=hotplug)
LOG.info(_('Mountpoint %(mountpoint)s attached to'
' instance %(instance_name)s') % locals())
def connect_volume(self, connection_data, dev_number, instance_name,
- vm_ref):
+ vm_ref, hotplug=True):
description = 'Disk-for:%s' % instance_name
uuid, label, sr_params = volume_utils.parse_sr_info(connection_data,
@@ -172,13 +173,14 @@ class VolumeOps(object):
raise Exception(_('Unable to use SR %(sr_ref)s for'
' instance %(instance_name)s') % locals())
- try:
- self._session.call_xenapi("VBD.plug", vbd_ref)
- except self._session.XenAPI.Failure, exc:
- LOG.exception(exc)
- self.forget_sr(uuid)
- raise Exception(_('Unable to attach volume to instance %s')
- % instance_name)
+ if hotplug:
+ try:
+ self._session.call_xenapi("VBD.plug", vbd_ref)
+ except self._session.XenAPI.Failure, exc:
+ LOG.exception(exc)
+ self.forget_sr(uuid)
+ raise Exception(_('Unable to attach volume to instance %s')
+ % instance_name)
def detach_volume(self, connection_info, instance_name, mountpoint):
"""Detach volume storage to VM instance"""