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

openSUSE Build Service is sponsored by