Notify nova with network-vif-plugged in case of live migration

- during live migration on pre migration step nova plugs instance
   vif device on the destination compute node;
 - L2 agent on destination host detects new device and requests device
   info from server;
 - server does not change port status since port is bound to another
   host (source host);
 - L2 agent processes device and sends update_device_up to server;
 - again server does not update status as port is bound to another host;

Nova notifications are sent only in case port status change so in this case
no notifications are sent.

The fix is to explicitly notify nova if agent reports device up from a host
other than port's current host.

This is the fix on neutron side, the actual fix of the bug is on nova side:
change-id Ib1cb9c2f6eb2f5ce6280c685ae44a691665b4e98

Closes-Bug: #1414559
Change-Id: Ifa919a9076a3cc2696688af3feadf8d7fa9e6fc2
This commit is contained in:
Oleg Bondarev 2015-11-18 12:15:09 +03:00
parent 0f8e23f235
commit b7c303ee0a
4 changed files with 67 additions and 13 deletions

View File

@ -166,26 +166,31 @@ class Notifier(object):
else:
return self._get_network_changed_event(port['device_id'])
def _can_notify(self, port):
if not port.id:
LOG.warning(_LW("Port ID not set! Nova will not be notified of "
"port status change."))
return False
# If there is no device_id set there is nothing we can do here.
if not port.device_id:
LOG.debug("device_id is not set on port %s yet.", port.id)
return False
# We only want to notify about nova ports.
if not self._is_compute_port(port):
return False
return True
def record_port_status_changed(self, port, current_port_status,
previous_port_status, initiator):
"""Determine if nova needs to be notified due to port status change.
"""
# clear out previous _notify_event
port._notify_event = None
# If there is no device_id set there is nothing we can do here.
if not port.device_id:
LOG.debug("device_id is not set on port yet.")
if not self._can_notify(port):
return
if not port.id:
LOG.warning(_LW("Port ID not set! Nova will not be notified of "
"port status change."))
return
# We only want to notify about nova ports.
if not self._is_compute_port(port):
return
# We notify nova when a vif is unplugged which only occurs when
# the status goes from ACTIVE to DOWN.
if (previous_port_status == constants.PORT_STATUS_ACTIVE and
@ -222,6 +227,24 @@ class Notifier(object):
self.batch_notifier.queue_event(event)
port._notify_event = None
def notify_port_active_direct(self, port):
"""Notify nova about active port
Used when port was wired on the host other than port's current host
according to port binding. This happens during live migration.
In this case ml2 plugin skips port status update but we still we need
to notify nova.
"""
if not self._can_notify(port):
return
port._notify_event = (
{'server_uuid': port.device_id,
'name': VIF_PLUGGED,
'status': 'completed',
'tag': port.id})
self.send_port_status(None, None, port)
def send_events(self, batched_events):
LOG.debug("Sending events: %s", batched_events)
try:

View File

@ -14,6 +14,7 @@
# under the License.
from neutron_lib import constants as n_const
from neutron_lib import exceptions
from oslo_log import log
import oslo_messaging
from sqlalchemy.orm import exc
@ -209,6 +210,17 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
LOG.debug("Device %(device)s not bound to the"
" agent host %(host)s",
{'device': device, 'host': host})
# this might mean that a VM is in the process of live migration
# and vif was plugged on the destination compute node;
# need to notify nova explicitly
try:
port = plugin._get_port(rpc_context, port_id)
except exceptions.PortNotFound:
LOG.debug("Port %s not found, will not notify nova.", port_id)
else:
if port.device_owner.startswith(
n_const.DEVICE_OWNER_COMPUTE_PREFIX):
plugin.nova_notifier.notify_port_active_direct(port)
return
if port and port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE:
# NOTE(kevinbenton): we have to special case DVR ports because of

View File

@ -328,3 +328,19 @@ class TestNovaNotify(base.BaseTestCase):
region_name=cfg.CONF.nova.region_name,
endpoint_type='internal',
extensions=mock.ANY)
def test_notify_port_active_direct(self):
device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87'
port_id = 'bee50827-bcee-4cc8-91c1-a27b0ce54222'
port = models_v2.Port(id=port_id, device_id=device_id,
device_owner=DEVICE_OWNER_COMPUTE)
expected_event = {'server_uuid': device_id,
'name': nova.VIF_PLUGGED,
'status': 'completed',
'tag': port_id}
self.nova_notifier.notify_port_active_direct(port)
self.assertEqual(
1, len(self.nova_notifier.batch_notifier.pending_events))
self.assertEqual(expected_event,
self.nova_notifier.batch_notifier.pending_events[0])

View File

@ -222,6 +222,9 @@ class RpcCallbacksTestCase(base.BaseTestCase):
def test_update_device_up_with_device_not_bound_to_host(self):
self.assertIsNone(self._test_update_device_not_bound_to_host(
self.callbacks.update_device_up))
port = self.plugin._get_port.return_value
(self.plugin.nova_notifier.notify_port_active_direct.
assert_called_once_with(port))
def test_update_device_down_with_device_not_bound_to_host(self):
self.assertEqual(