]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Better tolerate deleted OVS ports in OVS agent
authorPaul Ward <wpward@us.ibm.com>
Thu, 24 Sep 2015 19:52:28 +0000 (14:52 -0500)
committerPaul Ward <wpward@us.ibm.com>
Thu, 22 Oct 2015 19:45:42 +0000 (14:45 -0500)
This change will not force a resync in the case where a virtual machine is
deleted, and therefore its OVS port deleted, in between the time an RPC
call was made to get the devices and where we make the call to correlate
those devices to vif ports.

Change-Id: Ie55eb69ad7ee177f0cf8ee8fc7fc585fbd0d4a22
Closes-Bug: #1499488

neutron/agent/common/ovs_lib.py
neutron/tests/functional/agent/l2/base.py
neutron/tests/functional/agent/test_l2_ovs_agent.py
neutron/tests/unit/agent/common/test_ovs_lib.py

index 559fca2320dd30f03a709fd4204108000af50c8e..406e5e35c69b3683b55a6c7645a043c2ab26f1b7 100644 (file)
@@ -435,7 +435,8 @@ class OVSBridge(BaseOVS):
 
     def get_vifs_by_ids(self, port_ids):
         interface_info = self.get_ports_attributes(
-            "Interface", columns=["name", "external_ids", "ofport"])
+            "Interface", columns=["name", "external_ids", "ofport"],
+            if_exists=True)
         by_id = {x['external_ids'].get('iface-id'): x for x in interface_info}
         result = {}
         for port_id in port_ids:
index ef3779dd74b1bdfa26f4c46b07d414ab2b650947..9a29a72c3e3a00e124198297da09cf0c717839b0 100644 (file)
@@ -120,8 +120,8 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
         agent.sg_agent = mock.Mock()
         return agent
 
-    def start_agent(self, agent):
-        self.setup_agent_rpc_mocks(agent)
+    def start_agent(self, agent, unplug_ports=[]):
+        self.setup_agent_rpc_mocks(agent, unplug_ports)
         polling_manager = polling.InterfacePollingMinimizer()
         self.addCleanup(polling_manager.stop)
         polling_manager.start()
@@ -164,6 +164,11 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
             self.driver.init_l3(port.get('vif_name'), ip_cidrs,
                                 namespace=self.namespace)
 
+    def _unplug_ports(self, ports, agent):
+        for port in ports:
+            self.driver.unplug(
+                port.get('vif_name'), agent.int_br.br_name, self.namespace)
+
     def _get_device_details(self, port, network):
         dev = {'device': port['id'],
                'port_id': port['id'],
@@ -236,15 +241,17 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
                 'devices_down': dev_down,
                 'failed_devices_down': []}
 
-    def setup_agent_rpc_mocks(self, agent):
+    def setup_agent_rpc_mocks(self, agent, unplug_ports):
         def mock_device_details(context, devices, agent_id, host=None):
-
             details = []
             for port in self.ports:
                 if port['id'] in devices:
                     dev = self._get_device_details(
                         port, self.network)
                     details.append(dev)
+            ports_to_unplug = [x for x in unplug_ports if x['id'] in devices]
+            if ports_to_unplug:
+                self._unplug_ports(ports_to_unplug, self.agent)
             return {'devices': details, 'failed_devices': []}
 
         (agent.plugin_rpc.get_devices_details_list_and_failed_devices.
@@ -262,11 +269,12 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
         self.agent.plugin_rpc.update_device_list.side_effect = (
             mock_device_raise_exception)
 
-    def wait_until_ports_state(self, ports, up):
+    def wait_until_ports_state(self, ports, up, timeout=60):
         port_ids = [p['id'] for p in ports]
         agent_utils.wait_until_true(
             lambda: self._expected_plugin_rpc_call(
-                self.agent.plugin_rpc.update_device_list, port_ids, up))
+                self.agent.plugin_rpc.update_device_list, port_ids, up),
+            timeout=timeout)
 
     def setup_agent_and_ports(self, port_dicts, create_tunnels=True,
                               trigger_resync=False):
index c1baa8bafa245903238d2eb69c9707295b3765c9..b57a8f93158249eec8a0f922ef5cb3c18255f60b 100644 (file)
@@ -16,6 +16,7 @@
 
 import time
 
+from eventlet.timeout import Timeout
 from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
 from neutron.tests.common import net_helpers
 from neutron.tests.functional.agent.l2 import base
@@ -105,6 +106,20 @@ class TestOVSAgent(base.OVSAgentTestFramework):
                 self.agent.setup_integration_br()
                 time.sleep(0.25)
 
+    def test_noresync_after_port_gone(self):
+        '''This will test the scenario where a port is removed after listing
+        it but before getting vif info about it.
+        '''
+        self.ports = self.create_test_ports(amount=2)
+        self.agent = self.create_agent(create_tunnels=False)
+        self.network = self._create_test_network_dict()
+        self._plug_ports(self.network, self.ports, self.agent)
+        self.start_agent(self.agent, unplug_ports=[self.ports[1]])
+        self.wait_until_ports_state([self.ports[0]], up=True)
+        self.assertRaises(
+            Timeout, self.wait_until_ports_state, [self.ports[1]], up=True,
+            timeout=10)
+
 
 class TestOVSAgentExtensionConfig(base.OVSAgentTestFramework):
     def setUp(self):
index 5efc2979d2a321fc8855ecfe2624e3d16e8791ad..88a04e7360462e1005f55a4f4976cb797828c4c2 100644 (file)
@@ -691,6 +691,9 @@ class OVS_Lib_Test(base.BaseTestCase):
         self.assertEqual('pid2', by_id['pid2'].vif_id)
         self.assertEqual('qvo2', by_id['pid2'].port_name)
         self.assertEqual(2, by_id['pid2'].ofport)
+        self.br.get_ports_attributes.assert_has_calls(
+            [mock.call('Interface', columns=['name', 'external_ids', 'ofport'],
+                       if_exists=True)])
 
     def _test_get_vif_port_by_id(self, iface_id, data, br_name=None,
                                  extra_calls_and_values=None):