Merge "macvtap: Mech driver detects invalid migration"
This commit is contained in:
commit
80c1a6b981
@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron._i18n import _LE
|
||||
from neutron_lib import constants
|
||||
from oslo_log import log
|
||||
|
||||
@ -54,6 +55,22 @@ class MacvtapMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
"""Macvtap driver vlan transparency support."""
|
||||
return False
|
||||
|
||||
def _is_live_migration(self, context):
|
||||
# We cannot just check if
|
||||
# context.original['host_id'] != context.current['host_id']
|
||||
# This condition is also true, if nova does a reschedule of a
|
||||
# instance when something went wrong during spawn. In this case,
|
||||
# context.original['host_id'] is set to the failed host.
|
||||
# The only safe way to detect a migration is to look into the binding
|
||||
# profiles 'migrating_to' attribute, which is set by Nova since patch
|
||||
# https://review.openstack.org/#/c/275073/.
|
||||
port_profile = context.original.get(portbindings.PROFILE)
|
||||
if port_profile and port_profile.get('migrating_to', None):
|
||||
LOG.debug("Live migration with profile %s detected.", port_profile)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def try_to_bind_segment_for_agent(self, context, segment, agent):
|
||||
if self.check_segment_for_agent(segment, agent):
|
||||
vif_details_segment = self.vif_details
|
||||
@ -69,6 +86,35 @@ class MacvtapMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
else:
|
||||
macvtap_src = interface
|
||||
|
||||
if self._is_live_migration(context):
|
||||
# We can use the original port here, as during live migration
|
||||
# portbinding is done after the migration happened. Nova will
|
||||
# not do a reschedule of the instance migration if binding
|
||||
# fails, but just set the instance into error state.
|
||||
# Due to that we can be sure that the original port is the
|
||||
# migration source port.
|
||||
orig_vif_details = context.original[portbindings.VIF_DETAILS]
|
||||
orig_source = orig_vif_details[
|
||||
portbindings.VIF_DETAILS_MACVTAP_SOURCE]
|
||||
if orig_source != macvtap_src:
|
||||
source_host = context.original[portbindings.HOST_ID]
|
||||
target_host = agent['host']
|
||||
LOG.error(_LE("Vif binding denied by mechanism driver. "
|
||||
"MacVTap source device '%(target_dev)s' on "
|
||||
"the migration target '%(target_host)s'is "
|
||||
"not equal to device '%(source_dev)s' on "
|
||||
"the migration source '%(source_host)s. "
|
||||
"Make sure that the "
|
||||
"interface mapping of macvtap "
|
||||
"agent on both hosts is equal "
|
||||
"for the physical network '%(physnet)s'!"),
|
||||
{'source_dev': orig_source,
|
||||
'target_dev': macvtap_src,
|
||||
'target_host': target_host,
|
||||
'source_host': source_host,
|
||||
'physnet': segment['physical_network']})
|
||||
return False
|
||||
|
||||
vif_details_segment['physical_interface'] = interface
|
||||
vif_details_segment['macvtap_source'] = macvtap_src
|
||||
vif_details_segment['macvtap_mode'] = MACVTAP_MODE_BRIDGE
|
||||
|
@ -41,7 +41,8 @@ class FakeNetworkContext(api.NetworkContext):
|
||||
|
||||
class FakePortContext(api.PortContext):
|
||||
def __init__(self, agent_type, agents, segments,
|
||||
vnic_type=portbindings.VNIC_NORMAL):
|
||||
vnic_type=portbindings.VNIC_NORMAL,
|
||||
original=None):
|
||||
self._agent_type = agent_type
|
||||
self._agents = agents
|
||||
self._network_context = FakeNetworkContext(segments)
|
||||
@ -49,6 +50,7 @@ class FakePortContext(api.PortContext):
|
||||
self._bound_segment_id = None
|
||||
self._bound_vif_type = None
|
||||
self._bound_vif_details = None
|
||||
self._original = original
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
@ -57,7 +59,7 @@ class FakePortContext(api.PortContext):
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return None
|
||||
return self._original or {}
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
|
@ -16,6 +16,7 @@
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers.macvtap.mech_driver import mech_macvtap
|
||||
from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base
|
||||
|
||||
@ -31,9 +32,11 @@ class MacvtapMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
||||
BAD_MAPPINGS = {'wrong_physical_network': 'wrong_if'}
|
||||
BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENT = {'alive': True,
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}
|
||||
AGENTS = [AGENT]
|
||||
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
@ -55,8 +58,64 @@ class MacvtapMechanismGenericTestCase(MacvtapMechanismBaseTestCase,
|
||||
pass
|
||||
|
||||
|
||||
class MacvtapMechanismMigrationTestCase(object):
|
||||
# MIGRATION_SEGMENT must be overridden for the specific type being tested
|
||||
MIGRATION_SEGMENT = None
|
||||
|
||||
MIGRATION_SEGMENTS = [MIGRATION_SEGMENT]
|
||||
|
||||
def test__is_live_migration_true(self):
|
||||
original = {"binding:profile": {"migrating_to": "host"}}
|
||||
self._test__is_live_migration(True, original)
|
||||
|
||||
def test__is_live_migration_false(self):
|
||||
self._test__is_live_migration(False, {})
|
||||
|
||||
def _test__is_live_migration(self, expected, original):
|
||||
context = base.FakePortContext(self.AGENT_TYPE,
|
||||
self.AGENTS,
|
||||
self.MIGRATION_SEGMENTS,
|
||||
vnic_type=self.VNIC_TYPE,
|
||||
original=original)
|
||||
|
||||
self.assertEqual(expected, self.driver._is_live_migration(context))
|
||||
|
||||
def _test_try_to_bind_segment_for_agent_migration(self, expected,
|
||||
original):
|
||||
context = base.FakePortContext(self.AGENT_TYPE,
|
||||
self.AGENTS,
|
||||
self.MIGRATION_SEGMENTS,
|
||||
vnic_type=self.VNIC_TYPE,
|
||||
original=original)
|
||||
result = self.driver.try_to_bind_segment_for_agent(
|
||||
context, self.MIGRATION_SEGMENT, self.AGENT)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_try_to_bind_segment_for_agent_migration_abort(self):
|
||||
original = {"binding:profile": {"migrating_to": "host"},
|
||||
"binding:vif_details": {"macvtap_source": "bad_source"},
|
||||
"binding:host_id": "source_host"}
|
||||
self._test_try_to_bind_segment_for_agent_migration(False, original)
|
||||
|
||||
def test_try_to_bind_segment_for_agent_migration_ok(self):
|
||||
macvtap_src = "fake_if"
|
||||
seg_id = self.MIGRATION_SEGMENT.get(api.SEGMENTATION_ID)
|
||||
if seg_id:
|
||||
# In the vlan case, macvtap source name ends with .vlan_id
|
||||
macvtap_src += "." + str(seg_id)
|
||||
original = {"binding:profile": {"migrating_to": "host"},
|
||||
"binding:vif_details": {"macvtap_source": macvtap_src},
|
||||
"binding:host_id": "source_host"}
|
||||
self._test_try_to_bind_segment_for_agent_migration(True, original)
|
||||
|
||||
|
||||
class MacvtapMechanismFlatTestCase(MacvtapMechanismBaseTestCase,
|
||||
base.AgentMechanismFlatTestCase):
|
||||
base.AgentMechanismFlatTestCase,
|
||||
MacvtapMechanismMigrationTestCase):
|
||||
MIGRATION_SEGMENT = {api.ID: 'flat_segment_id',
|
||||
api.NETWORK_TYPE: 'flat',
|
||||
api.PHYSICAL_NETWORK: 'fake_physical_network'}
|
||||
|
||||
def test_type_flat_vif_details(self):
|
||||
context = base.FakePortContext(self.AGENT_TYPE,
|
||||
self.AGENTS,
|
||||
@ -75,7 +134,13 @@ class MacvtapMechanismFlatTestCase(MacvtapMechanismBaseTestCase,
|
||||
|
||||
|
||||
class MacvtapMechanismVlanTestCase(MacvtapMechanismBaseTestCase,
|
||||
base.AgentMechanismVlanTestCase):
|
||||
base.AgentMechanismVlanTestCase,
|
||||
MacvtapMechanismMigrationTestCase):
|
||||
MIGRATION_SEGMENT = {api.ID: 'vlan_segment_id',
|
||||
api.NETWORK_TYPE: 'vlan',
|
||||
api.PHYSICAL_NETWORK: 'fake_physical_network',
|
||||
api.SEGMENTATION_ID: 1234}
|
||||
|
||||
def test_type_vlan_vif_details(self):
|
||||
context = base.FakePortContext(self.AGENT_TYPE,
|
||||
self.AGENTS,
|
||||
|
Loading…
Reference in New Issue
Block a user