Skip to content

Commit b8f3be6

Browse files
Comrade88SeanMooney
andcommitted
Set migrate_data.vifs only when using multiple port bindings
In the rocky cycle nova was enhanced to support the multiple port binding live migration workflow when neutron supports the binding-extended API extension. When the migration_data object was extended to support multiple port bindings, populating the vifs field was used as a sentinel to indicate that the new workflow should be used. In the train release I734cc01dce13f9e75a16639faf890ddb1661b7eb (SR-IOV Live migration indirect port support) broke the semantics of the migrate_data object by unconditionally populating the vifs field This change restores the rocky semantics, which are depended on by several parts of the code base, by only conditionally populating vifs if neutron supports multiple port bindings. Co-Authored-By: Sean Mooney <[email protected]> Change-Id: Ia00277ac8a68a635db85f9e0ce2c6d8df396e0d8 Closes-Bug: #1888395
1 parent 71bc6fc commit b8f3be6

File tree

5 files changed

+110
-23
lines changed

5 files changed

+110
-23
lines changed

nova/compute/manager.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7650,15 +7650,18 @@ def check_can_live_migrate_destination(self, ctxt, instance,
76507650
LOG.info('Destination was ready for NUMA live migration, '
76517651
'but source is either too old, or is set to an '
76527652
'older upgrade level.', instance=instance)
7653-
# Create migrate_data vifs
7654-
migrate_data.vifs = \
7655-
migrate_data_obj.VIFMigrateData.create_skeleton_migrate_vifs(
7656-
instance.get_network_info())
7657-
# Claim PCI devices for VIFs on destination (if needed)
7658-
port_id_to_pci = self._claim_pci_for_instance_vifs(ctxt, instance)
7659-
# Update migrate VIFs with the newly claimed PCI devices
7660-
self._update_migrate_vifs_profile_with_pci(migrate_data.vifs,
7661-
port_id_to_pci)
7653+
if self.network_api.supports_port_binding_extension(ctxt):
7654+
# Create migrate_data vifs
7655+
migrate_data.vifs = \
7656+
migrate_data_obj.\
7657+
VIFMigrateData.create_skeleton_migrate_vifs(
7658+
instance.get_network_info())
7659+
# Claim PCI devices for VIFs on destination (if needed)
7660+
port_id_to_pci = self._claim_pci_for_instance_vifs(
7661+
ctxt, instance)
7662+
# Update migrate VIFs with the newly claimed PCI devices
7663+
self._update_migrate_vifs_profile_with_pci(
7664+
migrate_data.vifs, port_id_to_pci)
76627665
finally:
76637666
self.driver.cleanup_live_migration_destination_check(ctxt,
76647667
dest_check_data)
@@ -7826,8 +7829,12 @@ def pre_live_migration(self, context, instance, block_migration, disk,
78267829
# determine if it should wait for a 'network-vif-plugged' event
78277830
# from neutron before starting the actual guest transfer in the
78287831
# hypervisor
7832+
using_multiple_port_bindings = (
7833+
'vifs' in migrate_data and migrate_data.vifs)
78297834
migrate_data.wait_for_vif_plugged = (
7830-
CONF.compute.live_migration_wait_for_vif_plug)
7835+
CONF.compute.live_migration_wait_for_vif_plug and
7836+
using_multiple_port_bindings
7837+
)
78317838

78327839
# NOTE(tr3buchet): setup networks on destination host
78337840
self.network_api.setup_networks_on_host(context, instance,

nova/tests/functional/regressions/test_bug_1888395.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import fixtures
1414

15+
from lxml import etree
16+
from urllib import parse as urlparse
17+
1518
from nova import context
1619
from nova.network import constants as neutron_constants
1720
from nova.network import neutron
@@ -67,6 +70,40 @@ def setUp(self):
6770
kB_mem=10740000)})
6871

6972
self.ctxt = context.get_admin_context()
73+
# TODO(sean-k-mooney): remove this when it is part of ServersTestBase
74+
self.useFixture(fixtures.MonkeyPatch(
75+
'nova.tests.unit.virt.libvirt.fakelibvirt.Domain.migrateToURI3',
76+
self._migrate_stub))
77+
78+
def _migrate_stub(self, domain, destination, params, flags):
79+
"""Stub out migrateToURI3."""
80+
81+
src_hostname = domain._connection.hostname
82+
dst_hostname = urlparse.urlparse(destination).netloc
83+
84+
# In a real live migration, libvirt and QEMU on the source and
85+
# destination talk it out, resulting in the instance starting to exist
86+
# on the destination. Fakelibvirt cannot do that, so we have to
87+
# manually create the "incoming" instance on the destination
88+
# fakelibvirt.
89+
dst = self.computes[dst_hostname]
90+
dst.driver._host.get_connection().createXML(
91+
params['destination_xml'],
92+
'fake-createXML-doesnt-care-about-flags')
93+
94+
src = self.computes[src_hostname]
95+
conn = src.driver._host.get_connection()
96+
97+
# because migrateToURI3 is spawned in a background thread, this method
98+
# does not block the upper nova layers. Because we don't want nova to
99+
# think the live migration has finished until this method is done, the
100+
# last thing we do is make fakelibvirt's Domain.jobStats() return
101+
# VIR_DOMAIN_JOB_COMPLETED.
102+
server = etree.fromstring(
103+
params['destination_xml']
104+
).find('./uuid').text
105+
dom = conn.lookupByUUIDString(server)
106+
dom.complete_job()
70107

71108
def test_live_migrate(self):
72109
server = self._create_server(
@@ -86,14 +123,7 @@ def test_live_migrate(self):
86123
}
87124
)
88125

