File 0001-When-creating-a-container-wait-for-network-events.patch of Package openstack-nova-docker
From 9e977f700f775fba4a251b0f6ea12f41e49267f0 Mon Sep 17 00:00:00 2001
From: rossella <rsblendido@suse.com>
Date: Wed, 20 Jan 2016 15:36:12 +0100
Subject: [PATCH] When creating a container wait for network events
This patch adds a way to wait for Neutron to
confirm that the vif was plugged when creating a
container, similar to what's done in the nova libvirt
driver.
Change-Id: I17f295611046f352a82104984ea59a4465b989f6
Closes-bug: #1536546
---
novadocker/tests/virt/docker/test_driver.py | 107 ++++++++++++++++++++++++++++
novadocker/virt/docker/driver.py | 44 ++++++++++--
2 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/novadocker/tests/virt/docker/test_driver.py b/novadocker/tests/virt/docker/test_driver.py
index 2172847..d3f0050 100644
--- a/novadocker/tests/virt/docker/test_driver.py
+++ b/novadocker/tests/virt/docker/test_driver.py
@@ -17,10 +17,13 @@ import contextlib
import os
import socket
+import eventlet
import mock
+from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_utils import units
+from nova.compute import manager
from nova.compute import task_states
from nova import context
from nova import exception
@@ -160,6 +163,110 @@ class DockerDriverTestCase(test_virt_drivers._VirtDriverTestCase,
}
self.assertEqual(expected_stats, stats)
+ def _test_start_container_with_network_events(self, neutron_failure=None):
+ generated_events = []
+
+ def wait_timeout():
+ event = mock.MagicMock()
+ if neutron_failure == 'timeout':
+ raise eventlet.timeout.Timeout()
+ elif neutron_failure == 'error':
+ event.status = 'failed'
+ else:
+ event.status = 'completed'
+ return event
+
+ def fake_prepare(instance, event_name):
+ m = mock.MagicMock()
+ m.instance = instance
+ m.event_name = event_name
+ m.wait.side_effect = wait_timeout
+ generated_events.append(m)
+ return m
+
+ virtapi = manager.ComputeVirtAPI(mock.MagicMock())
+ prepare = virtapi._compute.instance_events.prepare_for_instance_event
+ prepare.side_effect = fake_prepare
+ drvr = novadocker.virt.docker.driver.DockerDriver(virtapi)
+
+ instance_href = utils.get_test_instance()
+ container_id = self.connection._find_container_by_uuid(
+ instance_href['uuid']).get('id')
+
+ vifs = [{'id': 'vif1', 'active': False},
+ {'id': 'vif2', 'active': False}]
+
+ @mock.patch.object(drvr, '_extract_dns_entries')
+ @mock.patch.object(drvr, 'plug_vifs')
+ @mock.patch.object(drvr, '_attach_vifs')
+ @mock.patch.object(self.mock_client, 'start')
+ @mock.patch.object(self.mock_client, 'kill')
+ @mock.patch.object(self.mock_client, 'remove_container')
+ def test_start(remove_container, kill, start, attach_vif, plug_vifs,
+ extract_dns_entries):
+ drvr._start_container(container_id, instance_href,
+ vifs)
+ plug_vifs.assert_called_with(instance_href, vifs)
+ attach_vif.assert_called_with(instance_href, vifs)
+
+ if neutron_failure and cfg.CONF.vif_plugging_is_fatal:
+ kill.assert_called_once_with(container_id)
+ remove_container.assert_called_once_with(container_id,
+ Force=True)
+ test_start()
+
+ if nova.utils.is_neutron() and cfg.CONF.vif_plugging_timeout:
+ prepare.assert_has_calls([
+ mock.call(instance_href, 'network-vif-plugged-vif1'),
+ mock.call(instance_href, 'network-vif-plugged-vif2')])
+ for event in generated_events:
+ if neutron_failure and generated_events.index(event) != 0:
+ self.assertEqual(0, event.call_count)
+ else:
+ self.assertEqual(0, prepare.call_count)
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events(self, is_neutron):
+ self._test_start_container_with_network_events()
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events_nowait(self, is_neutron):
+ self.flags(vif_plugging_timeout=0)
+ self._test_start_container_with_network_events()
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events_failed_timeout_non_fatal(
+ self, is_neutron):
+ self.flags(vif_plugging_is_fatal=False)
+ self._test_start_container_with_network_events(
+ neutron_failure='timeout')
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events_failed_timeout_fatal(
+ self, is_neutron):
+ self.assertRaises(exception.InstanceDeployFailure,
+ self._test_start_container_with_network_events,
+ neutron_failure='timeout')
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events_failed_error_non_fatal(
+ self, is_neutron):
+ self.flags(vif_plugging_is_fatal=False)
+ self._test_start_container_with_network_events(
+ neutron_failure='error')
+
+ @mock.patch('nova.utils.is_neutron', return_value=True)
+ def test_start_container_with_network_events_failed_error_fatal(
+ self, is_neutron):
+ self.assertRaises(exception.InstanceDeployFailure,
+ self._test_start_container_with_network_events,
+ neutron_failure='error')
+
+ @mock.patch('nova.utils.is_neutron', return_value=False)
+ def test_start_container_with_network_events_non_neutron(self,
+ is_neutron):
+ self._test_start_container_with_network_events()
+
def test_create_container(self, image_info=None, instance_href=None,
network_info=None):
if instance_href is None:
diff --git a/novadocker/virt/docker/driver.py b/novadocker/virt/docker/driver.py
index 908af34..cf95025 100644
--- a/novadocker/virt/docker/driver.py
+++ b/novadocker/virt/docker/driver.py
@@ -26,6 +26,7 @@ import uuid
from docker import errors
from docker import utils as docker_utils
+import eventlet
from oslo_config import cfg
from oslo_log import log
from oslo_serialization import jsonutils
@@ -49,7 +50,7 @@ from nova.virt import driver
from nova.virt import firewall
from nova.virt import hardware
from nova.virt import images
-from novadocker.i18n import _, _LI, _LE
+from novadocker.i18n import _, _LI, _LE, _LW
from novadocker.virt.docker import client as docker_client
from novadocker.virt.docker import hostinfo
from novadocker.virt.docker import network
@@ -440,6 +441,23 @@ class DockerDriver(driver.ComputeDriver):
binds = {mount_origin: {'bind': '/root/.ssh', 'ro': True}}
return binds
+ def _neutron_failed_callback(self, event_name, instance):
+ LOG.error(_LE('Neutron Reported failure on event '
+ '%(event)s for instance %(uuid)s'),
+ {'event': event_name, 'uuid': instance.uuid},
+ instance=instance)
+ if CONF.vif_plugging_is_fatal:
+ raise exception.VirtualInterfaceCreateException()
+
+ def _get_neutron_events(self, network_info):
+ # NOTE(danms): We need to collect any VIFs that are currently
+ # down that we expect a down->up event for. Anything that is
+ # already up will not undergo that transition, and for
+ # anything that might be stale (cache-wise) assume it's
+ # already up so we don't block on it.
+ return [('network-vif-plugged', vif['id'])
+ for vif in network_info if vif.get('active', True) is False]
+
def _start_container(self, container_id, instance, network_info=None):
binds = self._get_key_binds(container_id, instance)
dns = self._extract_dns_entries(network_info)
@@ -448,10 +466,28 @@ class DockerDriver(driver.ComputeDriver):
if not network_info:
return
+ timeout = CONF.vif_plugging_timeout
+ if (utils.is_neutron() and timeout):
+ events = self._get_neutron_events(network_info)
+ else:
+ events = []
+
try:
- self.plug_vifs(instance, network_info)
- self._attach_vifs(instance, network_info)
- except Exception as e:
+ with self.virtapi.wait_for_instance_event(
+ instance, events, deadline=timeout,
+ error_callback=self._neutron_failed_callback):
+ self.plug_vifs(instance, network_info)
+ self._attach_vifs(instance, network_info)
+ except eventlet.timeout.Timeout:
+ LOG.warn(_LW('Timeout waiting for vif plugging callback for '
+ 'instance %(uuid)s'), {'uuid': instance['name']})
+ if CONF.vif_plugging_is_fatal:
+ self.docker.kill(container_id)
+ self.docker.remove_container(container_id, force=True)
+ raise exception.InstanceDeployFailure(
+ 'Timeout waiting for vif plugging',
+ instance_id=instance['name'])
+ except (Exception) as e:
LOG.warning(_('Cannot setup network: %s'),
e, instance=instance, exc_info=True)
msg = _('Cannot setup network: {0}')
--
2.7.0