From: Oleg Bondarev Date: Tue, 18 Mar 2014 08:22:05 +0000 (+0400) Subject: LBaaS: remove orphan haproxy instances on agent start X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=d00e0b839378aed4c337efa1a33f6abbb384c149;p=openstack-build%2Fneutron-build.git LBaaS: remove orphan haproxy instances on agent start This change adds remove_orphans() handling to the haproxy namespace driver. remove_orphans() is already called by lbaas agent on start for all drivers Closes-Bug: #1262885 Change-Id: I5deae8e56c2cd2deb1667e9646633fd59a94b34e --- diff --git a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py b/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py index 7307bca1e..6bbe4f6b8 100644 --- a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py +++ b/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py @@ -113,7 +113,7 @@ class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver): self.pool_to_port_id[pool_id] = logical_config['vip']['port']['id'] @n_utils.synchronized('haproxy-driver') - def undeploy_instance(self, pool_id): + def undeploy_instance(self, pool_id, cleanup_namespace=False): namespace = get_ns_name(pool_id) ns = ip_lib.IPWrapper(self.root_helper, namespace) pid_path = self._get_state_file_path(pool_id, 'pid') @@ -125,6 +125,12 @@ class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver): if pool_id in self.pool_to_port_id: self._unplug(namespace, self.pool_to_port_id[pool_id]) + # delete all devices from namespace; + # used when deleting orphans and port_id is not known for pool_id + if cleanup_namespace: + for device in ns.get_devices(exclude_loopback=True): + self.vif_driver.unplug(device.name, namespace=namespace) + # remove the configuration directory conf_dir = os.path.dirname(self._get_state_file_path(pool_id, '')) if os.path.isdir(conf_dir): @@ -326,6 +332,16 @@ class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver): def delete_pool_health_monitor(self, health_monitor, pool_id): self._refresh_device(pool_id) + def remove_orphans(self, known_pool_ids): + if not os.path.exists(self.state_path): + return + + orphans = (pool_id for pool_id in os.listdir(self.state_path) + if pool_id not in known_pool_ids) + for pool_id in orphans: + if self.exists(pool_id): + self.undeploy_instance(pool_id, cleanup_namespace=True) + # NOTE (markmcclain) For compliance with interface.py which expects objects class Wrap(object): diff --git a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py index 504c592e9..6d96bd550 100644 --- a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py +++ b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py @@ -123,6 +123,41 @@ class TestHaproxyNSDriver(base.BaseTestCase): mock.call().garbage_collect_namespace() ]) + def test_undeploy_instance_with_ns_cleanup(self): + with contextlib.nested( + mock.patch.object(self.driver, '_get_state_file_path'), + mock.patch.object(self.driver, 'vif_driver'), + mock.patch.object(namespace_driver, 'kill_pids_in_file'), + mock.patch('neutron.agent.linux.ip_lib.IPWrapper'), + mock.patch('os.path.isdir'), + mock.patch('shutil.rmtree') + ) as (gsp, vif, kill, ip_wrap, isdir, rmtree): + device = mock.Mock() + device_name = 'port_device' + device.name = device_name + ip_wrap.return_value.get_devices.return_value = [device] + + self.driver.undeploy_instance('pool_id', cleanup_namespace=True) + vif.unplug.assert_called_once_with(device_name, + namespace='qlbaas-pool_id') + + def test_remove_orphans(self): + with contextlib.nested( + mock.patch.object(self.driver, 'exists'), + mock.patch.object(self.driver, 'undeploy_instance'), + mock.patch('os.listdir'), + mock.patch('os.path.exists') + ) as (exists, undeploy, listdir, path_exists): + known = ['known1', 'known2'] + unknown = ['unknown1', 'unknown2'] + listdir.return_value = known + unknown + exists.side_effect = lambda x: x == 'unknown2' + + self.driver.remove_orphans(known) + + undeploy.assert_called_once_with('unknown2', + cleanup_namespace=True) + def test_exists(self): with contextlib.nested( mock.patch.object(self.driver, '_get_state_file_path'),