From 0927525ccda92c9788d4fa4d75d7157e28493446 Mon Sep 17 00:00:00 2001
From: Swaminathan Vasudevan <SVasudevan@suse.com>
Date: Thu, 28 Jun 2018 11:56:39 -0700
Subject: [PATCH] neutron-lbaas haproxy agent prevent vif unplug when failover
 occurs

When lbaas fails over after an agent is unresponsive, the dead
agent on coming up should not unplug the vif port, if the lbaas
is active on other agent and when failover is configured.

This patch fixes the problem.

Change-Id: I76c38b20eb72c1dba0a0a2a140bbe77053aa3ed0
Closes-Bug: #1779194
(cherry picked from commit 72399374b222bd4a9d192081bfab2344af27fdd3)
---
 neutron_lbaas/agent/agent_manager.py           | 14 ++++++++------
 .../drivers/haproxy/namespace_driver.py        | 14 ++++++++++----
 .../tests/unit/agent/test_agent_manager.py     | 18 ++++++++++--------
 .../drivers/haproxy/test_namespace_driver.py   |  2 +-
 4 files changed, 29 insertions(+), 19 deletions(-)

Index: neutron-lbaas-11.0.4.dev6/neutron_lbaas/agent/agent_manager.py
===================================================================
--- neutron-lbaas-11.0.4.dev6.orig/neutron_lbaas/agent/agent_manager.py
+++ neutron-lbaas-11.0.4.dev6/neutron_lbaas/agent/agent_manager.py
@@ -154,7 +154,7 @@ class LbaasAgentManager(periodic_task.Pe
             ready_instances = set(self.plugin_rpc.get_ready_devices())
 
             for deleted_id in known_instances - ready_instances:
-                self._destroy_loadbalancer(deleted_id)
+                self._destroy_loadbalancer(deleted_id, resync=True)
 
             for loadbalancer_id in ready_instances:
                 self._reload_loadbalancer(loadbalancer_id)
@@ -163,7 +163,7 @@ class LbaasAgentManager(periodic_task.Pe
             LOG.exception('Unable to retrieve ready devices')
             self.needs_resync = True
 
-        self.remove_orphans()
+        self.remove_orphans(resync=True)
 
     def _get_driver(self, loadbalancer_id):
         if loadbalancer_id not in self.instance_mapping:
@@ -193,10 +193,11 @@ class LbaasAgentManager(periodic_task.Pe
                           loadbalancer_id)
             self.needs_resync = True
 
-    def _destroy_loadbalancer(self, lb_id):
+    def _destroy_loadbalancer(self, lb_id, resync=False):
         driver = self._get_driver(lb_id)
         try:
-            driver.undeploy_instance(lb_id, delete_namespace=True)
+            driver.undeploy_instance(lb_id, delete_namespace=True,
+                                     resync=resync)
             del self.instance_mapping[lb_id]
             self.plugin_rpc.loadbalancer_destroyed(lb_id)
         except Exception:
@@ -204,12 +205,13 @@ class LbaasAgentManager(periodic_task.Pe
                           lb_id)
             self.needs_resync = True
 
-    def remove_orphans(self):
+    def remove_orphans(self, resync=False):
         for driver_name in self.device_drivers:
             lb_ids = [lb_id for lb_id in self.instance_mapping
                       if self.instance_mapping[lb_id] == driver_name]
             try:
-                self.device_drivers[driver_name].remove_orphans(lb_ids)
+                self.device_drivers[driver_name].remove_orphans(lb_ids,
+                                                                resync=resync)
             except NotImplementedError:
                 pass  # Not all drivers will support this
 
Index: neutron-lbaas-11.0.4.dev6/neutron_lbaas/drivers/haproxy/namespace_driver.py
===================================================================
--- neutron-lbaas-11.0.4.dev6.orig/neutron_lbaas/drivers/haproxy/namespace_driver.py
+++ neutron-lbaas-11.0.4.dev6/neutron_lbaas/drivers/haproxy/namespace_driver.py
@@ -141,8 +141,13 @@ class HaproxyNSDriver(agent_device_drive
                                              pids_path=pid_path,
                                              pid_file=pid_data)
         pm.disable()
-        # unplug the ports
-        if loadbalancer_id in self.deployed_loadbalancers:
+        # Before unplugging the port check if the LBaas
+        # is being active and see if it is a resync
+        # or failover is configured
+        resync = kwargs.get('resync', False)
+        failover_state = cfg.CONF.allow_automatic_lbaas_agent_failover
+        if (loadbalancer_id in self.deployed_loadbalancers and
+            not (resync and failover_state)):
             self._unplug(namespace,
                          self.deployed_loadbalancers[loadbalancer_id].vip_port)
 
@@ -166,7 +171,7 @@ class HaproxyNSDriver(agent_device_drive
         if loadbalancer_id in self.deployed_loadbalancers:
             del self.deployed_loadbalancers[loadbalancer_id]
 
-    def remove_orphans(self, known_loadbalancer_ids):
+    def remove_orphans(self, known_loadbalancer_ids, resync=False):
         if not os.path.exists(self.state_path):
             return
 
@@ -174,7 +179,8 @@ class HaproxyNSDriver(agent_device_drive
                    if lb_id not in known_loadbalancer_ids)
         for lb_id in orphans:
             if self.exists(lb_id):
-                self.undeploy_instance(lb_id, cleanup_namespace=True)
+                self.undeploy_instance(lb_id, cleanup_namespace=True,
+                                       resync=resync)
 
     def get_stats(self, loadbalancer_id):
         socket_path = self._get_state_file_path(loadbalancer_id,
Index: neutron-lbaas-11.0.4.dev6/neutron_lbaas/tests/unit/agent/test_agent_manager.py
===================================================================
--- neutron-lbaas-11.0.4.dev6.orig/neutron_lbaas/tests/unit/agent/test_agent_manager.py
+++ neutron-lbaas-11.0.4.dev6/neutron_lbaas/tests/unit/agent/test_agent_manager.py
@@ -97,8 +97,9 @@ class TestManager(base.BaseTestCase):
 
             reload.assert_has_calls([mock.call(i) for i in reloaded],
                                     any_order=True)
-            destroy.assert_has_calls([mock.call(i) for i in destroyed],
-                                     any_order=True)
+            destroy.assert_has_calls(
+                [mock.call(i, resync=True) for i in destroyed],
+                any_order=True)
             self.assertFalse(self.mgr.needs_resync)
 
     def test_sync_state_all_known(self):
@@ -179,7 +180,7 @@ class TestManager(base.BaseTestCase):
         self.mgr._destroy_loadbalancer(lb_id)
 
         self.driver_mock.undeploy_instance.assert_called_once_with(
-            lb_id, delete_namespace=True)
+            lb_id, delete_namespace=True, resync=False)
         self.assertNotIn(lb_id, self.mgr.instance_mapping)
         self.rpc_mock.loadbalancer_destroyed.assert_called_once_with(lb_id)
         self.assertFalse(self.mgr.needs_resync)
@@ -192,7 +193,7 @@ class TestManager(base.BaseTestCase):
         self.mgr._destroy_loadbalancer(lb_id)
 
         self.driver_mock.undeploy_instance.assert_called_once_with(
-            lb_id, delete_namespace=True)
+            lb_id, delete_namespace=True, resync=False)
         self.assertIn(lb_id, self.mgr.instance_mapping)
         self.assertFalse(self.rpc_mock.loadbalancer_destroyed.called)
         self.assertTrue(self.log.exception.called)
@@ -203,15 +204,16 @@ class TestManager(base.BaseTestCase):
                           self.mgr._get_driver, 'unknown')
 
     def test_remove_orphans(self):
-        self.mgr.remove_orphans()
-        self.driver_mock.remove_orphans.assert_called_once_with(['1', '2'])
+        self.mgr.remove_orphans(resync=False)
+        self.driver_mock.remove_orphans.assert_called_once_with(
+            ['1', '2'], resync=False)
 
     def test_agent_disabled(self):
         payload = {'admin_state_up': False}
         self.mgr.agent_updated(mock.Mock(), payload)
         self.driver_mock.undeploy_instance.assert_has_calls(
-            [mock.call('1', delete_namespace=True),
-             mock.call('2', delete_namespace=True)],
+            [mock.call('1', delete_namespace=True, resync=False),
+             mock.call('2', delete_namespace=True, resync=False)],
             any_order=True
         )
 
Index: neutron-lbaas-11.0.4.dev6/neutron_lbaas/tests/unit/drivers/haproxy/test_namespace_driver.py
===================================================================
--- neutron-lbaas-11.0.4.dev6.orig/neutron_lbaas/tests/unit/drivers/haproxy/test_namespace_driver.py
+++ neutron-lbaas-11.0.4.dev6/neutron_lbaas/tests/unit/drivers/haproxy/test_namespace_driver.py
@@ -142,7 +142,7 @@ class TestHaproxyNSDriver(base.BaseTestC
         list_dir.assert_called_once_with(self.driver.state_path)
         self.driver.exists.assert_called_once_with('lb2')
         self.driver.undeploy_instance.assert_called_once_with(
-            'lb2', cleanup_namespace=True)
+            'lb2', cleanup_namespace=True, resync=False)
 
     def test_get_stats(self):
         # Shamelessly stolen from v1 namespace driver tests.
