From fc365c8c0d876f4cab09ba10c99277cb57714c4c Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 23 Oct 2019 14:47:54 +0000 Subject: [PATCH] Check dnsmasq process is active when spawned After spawning the "dnsmasq" process in the method "Dnsmasq._spawn_or_reload_process", we need to check that the "dnsmasq" process is running and could be detected by the ProcessManager instance controlling it. ProcessManager determines if a process is "active": - If the network ID is in the cmdline used to execute the process. - If the process is detected by psutil.Process(pid), returning the cmdline needed in the first condition. - If the PID file exists; this is written by the dnsmasq process once is started and is needed in the second condition. To make this feature available for any other process using ProcessManager, the implementation is done in this class. Change-Id: I51dc9d342c613afcbcfdc50a1d2811502748f170 Closes-Bug: #1849502 (cherry picked from commit 7c5ce50a0c9e09d2729ae1ce79d2623ccafca9ee) (cherry picked from commit ce3f2f7d2690eec0dcab4073cbf357b683c6394b) (cherry picked from commit 2d8613e3c4d29a4bb5be81bd78d749e0f448cffe) --- neutron/agent/linux/dhcp.py | 2 +- neutron/agent/linux/external_process.py | 5 ++++- neutron/tests/unit/agent/linux/test_dhcp.py | 4 ++-- .../unit/agent/linux/test_external_process.py | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index e2322dc07c4..42261cb91a5 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -470,7 +470,7 @@ class Dnsmasq(DhcpLocalProcess): pm = self._get_process_manager( cmd_callback=self._build_cmdline_callback) - pm.enable(reload_cfg=reload_with_HUP) + pm.enable(reload_cfg=reload_with_HUP, ensure_active=True) self.process_monitor.register(uuid=self.network.id, service_name=DNSMASQ_SERVICE_NAME, diff --git a/neutron/agent/linux/external_process.py b/neutron/agent/linux/external_process.py index b1d9c3cbc7e..ad7b517b457 100644 --- a/neutron/agent/linux/external_process.py +++ b/neutron/agent/linux/external_process.py @@ -26,6 +26,7 @@ import six from neutron.agent.linux import ip_lib from neutron.agent.linux import utils +from neutron.common import utils as common_utils from neutron.conf.agent import common as agent_cfg @@ -77,7 +78,7 @@ class ProcessManager(MonitoredProcess): fileutils.ensure_tree(os.path.dirname(self.get_pid_file_name()), mode=0o755) - def enable(self, cmd_callback=None, reload_cfg=False): + def enable(self, cmd_callback=None, reload_cfg=False, ensure_active=False): if not self.active: if not cmd_callback: cmd_callback = self.default_cmd_callback @@ -88,6 +89,8 @@ class ProcessManager(MonitoredProcess): run_as_root=self.run_as_root) elif reload_cfg: self.reload_cfg() + if ensure_active: + common_utils.wait_until_true(lambda: self.active) def reload_cfg(self): if self.custom_reload_callback: diff --git a/neutron/tests/unit/agent/linux/test_dhcp.py b/neutron/tests/unit/agent/linux/test_dhcp.py index 848dedb2edd..20b806c3c14 100644 --- a/neutron/tests/unit/agent/linux/test_dhcp.py +++ b/neutron/tests/unit/agent/linux/test_dhcp.py @@ -1310,7 +1310,7 @@ class TestDnsmasq(TestBase): self.assertTrue(test_pm.register.called) self.external_process().enable.assert_called_once_with( - reload_cfg=False) + ensure_active=True, reload_cfg=False) call_kwargs = self.external_process.mock_calls[0][2] cmd_callback = call_kwargs['default_cmd_callback'] @@ -2052,7 +2052,7 @@ class TestDnsmasq(TestBase): dm.reload_allocations() self.assertTrue(test_pm.register.called) self.external_process().enable.assert_called_once_with( - reload_cfg=True) + ensure_active=True, reload_cfg=True) self.safe.assert_has_calls([ mock.call(exp_host_name, exp_host_data), diff --git a/neutron/tests/unit/agent/linux/test_external_process.py b/neutron/tests/unit/agent/linux/test_external_process.py index 866a284514b..808ff9510e4 100644 --- a/neutron/tests/unit/agent/linux/test_external_process.py +++ b/neutron/tests/unit/agent/linux/test_external_process.py @@ -19,6 +19,7 @@ from oslo_utils import fileutils import psutil from neutron.agent.linux import external_process as ep +from neutron.common import utils as common_utils from neutron.tests import base from neutron.tests import tools @@ -168,6 +169,24 @@ class TestProcessManager(base.BaseTestCase): manager.enable(callback) self.assertFalse(callback.called) + def test_enable_with_ensure_active(self): + def _create_cmd(*args): + return ['sleep', 0] + + pm = ep.ProcessManager(self.conf, 'uuid', pid_file='pid_file', + default_cmd_callback=_create_cmd) + with mock.patch.object(psutil, 'Process') as mock_psutil_process, \ + mock.patch.object(ep.ProcessManager, 'pid', + new_callable=mock.PropertyMock) as mock_pid: + mock_pid.return_value = 'pid_value' + mock_process = mock.Mock() + mock_process.cmdline.side_effect = [[], ['the', 'cmd', 'uuid']] + mock_psutil_process.return_value = mock_process + try: + pm.enable(ensure_active=True) + except common_utils.WaitTimeout: + self.fail('ProcessManager.enable() raised WaitTimeout') + def test_reload_cfg_without_custom_reload_callback(self): with mock.patch.object(ep.ProcessManager, 'disable') as disable: manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns')