Merge "Revert resize: wait for events according to hybrid plug" into stable/rocky
This commit is contained in:
commit
dbe35cc27a
|
@ -4144,6 +4144,49 @@ class ComputeManager(manager.Manager):
|
||||||
self.compute_rpcapi.finish_revert_resize(context, instance,
|
self.compute_rpcapi.finish_revert_resize(context, instance,
|
||||||
migration, migration.source_compute)
|
migration, migration.source_compute)
|
||||||
|
|
||||||
|
def _finish_revert_resize_network_migrate_finish(self, context, instance,
|
||||||
|
migration):
|
||||||
|
"""Causes port binding to be updated. In some Neutron or port
|
||||||
|
configurations - see NetworkModel.get_bind_time_events() - we
|
||||||
|
expect the vif-plugged event from Neutron immediately and wait for it.
|
||||||
|
The rest of the time, the event is expected further along in the
|
||||||
|
virt driver, so we don't wait here.
|
||||||
|
|
||||||
|
:param context: The request context.
|
||||||
|
:param instance: The instance undergoing the revert resize.
|
||||||
|
:param migration: The Migration object of the resize being reverted.
|
||||||
|
:raises: eventlet.timeout.Timeout or
|
||||||
|
exception.VirtualInterfacePlugException.
|
||||||
|
"""
|
||||||
|
network_info = instance.get_network_info()
|
||||||
|
events = []
|
||||||
|
deadline = CONF.vif_plugging_timeout
|
||||||
|
if deadline and utils.is_neutron() and network_info:
|
||||||
|
events = network_info.get_bind_time_events(migration)
|
||||||
|
if events:
|
||||||
|
LOG.debug('Will wait for bind-time events: %s', events)
|
||||||
|
error_cb = self._neutron_failed_migration_callback
|
||||||
|
try:
|
||||||
|
with self.virtapi.wait_for_instance_event(instance, events,
|
||||||
|
deadline=deadline,
|
||||||
|
error_callback=error_cb):
|
||||||
|
# NOTE(hanrong): we need to change migration.dest_compute to
|
||||||
|
# source host temporarily.
|
||||||
|
# "network_api.migrate_instance_finish" will setup the network
|
||||||
|
# for the instance on the destination host. For revert resize,
|
||||||
|
# the instance will back to the source host, the setup of the
|
||||||
|
# network for instance should be on the source host. So set
|
||||||
|
# the migration.dest_compute to source host at here.
|
||||||
|
with utils.temporary_mutation(
|
||||||
|
migration, dest_compute=migration.source_compute):
|
||||||
|
self.network_api.migrate_instance_finish(context,
|
||||||
|
instance,
|
||||||
|
migration)
|
||||||
|
except eventlet.timeout.Timeout:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error('Timeout waiting for Neutron events: %s', events,
|
||||||
|
instance=instance)
|
||||||
|
|
||||||
@wrap_exception()
|
@wrap_exception()
|
||||||
@reverts_task_state
|
@reverts_task_state
|
||||||
@wrap_instance_event(prefix='compute')
|
@wrap_instance_event(prefix='compute')
|
||||||
|
@ -4182,17 +4225,8 @@ class ComputeManager(manager.Manager):
|
||||||
|
|
||||||
self.network_api.setup_networks_on_host(context, instance,
|
self.network_api.setup_networks_on_host(context, instance,
|
||||||
migration.source_compute)
|
migration.source_compute)
|
||||||
# NOTE(hanrong): we need to change migration.dest_compute to
|
self._finish_revert_resize_network_migrate_finish(
|
||||||
# source host temporarily. "network_api.migrate_instance_finish"
|
context, instance, migration)
|
||||||
# will setup the network for the instance on the destination host.
|
|
||||||
# For revert resize, the instance will back to the source host, the
|
|
||||||
# setup of the network for instance should be on the source host.
|
|
||||||
# So set the migration.dest_compute to source host at here.
|
|
||||||
with utils.temporary_mutation(
|
|
||||||
migration, dest_compute=migration.source_compute):
|
|
||||||
self.network_api.migrate_instance_finish(context,
|
|
||||||
instance,
|
|
||||||
migration)
|
|
||||||
network_info = self.network_api.get_instance_nw_info(context,
|
network_info = self.network_api.get_instance_nw_info(context,
|
||||||
instance)
|
instance)
|
||||||
|
|
||||||
|
@ -6322,8 +6356,8 @@ class ComputeManager(manager.Manager):
|
||||||
return migrate_data
|
return migrate_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _neutron_failed_live_migration_callback(event_name, instance):
|
def _neutron_failed_migration_callback(event_name, instance):
|
||||||
msg = ('Neutron reported failure during live migration '
|
msg = ('Neutron reported failure during migration '
|
||||||
'with %(event)s for instance %(uuid)s')
|
'with %(event)s for instance %(uuid)s')
|
||||||
msg_args = {'event': event_name, 'uuid': instance.uuid}
|
msg_args = {'event': event_name, 'uuid': instance.uuid}
|
||||||
if CONF.vif_plugging_is_fatal:
|
if CONF.vif_plugging_is_fatal:
|
||||||
|
@ -6401,7 +6435,7 @@ class ComputeManager(manager.Manager):
|
||||||
disk = None
|
disk = None
|
||||||
|
|
||||||
deadline = CONF.vif_plugging_timeout
|
deadline = CONF.vif_plugging_timeout
|
||||||
error_cb = self._neutron_failed_live_migration_callback
|
error_cb = self._neutron_failed_migration_callback
|
||||||
# In order to avoid a race with the vif plugging that the virt
|
# In order to avoid a race with the vif plugging that the virt
|
||||||
# driver does on the destination host, we register our events
|
# driver does on the destination host, we register our events
|
||||||
# to wait for before calling pre_live_migration. Then if the
|
# to wait for before calling pre_live_migration. Then if the
|
||||||
|
|
|
@ -456,6 +456,17 @@ class VIF(Model):
|
||||||
'ips': ips}
|
'ips': ips}
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def has_bind_time_event(self, migration):
|
||||||
|
"""Returns whether this VIF's network-vif-plugged external event will
|
||||||
|
be sent by Neutron at "bind-time" - in other words, as soon as the port
|
||||||
|
binding is updated. This is in the context of updating the port binding
|
||||||
|
to a host that already has the instance in a shutoff state - in
|
||||||
|
practice, this means reverting either a cold migration or a
|
||||||
|
non-same-host resize.
|
||||||
|
"""
|
||||||
|
return (self.is_hybrid_plug_enabled() and not
|
||||||
|
migration.is_same_host())
|
||||||
|
|
||||||
def is_hybrid_plug_enabled(self):
|
def is_hybrid_plug_enabled(self):
|
||||||
return self['details'].get(VIF_DETAILS_OVS_HYBRID_PLUG, False)
|
return self['details'].get(VIF_DETAILS_OVS_HYBRID_PLUG, False)
|
||||||
|
|
||||||
|
@ -513,6 +524,20 @@ class NetworkInfo(list):
|
||||||
def json(self):
|
def json(self):
|
||||||
return jsonutils.dumps(self)
|
return jsonutils.dumps(self)
|
||||||
|
|
||||||
|
def get_bind_time_events(self, migration):
|
||||||
|
"""Returns whether any of our VIFs have "bind-time" events. See
|
||||||
|
has_bind_time_event() docstring for more details.
|
||||||
|
"""
|
||||||
|
return [('network-vif-plugged', vif['id'])
|
||||||
|
for vif in self if vif.has_bind_time_event(migration)]
|
||||||
|
|
||||||
|
def get_plug_time_events(self, migration):
|
||||||
|
"""Complementary to get_bind_time_events(), any event that does not
|
||||||
|
fall in that category is a plug-time event.
|
||||||
|
"""
|
||||||
|
return [('network-vif-plugged', vif['id'])
|
||||||
|
for vif in self if not vif.has_bind_time_event(migration)]
|
||||||
|
|
||||||
|
|
||||||
class NetworkInfoAsyncWrapper(NetworkInfo):
|
class NetworkInfoAsyncWrapper(NetworkInfo):
|
||||||
"""Wrapper around NetworkInfo that allows retrieving NetworkInfo
|
"""Wrapper around NetworkInfo that allows retrieving NetworkInfo
|
||||||
|
|
|
@ -176,6 +176,9 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
|
||||||
def instance(self, instance):
|
def instance(self, instance):
|
||||||
self._cached_instance = instance
|
self._cached_instance = instance
|
||||||
|
|
||||||
|
def is_same_host(self):
|
||||||
|
return self.source_compute == self.dest_compute
|
||||||
|
|
||||||
|
|
||||||
@base.NovaObjectRegistry.register
|
@base.NovaObjectRegistry.register
|
||||||
class MigrationList(base.ObjectListBase, base.NovaObject):
|
class MigrationList(base.ObjectListBase, base.NovaObject):
|
||||||
|
|
|
@ -5941,7 +5941,9 @@ class ComputeTestCase(BaseTestCase,
|
||||||
old_vm_state = vm_states.ACTIVE
|
old_vm_state = vm_states.ACTIVE
|
||||||
else:
|
else:
|
||||||
old_vm_state = vm_states.STOPPED
|
old_vm_state = vm_states.STOPPED
|
||||||
params = {'vm_state': old_vm_state}
|
params = {'vm_state': old_vm_state,
|
||||||
|
'info_cache': objects.InstanceInfoCache(
|
||||||
|
network_info=network_model.NetworkInfo([]))}
|
||||||
instance = self._create_fake_instance_obj(params)
|
instance = self._create_fake_instance_obj(params)
|
||||||
|
|
||||||
self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
|
self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
|
||||||
|
@ -6091,7 +6093,9 @@ class ComputeTestCase(BaseTestCase,
|
||||||
def fake(*args, **kwargs):
|
def fake(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
instance = self._create_fake_instance_obj()
|
params = {'info_cache': objects.InstanceInfoCache(
|
||||||
|
network_info=network_model.NetworkInfo([]))}
|
||||||
|
instance = self._create_fake_instance_obj(params)
|
||||||
|
|
||||||
self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
|
self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
|
||||||
self.stub_out('nova.virt.fake.FakeDriver.finish_revert_migration',
|
self.stub_out('nova.virt.fake.FakeDriver.finish_revert_migration',
|
||||||
|
|
|
@ -4936,6 +4936,97 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
|
||||||
self.context, fake_instance, fake_bdm)
|
self.context, fake_instance, fake_bdm)
|
||||||
block_stats.assert_called_once_with(fake_instance, 'vda')
|
block_stats.assert_called_once_with(fake_instance, 'vda')
|
||||||
|
|
||||||
|
def _test_finish_revert_resize_network_migrate_finish(
|
||||||
|
self, vifs, events, migration=None):
|
||||||
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
instance.info_cache = objects.InstanceInfoCache(
|
||||||
|
network_info=network_model.NetworkInfo(vifs))
|
||||||
|
if migration is None:
|
||||||
|
migration = objects.Migration(
|
||||||
|
source_compute='fake-source',
|
||||||
|
dest_compute='fake-dest')
|
||||||
|
|
||||||
|
def fake_migrate_instance_finish(context, instance, migration):
|
||||||
|
# NOTE(artom) This looks weird, but it's checking that the
|
||||||
|
# temporaty_mutation() context manager did its job.
|
||||||
|
self.assertEqual(migration.dest_compute, migration.source_compute)
|
||||||
|
|
||||||
|
with test.nested(
|
||||||
|
mock.patch.object(self.compute.virtapi,
|
||||||
|
'wait_for_instance_event'),
|
||||||
|
mock.patch.object(self.compute.network_api,
|
||||||
|
'migrate_instance_finish',
|
||||||
|
side_effect=fake_migrate_instance_finish)
|
||||||
|
) as (mock_wait, mock_migrate_instance_finish):
|
||||||
|
self.compute._finish_revert_resize_network_migrate_finish(
|
||||||
|
self.context, instance, migration)
|
||||||
|
mock_wait.assert_called_once_with(
|
||||||
|
instance, events, deadline=CONF.vif_plugging_timeout,
|
||||||
|
error_callback=self.compute._neutron_failed_migration_callback)
|
||||||
|
mock_migrate_instance_finish.assert_called_once_with(
|
||||||
|
self.context, instance, migration)
|
||||||
|
|
||||||
|
def test_finish_revert_resize_network_migrate_finish_wait(self):
|
||||||
|
"""Test that we wait for bind-time events if we have a hybrid-plugged
|
||||||
|
VIF.
|
||||||
|
"""
|
||||||
|
self._test_finish_revert_resize_network_migrate_finish(
|
||||||
|
[network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True}),
|
||||||
|
network_model.VIF(id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': False})],
|
||||||
|
[('network-vif-plugged', uuids.hybrid_vif)])
|
||||||
|
|
||||||
|
def test_finish_revert_resize_network_migrate_finish_same_host(self):
|
||||||
|
"""Test that we're not waiting for any events if its a same host
|
||||||
|
resize revert.
|
||||||
|
"""
|
||||||
|
migration = objects.Migration(
|
||||||
|
source_compute='fake-source', dest_compute='fake-source')
|
||||||
|
|
||||||
|
self._test_finish_revert_resize_network_migrate_finish(
|
||||||
|
[network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True}),
|
||||||
|
network_model.VIF(id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': False})],
|
||||||
|
[], migration=migration
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_finish_revert_resize_network_migrate_finish_dont_wait(self):
|
||||||
|
"""Test that we're not waiting for any events if we don't have any
|
||||||
|
hybrid-plugged VIFs.
|
||||||
|
"""
|
||||||
|
self._test_finish_revert_resize_network_migrate_finish(
|
||||||
|
[network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': False}),
|
||||||
|
network_model.VIF(id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': False})],
|
||||||
|
[])
|
||||||
|
|
||||||
|
def test_finish_revert_resize_network_migrate_finish_no_vif_timeout(self):
|
||||||
|
"""Test that we're not waiting for any events if vif_plugging_timeout
|
||||||
|
is 0.
|
||||||
|
"""
|
||||||
|
self.flags(vif_plugging_timeout=0)
|
||||||
|
self._test_finish_revert_resize_network_migrate_finish(
|
||||||
|
[network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True}),
|
||||||
|
network_model.VIF(id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': True})],
|
||||||
|
[])
|
||||||
|
|
||||||
|
@mock.patch.object(utils, 'is_neutron', return_value=False)
|
||||||
|
def test_finish_revert_resize_network_migrate_finish_not_neutron(self, _):
|
||||||
|
"""Test that we're not waiting for any events if we're not using
|
||||||
|
Neutron.
|
||||||
|
"""
|
||||||
|
self._test_finish_revert_resize_network_migrate_finish(
|
||||||
|
[network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True}),
|
||||||
|
network_model.VIF(id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': True})],
|
||||||
|
[])
|
||||||
|
|
||||||
|
|
||||||
class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -18,8 +18,10 @@ from oslo_config import cfg
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.network import model
|
from nova.network import model
|
||||||
|
from nova import objects
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_network_cache_model
|
from nova.tests.unit import fake_network_cache_model
|
||||||
|
from nova.tests import uuidsentinel as uuids
|
||||||
from nova.virt import netutils
|
from nova.virt import netutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -857,6 +859,34 @@ iface eth1 inet static
|
||||||
libvirt_virt_type='lxc')
|
libvirt_virt_type='lxc')
|
||||||
self.assertEqual(expected, template)
|
self.assertEqual(expected, template)
|
||||||
|
|
||||||
|
def test_get_events(self):
|
||||||
|
network_info = model.NetworkInfo([
|
||||||
|
model.VIF(
|
||||||
|
id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True}),
|
||||||
|
model.VIF(
|
||||||
|
id=uuids.normal_vif,
|
||||||
|
details={'ovs_hybrid_plug': False})])
|
||||||
|
same_host = objects.Migration(source_compute='fake-host',
|
||||||
|
dest_compute='fake-host')
|
||||||
|
diff_host = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
# Same-host migrations will have all events be plug-time.
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[('network-vif-plugged', uuids.normal_vif),
|
||||||
|
('network-vif-plugged', uuids.hybrid_vif)],
|
||||||
|
network_info.get_plug_time_events(same_host))
|
||||||
|
# Same host migration will have no plug-time events.
|
||||||
|
self.assertEqual([], network_info.get_bind_time_events(same_host))
|
||||||
|
# Diff-host migration + OVS hybrid plug = bind-time events
|
||||||
|
self.assertEqual(
|
||||||
|
[('network-vif-plugged', uuids.hybrid_vif)],
|
||||||
|
network_info.get_bind_time_events(diff_host))
|
||||||
|
# Diff-host migration + normal OVS = plug-time events
|
||||||
|
self.assertEqual(
|
||||||
|
[('network-vif-plugged', uuids.normal_vif)],
|
||||||
|
network_info.get_plug_time_events(diff_host))
|
||||||
|
|
||||||
|
|
||||||
class TestNetworkMetadata(test.NoDBTestCase):
|
class TestNetworkMetadata(test.NoDBTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -276,6 +276,14 @@ class _TestMigrationObject(object):
|
||||||
mig = objects.Migration.get_by_id(self.context, db_mig.id)
|
mig = objects.Migration.get_by_id(self.context, db_mig.id)
|
||||||
self.assertEqual(uuid, mig.uuid)
|
self.assertEqual(uuid, mig.uuid)
|
||||||
|
|
||||||
|
def test_is_same_host(self):
|
||||||
|
same_host = objects.Migration(source_compute='fake-host',
|
||||||
|
dest_compute='fake-host')
|
||||||
|
diff_host = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
self.assertTrue(same_host.is_same_host())
|
||||||
|
self.assertFalse(diff_host.is_same_host())
|
||||||
|
|
||||||
|
|
||||||
class TestMigrationObject(test_objects._LocalTest,
|
class TestMigrationObject(test_objects._LocalTest,
|
||||||
_TestMigrationObject):
|
_TestMigrationObject):
|
||||||
|
|
|
@ -16916,8 +16916,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
self.assertEqual(0, domain.resume.call_count)
|
self.assertEqual(0, domain.resume.call_count)
|
||||||
|
|
||||||
def _test_create_with_network_events(self, neutron_failure=None,
|
def _test_create_with_network_events(self, neutron_failure=None,
|
||||||
power_on=True):
|
power_on=True, events=None):
|
||||||
generated_events = []
|
generated_events = []
|
||||||
|
events_passed_to_prepare = []
|
||||||
|
|
||||||
def wait_timeout():
|
def wait_timeout():
|
||||||
event = mock.MagicMock()
|
event = mock.MagicMock()
|
||||||
|
@ -16935,6 +16936,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
m.event_name = '%s-%s' % (name, tag)
|
m.event_name = '%s-%s' % (name, tag)
|
||||||
m.wait.side_effect = wait_timeout
|
m.wait.side_effect = wait_timeout
|
||||||
generated_events.append(m)
|
generated_events.append(m)
|
||||||
|
events_passed_to_prepare.append((name, tag))
|
||||||
return m
|
return m
|
||||||
|
|
||||||
virtapi = manager.ComputeVirtAPI(mock.MagicMock())
|
virtapi = manager.ComputeVirtAPI(mock.MagicMock())
|
||||||
|
@ -16954,7 +16956,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
def test_create(cleanup, create, fw_driver, plug_vifs):
|
def test_create(cleanup, create, fw_driver, plug_vifs):
|
||||||
domain = drvr._create_domain_and_network(self.context, 'xml',
|
domain = drvr._create_domain_and_network(self.context, 'xml',
|
||||||
instance, vifs,
|
instance, vifs,
|
||||||
power_on=power_on)
|
power_on=power_on,
|
||||||
|
external_events=events)
|
||||||
plug_vifs.assert_called_with(instance, vifs)
|
plug_vifs.assert_called_with(instance, vifs)
|
||||||
|
|
||||||
pause = self._get_pause_flag(drvr, vifs, power_on=power_on)
|
pause = self._get_pause_flag(drvr, vifs, power_on=power_on)
|
||||||
|
@ -16969,7 +16972,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
|
|
||||||
test_create()
|
test_create()
|
||||||
|
|
||||||
if utils.is_neutron() and CONF.vif_plugging_timeout and power_on:
|
if events and utils.is_neutron() and CONF.vif_plugging_timeout:
|
||||||
|
self.assertEqual(events_passed_to_prepare, events)
|
||||||
|
elif utils.is_neutron() and CONF.vif_plugging_timeout and power_on:
|
||||||
prepare.assert_has_calls([
|
prepare.assert_has_calls([
|
||||||
mock.call(instance, 'network-vif-plugged', uuids.vif_1),
|
mock.call(instance, 'network-vif-plugged', uuids.vif_1),
|
||||||
mock.call(instance, 'network-vif-plugged', uuids.vif_2)])
|
mock.call(instance, 'network-vif-plugged', uuids.vif_2)])
|
||||||
|
@ -16982,6 +16987,22 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
else:
|
else:
|
||||||
self.assertEqual(0, prepare.call_count)
|
self.assertEqual(0, prepare.call_count)
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=True))
|
||||||
|
def test_create_with_network_events_passed_in(self):
|
||||||
|
self._test_create_with_network_events(
|
||||||
|
events=[('network-vif-plugged', uuids.fake_vif)])
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=False))
|
||||||
|
def test_create_with_network_events_passed_in_nova_net(self):
|
||||||
|
self._test_create_with_network_events(
|
||||||
|
events=[('network-vif-plugged', uuids.fake_vif)])
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=True))
|
||||||
|
def test_create_with_network_events_passed_in_0_timeout(self):
|
||||||
|
self.flags(vif_plugging_timeout=0)
|
||||||
|
self._test_create_with_network_events(
|
||||||
|
events=[('network-vif-plugged', uuids.fake_vif)])
|
||||||
|
|
||||||
@mock.patch('nova.utils.is_neutron', return_value=True)
|
@mock.patch('nova.utils.is_neutron', return_value=True)
|
||||||
def test_create_with_network_events_neutron(self, is_neutron):
|
def test_create_with_network_events_neutron(self, is_neutron):
|
||||||
self._test_create_with_network_events()
|
self._test_create_with_network_events()
|
||||||
|
@ -18926,7 +18947,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
def test_finish_migration_power_off(self):
|
def test_finish_migration_power_off(self):
|
||||||
self._test_finish_migration(power_on=False)
|
self._test_finish_migration(power_on=False)
|
||||||
|
|
||||||
def _test_finish_revert_migration(self, power_on):
|
def _test_finish_revert_migration(self, power_on, migration):
|
||||||
"""Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
|
"""Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
|
||||||
.finish_revert_migration.
|
.finish_revert_migration.
|
||||||
"""
|
"""
|
||||||
|
@ -18942,10 +18963,13 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
|
|
||||||
def fake_create_domain(context, xml, instance, network_info,
|
def fake_create_domain(context, xml, instance, network_info,
|
||||||
block_device_info=None, power_on=None,
|
block_device_info=None, power_on=None,
|
||||||
vifs_already_plugged=None):
|
vifs_already_plugged=None,
|
||||||
|
external_events=None):
|
||||||
self.fake_create_domain_called = True
|
self.fake_create_domain_called = True
|
||||||
self.assertEqual(powered_on, power_on)
|
self.assertEqual(powered_on, power_on)
|
||||||
self.assertFalse(vifs_already_plugged)
|
self.assertFalse(vifs_already_plugged)
|
||||||
|
self.assertEqual(self.events_passed_to_fake_create,
|
||||||
|
external_events)
|
||||||
return mock.MagicMock()
|
return mock.MagicMock()
|
||||||
|
|
||||||
def fake_enable_hairpin():
|
def fake_enable_hairpin():
|
||||||
|
@ -18979,6 +19003,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
with utils.tempdir() as tmpdir:
|
with utils.tempdir() as tmpdir:
|
||||||
self.flags(instances_path=tmpdir)
|
self.flags(instances_path=tmpdir)
|
||||||
ins_ref = self._create_instance()
|
ins_ref = self._create_instance()
|
||||||
|
ins_ref.migration_context = objects.MigrationContext(
|
||||||
|
migration_id=migration.id)
|
||||||
os.mkdir(os.path.join(tmpdir, ins_ref['name']))
|
os.mkdir(os.path.join(tmpdir, ins_ref['name']))
|
||||||
libvirt_xml_path = os.path.join(tmpdir,
|
libvirt_xml_path = os.path.join(tmpdir,
|
||||||
ins_ref['name'],
|
ins_ref['name'],
|
||||||
|
@ -18986,16 +19012,50 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
f = open(libvirt_xml_path, 'w')
|
f = open(libvirt_xml_path, 'w')
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
self.drvr.finish_revert_migration(
|
network_info = network_model.NetworkInfo(
|
||||||
context.get_admin_context(), ins_ref,
|
[network_model.VIF(id=uuids.normal_vif),
|
||||||
[], None, power_on)
|
network_model.VIF(id=uuids.hybrid_vif,
|
||||||
|
details={'ovs_hybrid_plug': True})])
|
||||||
|
if migration.is_same_host():
|
||||||
|
# Same host is all plug-time
|
||||||
|
self.events_passed_to_fake_create = [
|
||||||
|
('network-vif-plugged', uuids.normal_vif),
|
||||||
|
('network-vif-plugged', uuids.hybrid_vif)]
|
||||||
|
else:
|
||||||
|
# For different host migration only non-hybrid plug
|
||||||
|
# ("normal") VIFs "emit" plug-time events.
|
||||||
|
self.events_passed_to_fake_create = [
|
||||||
|
('network-vif-plugged', uuids.normal_vif)]
|
||||||
|
|
||||||
|
with mock.patch.object(objects.Migration, 'get_by_id_and_instance',
|
||||||
|
return_value=migration) as mock_get_mig:
|
||||||
|
self.drvr.finish_revert_migration(
|
||||||
|
context.get_admin_context(), ins_ref,
|
||||||
|
network_info, None, power_on)
|
||||||
|
mock_get_mig.assert_called_with(mock.ANY, migration.id,
|
||||||
|
ins_ref.uuid)
|
||||||
|
|
||||||
self.assertTrue(self.fake_create_domain_called)
|
self.assertTrue(self.fake_create_domain_called)
|
||||||
|
|
||||||
def test_finish_revert_migration_power_on(self):
|
def test_finish_revert_migration_power_on(self):
|
||||||
self._test_finish_revert_migration(True)
|
migration = objects.Migration(id=42, source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
self._test_finish_revert_migration(power_on=True, migration=migration)
|
||||||
|
|
||||||
def test_finish_revert_migration_power_off(self):
|
def test_finish_revert_migration_power_off(self):
|
||||||
self._test_finish_revert_migration(False)
|
migration = objects.Migration(id=42, source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
self._test_finish_revert_migration(power_on=False, migration=migration)
|
||||||
|
|
||||||
|
def test_finish_revert_migration_same_host(self):
|
||||||
|
migration = objects.Migration(id=42, source_compute='fake-host',
|
||||||
|
dest_compute='fake-host')
|
||||||
|
self._test_finish_revert_migration(power_on=True, migration=migration)
|
||||||
|
|
||||||
|
def test_finish_revert_migration_diff_host(self):
|
||||||
|
migration = objects.Migration(id=42, source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
self._test_finish_revert_migration(power_on=True, migration=migration)
|
||||||
|
|
||||||
def _test_finish_revert_migration_after_crash(self, backup_made=True,
|
def _test_finish_revert_migration_after_crash(self, backup_made=True,
|
||||||
del_inst_failed=False):
|
del_inst_failed=False):
|
||||||
|
@ -19005,6 +19065,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
drvr.image_backend.by_name.return_value = drvr.image_backend
|
drvr.image_backend.by_name.return_value = drvr.image_backend
|
||||||
context = 'fake_context'
|
context = 'fake_context'
|
||||||
ins_ref = self._create_instance()
|
ins_ref = self._create_instance()
|
||||||
|
ins_ref.migration_context = objects.MigrationContext(
|
||||||
|
migration_id=42)
|
||||||
|
migration = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
|
||||||
with test.nested(
|
with test.nested(
|
||||||
mock.patch.object(os.path, 'exists', return_value=backup_made),
|
mock.patch.object(os.path, 'exists', return_value=backup_made),
|
||||||
|
@ -19014,13 +19078,17 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
mock.patch.object(drvr, '_get_guest_xml'),
|
mock.patch.object(drvr, '_get_guest_xml'),
|
||||||
mock.patch.object(shutil, 'rmtree'),
|
mock.patch.object(shutil, 'rmtree'),
|
||||||
mock.patch.object(loopingcall, 'FixedIntervalLoopingCall'),
|
mock.patch.object(loopingcall, 'FixedIntervalLoopingCall'),
|
||||||
|
mock.patch.object(objects.Migration, 'get_by_id_and_instance',
|
||||||
|
return_value=migration)
|
||||||
) as (mock_stat, mock_path, mock_rename, mock_cdn, mock_ggx,
|
) as (mock_stat, mock_path, mock_rename, mock_cdn, mock_ggx,
|
||||||
mock_rmtree, mock_looping_call):
|
mock_rmtree, mock_looping_call, mock_get_mig):
|
||||||
mock_path.return_value = '/fake/foo'
|
mock_path.return_value = '/fake/foo'
|
||||||
if del_inst_failed:
|
if del_inst_failed:
|
||||||
mock_rmtree.side_effect = OSError(errno.ENOENT,
|
mock_rmtree.side_effect = OSError(errno.ENOENT,
|
||||||
'test exception')
|
'test exception')
|
||||||
drvr.finish_revert_migration(context, ins_ref, [])
|
drvr.finish_revert_migration(context, ins_ref,
|
||||||
|
network_model.NetworkInfo())
|
||||||
|
mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
|
||||||
if backup_made:
|
if backup_made:
|
||||||
mock_rename.assert_called_once_with('/fake/foo_resize',
|
mock_rename.assert_called_once_with('/fake/foo_resize',
|
||||||
'/fake/foo')
|
'/fake/foo')
|
||||||
|
@ -19049,6 +19117,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
image_meta = {"disk_format": "raw",
|
image_meta = {"disk_format": "raw",
|
||||||
"properties": {"hw_disk_bus": "ide"}}
|
"properties": {"hw_disk_bus": "ide"}}
|
||||||
instance = self._create_instance()
|
instance = self._create_instance()
|
||||||
|
instance.migration_context = objects.MigrationContext(
|
||||||
|
migration_id=42)
|
||||||
|
migration = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||||
|
|
||||||
|
@ -19058,22 +19130,37 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
mock.patch.object(utils, 'get_image_from_system_metadata',
|
mock.patch.object(utils, 'get_image_from_system_metadata',
|
||||||
return_value=image_meta),
|
return_value=image_meta),
|
||||||
mock.patch.object(drvr, '_get_guest_xml',
|
mock.patch.object(drvr, '_get_guest_xml',
|
||||||
side_effect=fake_get_guest_xml)):
|
side_effect=fake_get_guest_xml),
|
||||||
drvr.finish_revert_migration('', instance, None, power_on=False)
|
mock.patch.object(objects.Migration, 'get_by_id_and_instance',
|
||||||
|
return_value=migration)
|
||||||
|
) as (mock_img_bkend, mock_cdan, mock_gifsm, mock_ggxml, mock_get_mig):
|
||||||
|
drvr.finish_revert_migration('', instance,
|
||||||
|
network_model.NetworkInfo(),
|
||||||
|
power_on=False)
|
||||||
|
mock_get_mig.assert_called_with(mock.ANY, 42, instance.uuid)
|
||||||
|
|
||||||
def test_finish_revert_migration_snap_backend(self):
|
def test_finish_revert_migration_snap_backend(self):
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||||
drvr.image_backend = mock.Mock()
|
drvr.image_backend = mock.Mock()
|
||||||
drvr.image_backend.by_name.return_value = drvr.image_backend
|
drvr.image_backend.by_name.return_value = drvr.image_backend
|
||||||
ins_ref = self._create_instance()
|
ins_ref = self._create_instance()
|
||||||
|
ins_ref.migration_context = objects.MigrationContext(
|
||||||
|
migration_id=42)
|
||||||
|
migration = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
|
||||||
with test.nested(
|
with test.nested(
|
||||||
mock.patch.object(utils, 'get_image_from_system_metadata'),
|
mock.patch.object(utils, 'get_image_from_system_metadata'),
|
||||||
mock.patch.object(drvr, '_create_domain_and_network'),
|
mock.patch.object(drvr, '_create_domain_and_network'),
|
||||||
mock.patch.object(drvr, '_get_guest_xml')) as (
|
mock.patch.object(drvr, '_get_guest_xml'),
|
||||||
mock_image, mock_cdn, mock_ggx):
|
mock.patch.object(objects.Migration, 'get_by_id_and_instance',
|
||||||
|
return_value=migration)) as (
|
||||||
|
mock_image, mock_cdn, mock_ggx, mock_get_mig):
|
||||||
mock_image.return_value = {'disk_format': 'raw'}
|
mock_image.return_value = {'disk_format': 'raw'}
|
||||||
drvr.finish_revert_migration('', ins_ref, None, power_on=False)
|
drvr.finish_revert_migration('', ins_ref,
|
||||||
|
network_model.NetworkInfo(),
|
||||||
|
power_on=False)
|
||||||
|
mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
|
||||||
|
|
||||||
drvr.image_backend.rollback_to_snap.assert_called_once_with(
|
drvr.image_backend.rollback_to_snap.assert_called_once_with(
|
||||||
libvirt_utils.RESIZE_SNAPSHOT_NAME)
|
libvirt_utils.RESIZE_SNAPSHOT_NAME)
|
||||||
|
@ -19105,17 +19192,26 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
drvr.image_backend.by_name.return_value = drvr.image_backend
|
drvr.image_backend.by_name.return_value = drvr.image_backend
|
||||||
drvr.image_backend.exists.return_value = False
|
drvr.image_backend.exists.return_value = False
|
||||||
ins_ref = self._create_instance()
|
ins_ref = self._create_instance()
|
||||||
|
ins_ref.migration_context = objects.MigrationContext(
|
||||||
|
migration_id=42)
|
||||||
|
migration = objects.Migration(source_compute='fake-host1',
|
||||||
|
dest_compute='fake-host2')
|
||||||
|
|
||||||
with test.nested(
|
with test.nested(
|
||||||
mock.patch.object(rbd_utils, 'RBDDriver'),
|
mock.patch.object(rbd_utils, 'RBDDriver'),
|
||||||
mock.patch.object(utils, 'get_image_from_system_metadata'),
|
mock.patch.object(utils, 'get_image_from_system_metadata'),
|
||||||
mock.patch.object(drvr, '_create_domain_and_network'),
|
mock.patch.object(drvr, '_create_domain_and_network'),
|
||||||
mock.patch.object(drvr, '_get_guest_xml')) as (
|
mock.patch.object(drvr, '_get_guest_xml'),
|
||||||
mock_rbd, mock_image, mock_cdn, mock_ggx):
|
mock.patch.object(objects.Migration, 'get_by_id_and_instance',
|
||||||
|
return_value=migration)) as (
|
||||||
|
mock_rbd, mock_image, mock_cdn, mock_ggx, mock_get_mig):
|
||||||
mock_image.return_value = {'disk_format': 'raw'}
|
mock_image.return_value = {'disk_format': 'raw'}
|
||||||
drvr.finish_revert_migration('', ins_ref, None, power_on=False)
|
drvr.finish_revert_migration('', ins_ref,
|
||||||
|
network_model.NetworkInfo(),
|
||||||
|
power_on=False)
|
||||||
self.assertFalse(drvr.image_backend.rollback_to_snap.called)
|
self.assertFalse(drvr.image_backend.rollback_to_snap.called)
|
||||||
self.assertFalse(drvr.image_backend.remove_snap.called)
|
self.assertFalse(drvr.image_backend.remove_snap.called)
|
||||||
|
mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
|
||||||
|
|
||||||
def test_cleanup_failed_migration(self):
|
def test_cleanup_failed_migration(self):
|
||||||
self.mox.StubOutWithMock(shutil, 'rmtree')
|
self.mox.StubOutWithMock(shutil, 'rmtree')
|
||||||
|
|
|
@ -5639,14 +5639,15 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
block_device_info=None, power_on=True,
|
block_device_info=None, power_on=True,
|
||||||
vifs_already_plugged=False,
|
vifs_already_plugged=False,
|
||||||
post_xml_callback=None,
|
post_xml_callback=None,
|
||||||
destroy_disks_on_failure=False):
|
destroy_disks_on_failure=False,
|
||||||
|
external_events=None):
|
||||||
|
|
||||||
"""Do required network setup and create domain."""
|
"""Do required network setup and create domain."""
|
||||||
timeout = CONF.vif_plugging_timeout
|
timeout = CONF.vif_plugging_timeout
|
||||||
if (self._conn_supports_start_paused and
|
if (self._conn_supports_start_paused and utils.is_neutron() and not
|
||||||
utils.is_neutron() and not
|
vifs_already_plugged and power_on and timeout):
|
||||||
vifs_already_plugged and power_on and timeout):
|
events = (external_events if external_events
|
||||||
events = self._get_neutron_events(network_info)
|
else self._get_neutron_events(network_info))
|
||||||
else:
|
else:
|
||||||
events = []
|
events = []
|
||||||
|
|
||||||
|
@ -8542,9 +8543,24 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
xml = self._get_guest_xml(context, instance, network_info, disk_info,
|
xml = self._get_guest_xml(context, instance, network_info, disk_info,
|
||||||
instance.image_meta,
|
instance.image_meta,
|
||||||
block_device_info=block_device_info)
|
block_device_info=block_device_info)
|
||||||
self._create_domain_and_network(context, xml, instance, network_info,
|
# NOTE(artom) In some Neutron or port configurations we've already
|
||||||
block_device_info=block_device_info,
|
# waited for vif-plugged events in the compute manager's
|
||||||
power_on=power_on)
|
# _finish_revert_resize_network_migrate_finish(), right after updating
|
||||||
|
# the port binding. For any ports not covered by those "bind-time"
|
||||||
|
# events, we wait for "plug-time" events here.
|
||||||
|
# TODO(artom) This DB lookup is done for backportability. A subsequent
|
||||||
|
# patch will remove it and change the finish_revert_migration() method
|
||||||
|
# signature to pass is the migration object.
|
||||||
|
migration = objects.Migration.get_by_id_and_instance(
|
||||||
|
context, instance.migration_context.migration_id, instance.uuid)
|
||||||
|
events = network_info.get_plug_time_events(migration)
|
||||||
|
if events:
|
||||||
|
LOG.debug('Instance is using plug-time events: %s', events,
|
||||||
|
instance=instance)
|
||||||
|
self._create_domain_and_network(
|
||||||
|
context, xml, instance, network_info,
|
||||||
|
block_device_info=block_device_info, power_on=power_on,
|
||||||
|
external_events=events)
|
||||||
|
|
||||||
if power_on:
|
if power_on:
|
||||||
timer = loopingcall.FixedIntervalLoopingCall(
|
timer = loopingcall.FixedIntervalLoopingCall(
|
||||||
|
|
Loading…
Reference in New Issue