diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 9ab3ff8ae4b..852a785a754 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -467,7 +467,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 6ba8a553019..f5bc1f89b0a 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 @@ -78,7 +79,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 @@ -89,6 +90,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 51dbd4a1951..a7c509f269b 100644 --- a/neutron/tests/unit/agent/linux/test_dhcp.py +++ b/neutron/tests/unit/agent/linux/test_dhcp.py @@ -1343,7 +1343,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'] @@ -2108,7 +2108,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 55d8918ddab..9bf719f1a44 100644 --- a/neutron/tests/unit/agent/linux/test_external_process.py +++ b/neutron/tests/unit/agent/linux/test_external_process.py @@ -21,6 +21,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 @@ -169,6 +170,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')