89-
# FIXME(sean-k-mooney): this should succeed but because of bug #188395
90-
# it will fail.
91-
# self._wait_for_server_parameter(
92-
# server, {'OS-EXT-SRV-ATTR:host': 'end_host', 'status': 'ACTIVE'})
93-
# because of the bug the migration will fail in pre_live_migrate so
94-
# the vm should still be active on the start_host
95126
self._wait_for_server_parameter(
96-
server, {'OS-EXT-SRV-ATTR:host': 'start_host', 'status': 'ACTIVE'})
97-
127+
server, {'OS-EXT-SRV-ATTR:host': 'end_host', 'status': 'ACTIVE'})
98128
msg = "NotImplementedError: Cannot load 'vif_type' in the base class"
99-
self.assertIn(msg, self.stdlog.logger.output)
129+
self.assertNotIn(msg, self.stdlog.logger.output)

nova/tests/unit/compute/test_compute.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"""Tests for compute service."""
1919

2020
import datetime
21+
import fixtures as std_fixtures
2122
from itertools import chain
2223
import operator
2324
import sys
@@ -6133,13 +6134,19 @@ def stupid(*args, **kwargs):
61336134
return fake_network.fake_get_instance_nw_info(self)
61346135

61356136
self.stub_out('nova.network.neutron.API.get_instance_nw_info', stupid)
6136-
6137+
self.useFixture(
6138+
std_fixtures.MonkeyPatch(
6139+
'nova.network.neutron.API.supports_port_binding_extension',
6140+
lambda *args: True))
61376141
# creating instance testdata
61386142
instance = self._create_fake_instance_obj({'host': 'dummy'})
61396143
c = context.get_admin_context()
61406144
fake_notifier.NOTIFICATIONS = []
61416145
migrate_data = objects.LibvirtLiveMigrateData(
61426146
is_shared_instance_path=False)
6147+
vifs = migrate_data_obj.VIFMigrateData.create_skeleton_migrate_vifs(
6148+
stupid())
6149+
migrate_data.vifs = vifs
61436150
mock_pre.return_value = migrate_data
61446151

61456152
with mock.patch.object(self.compute.network_api,

nova/tests/unit/compute/test_compute_mgr.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import contextlib
1616
import copy
1717
import datetime
18+
import fixtures as std_fixtures
1819
import time
1920

2021
from cinderclient import exceptions as cinder_exception
@@ -3374,20 +3375,48 @@ def _test_check_can_live_migrate_destination(self, do_raise=False,
33743375
mock_event.assert_called_once_with(
33753376
self.context, 'compute_check_can_live_migrate_destination',
33763377
CONF.host, instance.uuid, graceful_exit=False)
3378+
return result
33773379

33783380
def test_check_can_live_migrate_destination_success(self):
3381+
self.useFixture(std_fixtures.MonkeyPatch(
3382+
'nova.network.neutron.API.supports_port_binding_extension',
3383+
lambda *args: True))
33793384
self._test_check_can_live_migrate_destination()
33803385

33813386
def test_check_can_live_migrate_destination_fail(self):
3387+
self.useFixture(std_fixtures.MonkeyPatch(
3388+
'nova.network.neutron.API.supports_port_binding_extension',
3389+
lambda *args: True))
33823390
self.assertRaises(
3383-
test.TestingException,
3384-
self._test_check_can_live_migrate_destination,
3385-
do_raise=True)
3391+
test.TestingException,
3392+
self._test_check_can_live_migrate_destination,
3393+
do_raise=True)
3394+
3395+
def test_check_can_live_migrate_destination_contins_vifs(self):
3396+
self.useFixture(std_fixtures.MonkeyPatch(
3397+
'nova.network.neutron.API.supports_port_binding_extension',
3398+
lambda *args: True))
3399+
migrate_data = self._test_check_can_live_migrate_destination()
3400+
self.assertIn('vifs', migrate_data)
3401+
self.assertIsNotNone(migrate_data.vifs)
3402+
3403+
def test_check_can_live_migrate_destination_no_binding_extended(self):
3404+
self.useFixture(std_fixtures.MonkeyPatch(
3405+
'nova.network.neutron.API.supports_port_binding_extension',
3406+
lambda *args: False))
3407+
migrate_data = self._test_check_can_live_migrate_destination()
3408+
self.assertNotIn('vifs', migrate_data)
33863409

33873410
def test_check_can_live_migrate_destination_src_numa_lm_false(self):
3411+
self.useFixture(std_fixtures.MonkeyPatch(
3412+
'nova.network.neutron.API.supports_port_binding_extension',
3413+
lambda *args: True))
33883414
self._test_check_can_live_migrate_destination(src_numa_lm=False)
33893415

33903416
def test_check_can_live_migrate_destination_src_numa_lm_true(self):
3417+
self.useFixture(std_fixtures.MonkeyPatch(
3418+
'nova.network.neutron.API.supports_port_binding_extension',
3419+
lambda *args: True))
33913420
self._test_check_can_live_migrate_destination(src_numa_lm=True)
33923421

33933422
def test_dest_can_numa_live_migrate(self):
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
fixes:
3+
- |
4+
In the Rocky (18.0.0) release support was added to nova to use neutron's
5+
multiple port binding feature when the binding-extended API extension
6+
is available. In the Train (20.0.0) release the SR-IOV live migration
7+
feature broke the semantics of the vifs field in the ``migration_data``
8+
object that signals if the new multiple port binding workflow should
9+
be used by always populating it even when the ``binding-extended`` API
10+
extension is not present. This broke live migration for any deployment
11+
that did not support the optional ``binding-extended`` API extension.
12+
The Rocky behavior has now been restored enabling live migration
13+
using the single port binding workflow when multiple port bindings
14+
are not available.

0 commit comments

Comments
 (0)