diff --git a/compute_hyperv/nova/vmops.py b/compute_hyperv/nova/vmops.py index 9b14f34c..c988a2bc 100644 --- a/compute_hyperv/nova/vmops.py +++ b/compute_hyperv/nova/vmops.py @@ -352,9 +352,11 @@ class VMOps(object): @contextlib.contextmanager def wait_vif_plug_events(self, instance, network_info): timeout = CONF.vif_plugging_timeout - events = self._get_neutron_events(network_info) try: + # NOTE(claudiub): async calls to bind the neutron ports will be + # done when network_info is being accessed. + events = self._get_neutron_events(network_info) with self._virtapi.wait_for_instance_event( instance, events, deadline=timeout, error_callback=self._neutron_failed_callback): @@ -365,6 +367,18 @@ class VMOps(object): 'instance.', instance=instance) if CONF.vif_plugging_is_fatal: raise exception.VirtualInterfaceCreateException() + except exception.PortBindingFailed: + LOG.warning( + "Neutron failed to bind a port to this host. Make sure that " + "an L2 agent is registered on this node and alive (Neutron " + "Open vSwitch agent or Hyper-V agent), or that Neutron is " + "configured with a mechanism driver that is able to bind " + "ports without requiring an L2 agent on this host (e.g. OVN " + "mechanism driver). If you're using Neutron Hyper-V agent, " + "make sure that networking-hyperv is installed on the " + "Neutron controller and that the neutron-server service was " + "configured to use the 'hyperv' mechanism_driver.") + raise def _neutron_failed_callback(self, event_name, instance): LOG.error('Neutron Reported failure on event %s', diff --git a/compute_hyperv/tests/unit/test_vmops.py b/compute_hyperv/tests/unit/test_vmops.py index aa1b013b..1933405d 100644 --- a/compute_hyperv/tests/unit/test_vmops.py +++ b/compute_hyperv/tests/unit/test_vmops.py @@ -611,6 +611,18 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): deadline=CONF.vif_plugging_timeout, error_callback=self._vmops._neutron_failed_callback) + @mock.patch.object(vmops.VMOps, '_get_neutron_events') + def test_wait_vif_plug_events_port_binding_failed(self, mock_get_events): + mock_get_events.side_effect = exception.PortBindingFailed( + port_id='fake_id') + + def _context_user(): + with self._vmops.wait_vif_plug_events(mock.sentinel.instance, + mock.sentinel.network_info): + pass + + self.assertRaises(exception.PortBindingFailed, _context_user) + def test_neutron_failed_callback(self): self.flags(vif_plugging_is_fatal=True) self.assertRaises(exception.VirtualInterfaceCreateException,