Merge "hyper-v: wait for neutron vif plug events"
This commit is contained in:
@@ -62,7 +62,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
super(VMOpsTestCase, self).setUp()
|
||||
self.context = 'fake-context'
|
||||
|
||||
self._vmops = vmops.VMOps()
|
||||
self._vmops = vmops.VMOps(virtapi=mock.MagicMock())
|
||||
self._vmops._vmutils = mock.MagicMock()
|
||||
self._vmops._metricsutils = mock.MagicMock()
|
||||
self._vmops._vhdutils = mock.MagicMock()
|
||||
@@ -476,6 +476,53 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
||||
mock.sentinel.INFO, mock.sentinel.DEV_INFO)
|
||||
|
||||
@mock.patch.object(vmops.VMOps, '_get_neutron_events')
|
||||
def test_wait_vif_plug_events(self, mock_get_events):
|
||||
self._vmops._virtapi.wait_for_instance_event.side_effect = (
|
||||
etimeout.Timeout)
|
||||
self.flags(vif_plugging_timeout=1)
|
||||
self.flags(vif_plugging_is_fatal=True)
|
||||
|
||||
def _context_user():
|
||||
with self._vmops.wait_vif_plug_events(mock.sentinel.instance,
|
||||
mock.sentinel.network_info):
|
||||
pass
|
||||
|
||||
self.assertRaises(exception.VirtualInterfaceCreateException,
|
||||
_context_user)
|
||||
|
||||
mock_get_events.assert_called_once_with(mock.sentinel.network_info)
|
||||
self._vmops._virtapi.wait_for_instance_event.assert_called_once_with(
|
||||
mock.sentinel.instance, mock_get_events.return_value,
|
||||
deadline=CONF.vif_plugging_timeout,
|
||||
error_callback=self._vmops._neutron_failed_callback)
|
||||
|
||||
def test_neutron_failed_callback(self):
|
||||
self.flags(vif_plugging_is_fatal=True)
|
||||
self.assertRaises(exception.VirtualInterfaceCreateException,
|
||||
self._vmops._neutron_failed_callback,
|
||||
mock.sentinel.event_name, mock.sentinel.instance)
|
||||
|
||||
@mock.patch.object(vmops.utils, 'is_neutron')
|
||||
def test_get_neutron_events(self, mock_is_neutron):
|
||||
network_info = [{'id': mock.sentinel.vif_id1, 'active': True},
|
||||
{'id': mock.sentinel.vif_id2, 'active': False},
|
||||
{'id': mock.sentinel.vif_id3}]
|
||||
|
||||
events = self._vmops._get_neutron_events(network_info)
|
||||
self.assertEqual([('network-vif-plugged', mock.sentinel.vif_id2)],
|
||||
events)
|
||||
mock_is_neutron.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(vmops.utils, 'is_neutron')
|
||||
def test_get_neutron_events_no_timeout(self, mock_is_neutron):
|
||||
self.flags(vif_plugging_timeout=0)
|
||||
network_info = [{'id': mock.sentinel.vif_id1, 'active': True}]
|
||||
|
||||
events = self._vmops._get_neutron_events(network_info)
|
||||
self.assertEqual([], events)
|
||||
mock_is_neutron.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(vmops.VMOps, '_requires_secure_boot')
|
||||
@mock.patch.object(vmops.VMOps, '_requires_certificate')
|
||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
||||
|
||||
@@ -110,7 +110,7 @@ class HyperVDriver(driver.ComputeDriver):
|
||||
|
||||
self._hostops = hostops.HostOps()
|
||||
self._volumeops = volumeops.VolumeOps()
|
||||
self._vmops = vmops.VMOps()
|
||||
self._vmops = vmops.VMOps(virtapi)
|
||||
self._snapshotops = snapshotops.SnapshotOps()
|
||||
self._livemigrationops = livemigrationops.LiveMigrationOps()
|
||||
self._migrationops = migrationops.MigrationOps()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"""
|
||||
Management class for basic VM operations.
|
||||
"""
|
||||
import contextlib
|
||||
import functools
|
||||
import os
|
||||
import time
|
||||
@@ -98,7 +99,8 @@ class VMOps(object):
|
||||
_MAX_CONSOLE_LOG_FILE_SIZE = units.Mi / 2
|
||||
_ROOT_DISK_CTRL_ADDR = 0
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, virtapi=None):
|
||||
self._virtapi = virtapi
|
||||
self._vmutils = utilsfactory.get_vmutils()
|
||||
self._metricsutils = utilsfactory.get_metricsutils()
|
||||
self._vhdutils = utilsfactory.get_vhdutils()
|
||||
@@ -291,8 +293,11 @@ class VMOps(object):
|
||||
self._create_ephemerals(instance, block_device_info['ephemerals'])
|
||||
|
||||
try:
|
||||
self.create_instance(instance, network_info, root_device,
|
||||
block_device_info, vm_gen, image_meta)
|
||||
with self.wait_vif_plug_events(instance, network_info):
|
||||
# waiting will occur after the instance is created.
|
||||
self.create_instance(instance, network_info, root_device,
|
||||
block_device_info, vm_gen, image_meta)
|
||||
|
||||
self._save_device_metadata(context, instance, block_device_info)
|
||||
|
||||
if configdrive.required_by(instance):
|
||||
@@ -309,6 +314,41 @@ class VMOps(object):
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.destroy(instance)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wait_vif_plug_events(self, instance, network_info):
|
||||
timeout = CONF.vif_plugging_timeout
|
||||
events = self._get_neutron_events(network_info)
|
||||
|
||||
try:
|
||||
with self._virtapi.wait_for_instance_event(
|
||||
instance, events, deadline=timeout,
|
||||
error_callback=self._neutron_failed_callback):
|
||||
yield
|
||||
except etimeout.Timeout:
|
||||
# We never heard from Neutron
|
||||
LOG.warning(_LW('Timeout waiting for vif plugging callback for '
|
||||
'instance.'), instance=instance)
|
||||
if CONF.vif_plugging_is_fatal:
|
||||
raise exception.VirtualInterfaceCreateException()
|
||||
|
||||
def _neutron_failed_callback(self, event_name, instance):
|
||||
LOG.error(_LE('Neutron Reported failure on event %s'),
|
||||
event_name, instance=instance)
|
||||
if CONF.vif_plugging_is_fatal:
|
||||
raise exception.VirtualInterfaceCreateException()
|
||||
|
||||
def _get_neutron_events(self, network_info):
|
||||
# NOTE(danms): We need to collect any VIFs that are currently
|
||||
# down that we expect a down->up event for. Anything that is
|
||||
# already up will not undergo that transition, and for
|
||||
# anything that might be stale (cache-wise) assume it's
|
||||
# already up so we don't block on it.
|
||||
if utils.is_neutron() and CONF.vif_plugging_timeout:
|
||||
return [('network-vif-plugged', vif['id'])
|
||||
for vif in network_info if vif.get('active') is False]
|
||||
else:
|
||||
return []
|
||||
|
||||
def create_instance(self, instance, network_info, root_device,
|
||||
block_device_info, vm_gen, image_meta):
|
||||
instance_name = instance.name
|
||||
|
||||
Reference in New Issue
Block a user